From 8cafcc628fc2eea5b25bb0409eb7ed2a76befb2b Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Thu, 3 May 2018 11:32:46 -0400 Subject: remove (dead) caches from hwui Test: make Change-Id: I18bfe51896672272ce7d471eaead69b651399368 --- libs/hwui/ResourceCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libs/hwui/ResourceCache.cpp') diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index d60b99469368..464a58d0c0f8 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -113,7 +113,7 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { if (ref == nullptr) { // If we're not tracking this resource, just delete it if (Caches::hasInstance()) { - Caches::getInstance().patchCache.removeDeferred(resource); + // DEAD CODE } else { // A Res_png_9patch is actually an array of byte that's larger // than sizeof(Res_png_9patch). It must be freed as an array. @@ -136,7 +136,7 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource switch (ref->resourceType) { case kNinePatch: { if (Caches::hasInstance()) { - Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*)resource); + // DEAD CODE } else { // A Res_png_9patch is actually an array of byte that's larger // than sizeof(Res_png_9patch). It must be freed as an array. -- cgit v1.2.3-59-g8ed1b From c8e22a653297837da9a80b0ba65f6854c8986c96 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 14 Aug 2018 13:30:17 -0400 Subject: TextureView Vulkan support and optimized OpenGL draw Render TextureView as hardware bitmaps, instead of GL textures. Cache SkImage for each observed GraphicBuffer, which is faster even for GL. Implement C++ SurfaceTexture, which allows Java SurfaceTexture to be used with Vulkan HWUI render thread and application GL. threads. Delete GLLayer and VkLayer classes and texture code from old HWUI pipeline. Test: Ran skiagl and skiavk pipeline with a TextureView app. Test: TextureView CTS tests pass for GL pipeline. Test: Ran Android NDK Native codec sample app. Change-Id: Idc94f864ce2d34fd6ceff4be4fc7d3327e99879c --- core/jni/android/graphics/ColorFilter.cpp | 2 - core/jni/android/graphics/Matrix.cpp | 1 - core/jni/android/graphics/Shader.cpp | 1 - core/jni/android/graphics/SurfaceTexture.cpp | 47 +- core/jni/android_view_TextureLayer.cpp | 3 +- .../android_graphics_SurfaceTexture.h | 6 +- libs/hwui/Android.bp | 10 +- libs/hwui/Caches.cpp | 103 ---- libs/hwui/Caches.h | 135 ----- libs/hwui/DeferredLayerUpdater.cpp | 123 +--- libs/hwui/DeferredLayerUpdater.h | 34 +- libs/hwui/GlLayer.cpp | 68 --- libs/hwui/GlLayer.h | 75 --- libs/hwui/GpuMemoryTracker.cpp | 17 - libs/hwui/Layer.cpp | 8 +- libs/hwui/Layer.h | 57 +- libs/hwui/PixelBuffer.cpp | 156 ----- libs/hwui/PixelBuffer.h | 198 ------ libs/hwui/RenderProperties.h | 1 - libs/hwui/ResourceCache.cpp | 23 +- libs/hwui/Texture.cpp | 413 ------------- libs/hwui/Texture.h | 228 ------- libs/hwui/VkLayer.cpp | 40 -- libs/hwui/VkLayer.h | 70 --- libs/hwui/hwui/Bitmap.cpp | 2 +- libs/hwui/pipeline/skia/LayerDrawable.cpp | 32 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 12 +- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 9 +- libs/hwui/renderstate/PixelBufferState.cpp | 42 -- libs/hwui/renderstate/PixelBufferState.h | 38 -- libs/hwui/renderstate/RenderState.cpp | 83 +-- libs/hwui/renderstate/RenderState.h | 20 +- libs/hwui/renderstate/TextureState.cpp | 147 ----- libs/hwui/renderstate/TextureState.h | 98 --- libs/hwui/renderthread/CacheManager.cpp | 4 +- libs/hwui/renderthread/CanvasContext.cpp | 8 - libs/hwui/renderthread/EglManager.cpp | 104 ++++ libs/hwui/renderthread/EglManager.h | 10 + libs/hwui/renderthread/RenderThread.cpp | 4 +- libs/hwui/renderthread/VulkanManager.cpp | 20 +- libs/hwui/renderthread/VulkanManager.h | 8 + libs/hwui/surfacetexture/EGLConsumer.cpp | 675 +++++++++++++++++++++ libs/hwui/surfacetexture/EGLConsumer.h | 311 ++++++++++ libs/hwui/surfacetexture/ImageConsumer.cpp | 152 +++++ libs/hwui/surfacetexture/ImageConsumer.h | 97 +++ libs/hwui/surfacetexture/SurfaceTexture.cpp | 496 +++++++++++++++ libs/hwui/surfacetexture/SurfaceTexture.h | 452 ++++++++++++++ libs/hwui/tests/common/LeakChecker.cpp | 4 - libs/hwui/tests/common/TestUtils.cpp | 11 +- libs/hwui/tests/common/TestUtils.h | 1 - libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp | 23 +- libs/hwui/tests/unit/main.cpp | 3 +- libs/hwui/utils/PaintUtils.h | 1 + native/android/Android.bp | 1 + native/android/surface_texture.cpp | 5 +- 55 files changed, 2490 insertions(+), 2202 deletions(-) delete mode 100644 libs/hwui/Caches.cpp delete mode 100644 libs/hwui/Caches.h delete mode 100644 libs/hwui/GlLayer.cpp delete mode 100644 libs/hwui/GlLayer.h delete mode 100644 libs/hwui/PixelBuffer.cpp delete mode 100644 libs/hwui/PixelBuffer.h delete mode 100644 libs/hwui/Texture.cpp delete mode 100644 libs/hwui/Texture.h delete mode 100644 libs/hwui/VkLayer.cpp delete mode 100644 libs/hwui/VkLayer.h delete mode 100644 libs/hwui/renderstate/PixelBufferState.cpp delete mode 100644 libs/hwui/renderstate/PixelBufferState.h delete mode 100644 libs/hwui/renderstate/TextureState.cpp delete mode 100644 libs/hwui/renderstate/TextureState.h create mode 100644 libs/hwui/surfacetexture/EGLConsumer.cpp create mode 100644 libs/hwui/surfacetexture/EGLConsumer.h create mode 100644 libs/hwui/surfacetexture/ImageConsumer.cpp create mode 100644 libs/hwui/surfacetexture/ImageConsumer.h create mode 100644 libs/hwui/surfacetexture/SurfaceTexture.cpp create mode 100644 libs/hwui/surfacetexture/SurfaceTexture.h (limited to 'libs/hwui/ResourceCache.cpp') diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index 3fcedd0264ae..6ebf35c8e1dc 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -22,8 +22,6 @@ #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" -#include - namespace android { using namespace uirenderer; diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index f8bb77a9650c..755fcfb27141 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -20,7 +20,6 @@ #include "SkMatrix.h" #include "core_jni_helpers.h" -#include #include namespace android { diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index cff772002b14..68f5bef18de1 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -6,7 +6,6 @@ #include "SkBlendMode.h" #include "core_jni_helpers.h" -#include #include using namespace android::uirenderer; diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index d098a355085e..3e464c61665f 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -36,6 +36,7 @@ #include "jni.h" #include #include +#include "surfacetexture/SurfaceTexture.h" // ---------------------------------------------------------------------------- @@ -80,10 +81,10 @@ static bool isProtectedContext() { // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp& surfaceTexture) + const sp& surfaceTexture) { - GLConsumer* const p = - (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); + SurfaceTexture* const p = + (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); if (surfaceTexture.get()) { surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture); } @@ -108,10 +109,10 @@ static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz, } static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, - jobject thiz, sp listener) + jobject thiz, sp listener) { - GLConsumer::FrameAvailableListener* const p = - (GLConsumer::FrameAvailableListener*) + SurfaceTexture::FrameAvailableListener* const p = + (SurfaceTexture::FrameAvailableListener*) env->GetLongField(thiz, fields.frameAvailableListener); if (listener.get()) { listener->incStrong((void*)SurfaceTexture_setSurfaceTexture); @@ -122,8 +123,8 @@ static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get()); } -sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { - return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); +sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { + return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); } sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) { @@ -131,7 +132,7 @@ sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) } sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); sp producer(SurfaceTexture_getProducer(env, thiz)); sp surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL); return surfaceTextureClient; @@ -144,7 +145,7 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) { // ---------------------------------------------------------------------------- -class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener +class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener { public: JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz); @@ -266,12 +267,12 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, consumer->setMaxBufferCount(1); } - sp surfaceTexture; + sp surfaceTexture; if (isDetached) { - surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, + surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } else { - surfaceTexture = new GLConsumer(consumer, texName, + surfaceTexture = new SurfaceTexture(consumer, texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } @@ -306,7 +307,7 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setFrameAvailableListener(0); SurfaceTexture_setFrameAvailableListener(env, thiz, 0); SurfaceTexture_setSurfaceTexture(env, thiz, 0); @@ -315,13 +316,13 @@ static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) static void SurfaceTexture_setDefaultBufferSize( JNIEnv* env, jobject thiz, jint width, jint height) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->updateTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to update texture contents (see " @@ -333,7 +334,7 @@ static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->releaseTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to release texture contents (see " @@ -345,20 +346,20 @@ static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); float* mtx = env->GetFloatArrayElements(jmtx, NULL); surfaceTexture->getTransformMatrix(mtx); env->ReleaseFloatArrayElements(jmtx, mtx, 0); @@ -366,19 +367,19 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->isAbandoned(); } diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp index d3a447f1f7dc..1ccb6a8f610c 100644 --- a/core/jni/android_view_TextureLayer.cpp +++ b/core/jni/android_view_TextureLayer.cpp @@ -67,8 +67,7 @@ static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr, jobject surface) { DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); - layer->setSurfaceTexture(surfaceTexture); + layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); } static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h index c534d4bb9e0a..0ad25876a008 100644 --- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h +++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h @@ -23,14 +23,14 @@ namespace android { -class GLConsumer; class IGraphicBufferProducer; +class SurfaceTexture; extern sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz); extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz); -/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */ -extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */ +extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); /* gets the producer end of the SurfaceTexture */ extern sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 83e90b654ff6..62ab7900737e 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -175,9 +175,7 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", - "renderstate/PixelBufferState.cpp", "renderstate/RenderState.cpp", - "renderstate/TextureState.cpp", "renderthread/CacheManager.cpp", "renderthread/CanvasContext.cpp", "renderthread/DrawFrameTask.cpp", @@ -189,6 +187,9 @@ cc_defaults { "renderthread/TimeLord.cpp", "renderthread/Frame.cpp", "service/GraphicsStatsService.cpp", + "surfacetexture/EGLConsumer.cpp", + "surfacetexture/ImageConsumer.cpp", + "surfacetexture/SurfaceTexture.cpp", "thread/TaskManager.cpp", "utils/Blur.cpp", "utils/Color.cpp", @@ -200,7 +201,6 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", - "Caches.cpp", "CanvasState.cpp", "CanvasTransform.cpp", "ClipArea.cpp", @@ -209,7 +209,6 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", - "GlLayer.cpp", "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "Interpolator.cpp", @@ -219,7 +218,6 @@ cc_defaults { "Matrix.cpp", "EglReadback.cpp", "PathParser.cpp", - "PixelBuffer.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Properties.cpp", @@ -230,9 +228,7 @@ cc_defaults { "ResourceCache.cpp", "SkiaCanvas.cpp", "Snapshot.cpp", - "Texture.cpp", "VectorDrawable.cpp", - "VkLayer.cpp", "protos/graphicsstats.proto", ], diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp deleted file mode 100644 index 254144448859..000000000000 --- a/libs/hwui/Caches.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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 "GlLayer.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -Caches* Caches::sInstance = nullptr; - -/////////////////////////////////////////////////////////////////////////////// -// Macros -/////////////////////////////////////////////////////////////////////////////// - -#if DEBUG_CACHE_FLUSH -#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) -#else -#define FLUSH_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -Caches::Caches(RenderState& renderState) : mInitialized(false) { - INIT_LOGD("Creating OpenGL renderer caches"); - init(); - initStaticProperties(); -} - -bool Caches::init() { - if (mInitialized) return false; - - ATRACE_NAME("Caches::init"); - - mRegionMesh = nullptr; - - mInitialized = true; - - mPixelBufferState = new PixelBufferState(); - mTextureState = new TextureState(); - mTextureState->constructTexture(*this); - - return true; -} - -void Caches::initStaticProperties() { - // OpenGL ES 3.0+ specific features - gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() && - property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); -} - -void Caches::terminate() { - if (!mInitialized) return; - mRegionMesh.reset(nullptr); - - clearGarbage(); - - delete mPixelBufferState; - mPixelBufferState = nullptr; - delete mTextureState; - mTextureState = nullptr; - mInitialized = false; -} - -/////////////////////////////////////////////////////////////////////////////// -// Memory management -/////////////////////////////////////////////////////////////////////////////// - -void Caches::clearGarbage() {} - -void Caches::flush(FlushMode mode) { - clearGarbage(); - glFinish(); - // Errors during cleanup should be considered non-fatal, dump them and - // and move on. TODO: All errors or just errors like bad surface? - GLUtils::dumpGLErrors(); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h deleted file mode 100644 index 642f9dc50eb1..000000000000 --- a/libs/hwui/Caches.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#pragma once - -#include "DeviceInfo.h" -#include "Extensions.h" -#include "ResourceCache.h" -#include "renderstate/PixelBufferState.h" -#include "renderstate/TextureState.h" -#include "thread/TaskManager.h" -#include "thread/TaskProcessor.h" - -#include -#include - -#include - -#include - -#include - -#include - -#include - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Caches -/////////////////////////////////////////////////////////////////////////////// - -class RenderNode; -class RenderState; - -class ANDROID_API Caches { -public: - static Caches& createInstance(RenderState& renderState) { - LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted"); - sInstance = new Caches(renderState); - return *sInstance; - } - - static Caches& getInstance() { - LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created"); - return *sInstance; - } - - static bool hasInstance() { return sInstance != nullptr; } - -private: - explicit Caches(RenderState& renderState); - static Caches* sInstance; - -public: - enum class FlushMode { Layers = 0, Moderate, Full }; - - /** - * Initialize caches. - */ - bool init(); - - bool isInitialized() { return mInitialized; } - - /** - * Flush the cache. - * - * @param mode Indicates how much of the cache should be flushed - */ - void flush(FlushMode mode); - - /** - * Destroys all resources associated with this cache. This should - * be called after a flush(FlushMode::Full). - */ - void terminate(); - - /** - * Call this on each frame to ensure that garbage is deleted from - * GPU memory. - */ - void clearGarbage(); - - /** - * 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().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; - } - -public: - TaskManager tasks; - - bool gpuPixelBuffersEnabled; - - const Extensions& extensions() const { return DeviceInfo::get()->extensions(); } - PixelBufferState& pixelBufferState() { return *mPixelBufferState; } - TextureState& textureState() { return *mTextureState; } - -private: - void initStaticProperties(); - - static void eventMarkNull(GLsizei length, const GLchar* marker) {} - static void startMarkNull(GLsizei length, const GLchar* marker) {} - static void endMarkNull() {} - - // Used to render layers - std::unique_ptr mRegionMesh; - - bool mInitialized; - - // TODO: move below to RenderState - PixelBufferState* mPixelBufferState = nullptr; - TextureState* mTextureState = nullptr; - -}; // class Caches - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 569de76f294e..00916559a9c2 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,27 +15,20 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderTask.h" #include "utils/PaintUtils.h" namespace android { namespace uirenderer { -DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi) +DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) - , mLayer(nullptr) - , mLayerApi(layerApi) - , mCreateLayerFn(createLayerFn) { + , mLayer(nullptr) { renderState.registerDeferredLayerUpdater(this); } @@ -50,13 +43,9 @@ void DeferredLayerUpdater::destroyLayer() { return; } - if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) { - status_t err = mSurfaceTexture->detachFromContext(); + if (mSurfaceTexture.get() && mGLContextAttached) { + mSurfaceTexture->detachFromView(); mGLContextAttached = false; - if (err != 0) { - // TODO: Elevate to fatal exception - ALOGE("Failed to detach SurfaceTexture from context %d", err); - } } mLayer->postDecStrong(); @@ -75,99 +64,53 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { void DeferredLayerUpdater::apply() { if (!mLayer) { - mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend); + mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); } mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { - if (mLayer->getApi() == Layer::Api::Vulkan) { - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateVkTexImage(); - } - } else { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "apply surfaceTexture with non GL backend %x, GL %x, VK %x", - mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); - if (!mGLContextAttached) { - mGLContextAttached = true; - mUpdateTexImage = true; - mSurfaceTexture->attachToContext(static_cast(mLayer)->getTextureId()); - } - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateTexImage(); + if (!mGLContextAttached) { + mGLContextAttached = true; + mUpdateTexImage = true; + mSurfaceTexture->attachToView(); + } + if (mUpdateTexImage) { + mUpdateTexImage = false; + sk_sp layerImage; + SkMatrix textureTransform; + android_dataspace dataSpace; + bool queueEmpty = true; + // If the SurfaceTexture queue is in synchronous mode, need to discard all + // but latest frame. Since we can't tell which mode it is in, + // do this unconditionally. + do { + layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty, + mRenderState); + } while (layerImage.get() && (!queueEmpty)); + if (layerImage.get()) { + // force filtration if buffer size != layer size + bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); + updateLayer(forceFilter, textureTransform, dataSpace, layerImage); } - GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); - static_cast(mLayer)->setRenderTarget(renderTarget); } + if (mTransform) { - mLayer->getTransform().load(*mTransform); + mLayer->getTransform() = *mTransform; setTransform(nullptr); } } } -void DeferredLayerUpdater::doUpdateTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - if (mSurfaceTexture->updateTexImage() == NO_ERROR) { - float transform[16]; - - int64_t frameNumber = mSurfaceTexture->getFrameNumber(); - // If the GLConsumer queue is in synchronous mode, need to discard all - // but latest frame, using the frame number to tell when we no longer - // have newer frames to target. Since we can't tell which mode it is in, - // do this unconditionally. - int dropCounter = 0; - while (mSurfaceTexture->updateTexImage() == NO_ERROR) { - int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); - if (newFrameNumber == frameNumber) break; - frameNumber = newFrameNumber; - dropCounter++; - } - - bool forceFilter = false; - sp buffer = mSurfaceTexture->getCurrentBuffer(); - if (buffer != nullptr) { - // force filtration if buffer size != layer size - forceFilter = mWidth != static_cast(buffer->getWidth()) || - mHeight != static_cast(buffer->getHeight()); - } - -#if DEBUG_RENDERER - if (dropCounter > 0) { - RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); - } -#endif - mSurfaceTexture->getTransformMatrix(transform); - - updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace()); - } -} - -void DeferredLayerUpdater::doUpdateVkTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, - "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - - static const mat4 identityMatrix; - updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN); - - VkLayer* vkLayer = static_cast(mLayer); - vkLayer->updateTexture(); -} - -void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform, - android_dataspace dataspace) { +void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp& layerImage) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); - mLayer->getTexTransform().load(textureTransform); + mLayer->getTexTransform() = textureTransform; mLayer->setDataSpace(dataspace); + mLayer->setImage(layerImage); } void DeferredLayerUpdater::detachSurfaceTexture() { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index fe3ee7a2b4c6..4c323b861002 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -17,18 +17,19 @@ #pragma once #include +#include #include #include -#include +#include #include #include #include #include +#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" -#include "renderthread/RenderThread.h" namespace android { namespace uirenderer { @@ -41,12 +42,7 @@ class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - typedef std::function colorFilter, int alpha, - SkBlendMode mode, bool blend)> - CreateLayerFn; - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi); + ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState); ANDROID_API ~DeferredLayerUpdater(); @@ -70,13 +66,13 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp& texture) { - if (texture.get() != mSurfaceTexture.get()) { - mSurfaceTexture = texture; + ANDROID_API void setSurfaceTexture(const sp& consumer) { + if (consumer.get() != mSurfaceTexture.get()) { + mSurfaceTexture = consumer; - GLenum target = texture->getCurrentTextureTarget(); + GLenum target = consumer->getCurrentTextureTarget(); LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported GLConsumer with target %x", target); + "set unsupported SurfaceTexture with target %x", target); } } @@ -97,12 +93,11 @@ public: void detachSurfaceTexture(); - void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace); + void updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp& layerImage); void destroyLayer(); - Layer::Api getBackingLayerApi() { return mLayerApi; } - private: RenderState& mRenderState; @@ -113,17 +108,12 @@ private: sk_sp mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp mSurfaceTexture; + sp mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; Layer* mLayer; - Layer::Api mLayerApi; - CreateLayerFn mCreateLayerFn; - - void doUpdateTexImage(); - void doUpdateVkTexImage(); }; } /* namespace uirenderer */ diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp deleted file mode 100644 index 432bb8526465..000000000000 --- a/libs/hwui/GlLayer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 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 "GlLayer.h" - -#include "Caches.h" -#include "RenderNode.h" -#include "renderstate/RenderState.h" - -namespace android { -namespace uirenderer { - -GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode) - , caches(Caches::getInstance()) - , texture(caches) { - texture.mWidth = layerWidth; - texture.mHeight = layerHeight; - texture.blend = blend; -} - -GlLayer::~GlLayer() { - // There's a rare possibility that Caches could have been destroyed already - // since this method is queued up as a task. - // Since this is a reset method, treat this as non-fatal. - if (caches.isInitialized() && texture.mId) { - texture.deleteTexture(); - } -} - -void GlLayer::onGlContextLost() { - texture.deleteTexture(); -} - -void GlLayer::setRenderTarget(GLenum renderTarget) { - if (renderTarget != getRenderTarget()) { - // new render target: bind with new target, and update filter/wrap - texture.mTarget = renderTarget; - if (texture.mId) { - caches.textureState().bindTexture(texture.target(), texture.mId); - } - texture.setFilter(GL_NEAREST, false, true); - texture.setWrap(GL_CLAMP_TO_EDGE, false, true); - } -} - -void GlLayer::generateTexture() { - if (!texture.mId) { - glGenTextures(1, &texture.mId); - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h deleted file mode 100644 index 9f70fdae6790..000000000000 --- a/libs/hwui/GlLayer.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include "Layer.h" - -#include "Texture.h" - -namespace android { -namespace uirenderer { - -// Forward declarations -class Caches; - -/** - * A layer has dimensions and is backed by an OpenGL texture or FBO. - */ -class GlLayer : public Layer { -public: - GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend); - virtual ~GlLayer(); - - uint32_t getWidth() const override { return texture.mWidth; } - - uint32_t getHeight() const override { return texture.mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - texture.updateLayout(width, height, texture.internalFormat(), texture.format(), - texture.target()); - } - - void setBlend(bool blend) override { texture.blend = blend; } - - bool isBlend() const override { return texture.blend; } - - inline GLuint getTextureId() const { return texture.id(); } - - inline GLenum getRenderTarget() const { return texture.target(); } - - void setRenderTarget(GLenum renderTarget); - - void generateTexture(); - - /** - * Lost the GL context but the layer is still around, mark it invalid internally - * so the dtor knows not to do any GL work - */ - void onGlContextLost(); - -private: - Caches& caches; - - /** - * The texture backing this layer. - */ - Texture texture; -}; // struct GlLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index 612bfde1a3fa..a9a7af8f22f3 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "Texture.h" #include "utils/StringUtils.h" #include @@ -117,22 +116,6 @@ void GpuMemoryTracker::onFrameCompleted() { ATRACE_INT(buf, stats.count); } } - - std::vector freeList; - for (const auto& obj : gObjectSet) { - if (obj->objectType() == GpuObjectType::Texture) { - const Texture* texture = static_cast(obj); - if (texture->cleanup) { - ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(), - texture->width(), texture->height()); - freeList.push_back(texture); - } - } - } - for (auto& texture : freeList) { - const_cast(texture)->deleteTexture(); - delete texture; - } } } // namespace uirenderer diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index fb8f0337c95e..f59a2e6ee5c1 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -17,17 +17,17 @@ #include "Layer.h" #include "renderstate/RenderState.h" +#include "utils/Color.h" #include namespace android { namespace uirenderer { -Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter, int alpha, - SkBlendMode mode) +Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alpha, + SkBlendMode mode) : GpuMemoryTracker(GpuObjectType::Layer) , mRenderState(renderState) - , mApi(api) , mColorFilter(colorFilter) , alpha(alpha) , mode(mode) { @@ -36,6 +36,8 @@ Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter incStrong(nullptr); buildColorSpaceWithFilter(); renderState.registerLayer(this); + texTransform.setIdentity(); + transform.setIdentity(); } Layer::~Layer() { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 31878ac23642..c4e4c1c96ba6 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -23,8 +23,9 @@ #include #include #include - -#include "Matrix.h" +#include +#include +#include namespace android { namespace uirenderer { @@ -40,24 +41,19 @@ class RenderState; */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - enum class Api { - OpenGL = 0, - Vulkan = 1, - }; - - Api getApi() const { return mApi; } + Layer(RenderState& renderState, sk_sp, int alpha, SkBlendMode mode); ~Layer(); - virtual uint32_t getWidth() const = 0; + virtual uint32_t getWidth() const { return mWidth; } - virtual uint32_t getHeight() const = 0; + virtual uint32_t getHeight() const { return mHeight; } - virtual void setSize(uint32_t width, uint32_t height) = 0; + virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } - virtual void setBlend(bool blend) = 0; + virtual void setBlend(bool blend) { mBlend = blend; } - virtual bool isBlend() const = 0; + virtual bool isBlend() const { return mBlend; } inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } @@ -84,9 +80,9 @@ public: inline sk_sp getColorSpaceWithFilter() const { return mColorSpaceWithFilter; } - inline mat4& getTexTransform() { return texTransform; } + inline SkMatrix& getTexTransform() { return texTransform; } - inline mat4& getTransform() { return transform; } + inline SkMatrix& getTransform() { return transform; } /** * Posts a decStrong call to the appropriate thread. @@ -94,16 +90,17 @@ public: */ void postDecStrong(); + inline void setImage(const sk_sp& image) { this->layerImage = image; } + + inline sk_sp getImage() const { return this->layerImage; } + protected: - Layer(RenderState& renderState, Api api, sk_sp, int alpha, SkBlendMode mode); RenderState& mRenderState; private: void buildColorSpaceWithFilter(); - Api mApi; - /** * Color filter used to draw this layer. Optional. */ @@ -137,12 +134,32 @@ private: /** * Optional texture coordinates transform. */ - mat4 texTransform; + SkMatrix texTransform; /** * Optional transform. */ - mat4 transform; + SkMatrix transform; + + /** + * An image backing the layer. + */ + sk_sp layerImage; + + /** + * layer width. + */ + uint32_t mWidth = 0; + + /** + * layer height. + */ + uint32_t mHeight = 0; + + /** + * enable blending + */ + bool mBlend = false; }; // struct Layer diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp deleted file mode 100644 index 910a9889db1f..000000000000 --- a/libs/hwui/PixelBuffer.cpp +++ /dev/null @@ -1,156 +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 "PixelBuffer.h" - -#include "Debug.h" -#include "Extensions.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// CPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class CpuPixelBuffer : public PixelBuffer { -public: - CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - std::unique_ptr mBuffer; -}; - -CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mBuffer(new uint8_t[width * height * formatSize(format)]) {} - -uint8_t* CpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mAccessMode = mode; - } - return mBuffer.get(); -} - -void CpuPixelBuffer::unmap() { - mAccessMode = kAccessMode_None; -} - -void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - &mBuffer[offset]); -} - -/////////////////////////////////////////////////////////////////////////////// -// GPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class GpuPixelBuffer : public PixelBuffer { -public: - GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - ~GpuPixelBuffer(); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - GLuint mBuffer; - uint8_t* mMappedPointer; - Caches& mCaches; -}; - -GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mMappedPointer(nullptr) - , mCaches(Caches::getInstance()) { - glGenBuffers(1, &mBuffer); - - mCaches.pixelBufferState().bind(mBuffer); - glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW); - mCaches.pixelBufferState().unbind(); -} - -GpuPixelBuffer::~GpuPixelBuffer() { - glDeleteBuffers(1, &mBuffer); -} - -uint8_t* GpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mCaches.pixelBufferState().bind(mBuffer); - mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); - if (CC_UNLIKELY(!mMappedPointer)) { - GLUtils::dumpGLErrors(); - LOG_ALWAYS_FATAL("Failed to map PBO"); - } - mAccessMode = mode; - mCaches.pixelBufferState().unbind(); - } - - return mMappedPointer; -} - -void GpuPixelBuffer::unmap() { - if (mAccessMode != kAccessMode_None) { - if (mMappedPointer) { - mCaches.pixelBufferState().bind(mBuffer); - GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - if (status == GL_FALSE) { - ALOGE("Corrupted GPU pixel buffer"); - } - } - mAccessMode = kAccessMode_None; - mMappedPointer = nullptr; - } -} - -void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - // If the buffer is not mapped, unmap() will not bind it - mCaches.pixelBufferState().bind(mBuffer); - unmap(); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - reinterpret_cast(offset)); - mCaches.pixelBufferState().unbind(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Factory -/////////////////////////////////////////////////////////////////////////////// - -PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { - if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { - return new GpuPixelBuffer(format, width, height); - } - return new CpuPixelBuffer(format, width, height); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h deleted file mode 100644 index e7e341b90ad3..000000000000 --- a/libs/hwui/PixelBuffer.h +++ /dev/null @@ -1,198 +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_PIXEL_BUFFER_H -#define ANDROID_HWUI_PIXEL_BUFFER_H - -#include - -#include - -namespace android { -namespace uirenderer { - -/** - * Represents a pixel buffer. A pixel buffer will be backed either by a - * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other - * versions. If the buffer is backed by a PBO it will of type - * GL_PIXEL_UNPACK_BUFFER. - * - * To read from or write into a PixelBuffer you must first map the - * buffer using the map(AccessMode) method. This method returns a - * pointer to the beginning of the buffer. - * - * Before the buffer can be used by the GPU, for instance to upload - * a texture, you must first unmap the buffer. To do so, call the - * unmap() method. - * - * Mapping and unmapping a PixelBuffer can have the side effect of - * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is - * therefore recommended to call Caches::unbindPixelbuffer() after - * using a PixelBuffer to upload to a texture. - */ -class PixelBuffer { -public: - enum BufferType { kBufferType_Auto, kBufferType_CPU }; - - enum AccessMode { - kAccessMode_None = 0, - kAccessMode_Read = GL_MAP_READ_BIT, - kAccessMode_Write = GL_MAP_WRITE_BIT, - kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT - }; - - /** - * Creates a new PixelBuffer object with the specified format and - * dimensions. The buffer is immediately allocated. - * - * The buffer type specifies how the buffer should be allocated. - * By default this method will automatically choose whether to allocate - * a CPU or GPU buffer. - */ - static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height, - BufferType type = kBufferType_Auto); - - virtual ~PixelBuffer() {} - - /** - * Returns the format of this render buffer. - */ - GLenum getFormat() const { return mFormat; } - - /** - * Maps this before with the specified access mode. This method - * returns a pointer to the region of memory where the buffer was - * mapped. - * - * If the buffer is already mapped when this method is invoked, - * this method will return the previously mapped pointer. The - * access mode can only be changed by calling unmap() first. - * - * The specified access mode cannot be kAccessMode_None. - */ - virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0; - - /** - * Returns the current access mode for this buffer. If the buffer - * is not mapped, this method returns kAccessMode_None. - */ - AccessMode getAccessMode() const { return mAccessMode; } - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - */ - virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0; - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - * - * This is a convenience function provided to save callers the - * trouble of computing the offset parameter. - */ - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - upload(x, y, width, height, getOffset(x, y)); - } - - /** - * Returns the width of the render buffer in pixels. - */ - uint32_t getWidth() const { return mWidth; } - - /** - * Returns the height of the render buffer in pixels. - */ - uint32_t getHeight() const { return mHeight; } - - /** - * Returns the size of this pixel buffer in bytes. - */ - uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); } - - /** - * Returns the offset of a pixel in this pixel buffer, in bytes. - */ - uint32_t getOffset(uint32_t x, uint32_t y) const { - return (y * mWidth + x) * formatSize(mFormat); - } - - /** - * Returns the number of bytes per pixel in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatSize(GLenum format) { - switch (format) { - case GL_ALPHA: - return 1; - case GL_RGBA: - return 4; - } - return 0; - } - - /** - * Returns the alpha channel offset in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatAlphaOffset(GLenum format) { - switch (format) { - case GL_ALPHA: - return 0; - case GL_RGBA: - return 3; - } - - ALOGE("unsupported format: %d", format); - return 0; - } - -protected: - /** - * Creates a new render buffer in the specified format and dimensions. - * The format must be GL_ALPHA or GL_RGBA. - */ - PixelBuffer(GLenum format, uint32_t width, uint32_t height) - : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {} - - /** - * Unmaps this buffer, if needed. After the buffer is unmapped, - * the pointer previously returned by map() becomes invalid and - * should not be used. - */ - virtual void unmap() = 0; - - GLenum mFormat; - - uint32_t mWidth; - uint32_t mHeight; - - AccessMode mAccessMode; - -}; // class PixelBuffer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_PIXEL_BUFFER_H diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 0766e3b7ed28..7966845ff814 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,6 @@ #pragma once -#include "Caches.h" #include "DeviceInfo.h" #include "Outline.h" #include "Rect.h" diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 464a58d0c0f8..65bee476f14d 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -15,7 +15,6 @@ */ #include "ResourceCache.h" -#include "Caches.h" namespace android { @@ -112,13 +111,9 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; if (ref == nullptr) { // If we're not tracking this resource, just delete it - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + delete[](int8_t*) resource; return; } ref->destroyed = true; @@ -135,14 +130,10 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource if (ref->destroyed) { switch (ref->resourceType) { case kNinePatch: { - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + int8_t* patch = (int8_t*)resource; + delete[] patch; } break; } } diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp deleted file mode 100644 index 1e90eebe3bb8..000000000000 --- a/libs/hwui/Texture.cpp +++ /dev/null @@ -1,413 +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 "Texture.h" -#include "Caches.h" -#include "utils/GLUtils.h" -#include "utils/MathUtils.h" -#include "utils/TraceUtils.h" - -#include - -#include - -#include - -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 8; - default: - LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); - } -} - -void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { - if (force || wrapS != mWrapS || wrapT != mWrapT) { - mWrapS = wrapS; - mWrapT = wrapT; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); - glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); - } -} - -void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { - if (force || min != mMinFilter || mag != mMagFilter) { - mMinFilter = min; - mMagFilter = mag; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; - - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); - glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); - } -} - -void Texture::deleteTexture() { - mCaches.textureState().deleteTexture(mId); - mId = 0; - mTarget = GL_NONE; - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } -} - -bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - if (mWidth == width && mHeight == height && mFormat == format && - mInternalFormat == internalFormat && mTarget == target) { - return false; - } - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); - return true; -} - -void Texture::resetCachedParams() { - mWrapS = GL_REPEAT; - mWrapT = GL_REPEAT; - mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - mMagFilter = GL_LINEAR; -} - -void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, - GLenum type, const void* pixels) { - GL_CHECKPOINT(MODERATE); - - // We don't have color space information, we assume the data is gamma encoded - mIsLinear = false; - - bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D); - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - resetCachedParams(); - } - mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); - if (needsAlloc) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } - GL_CHECKPOINT(MODERATE); -} - -void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } - mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - buffer->getNativeBuffer(), 0); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); -} - -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(); - if ((stride == width) || useStride) { - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } - - if (resize) { - 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); - } - - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - } else { - // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width - - GLvoid* temp = (GLvoid*)malloc(width * height * bpp); - if (!temp) return; - - uint8_t* pDst = (uint8_t*)temp; - uint8_t* pSrc = (uint8_t*)data; - for (GLsizei i = 0; i < height; i++) { - memcpy(pDst, pSrc, width * bpp); - pDst += width * bpp; - pSrc += stride * bpp; - } - - if (resize) { - 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); - } - - free(temp); - } -} - -void Texture::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: - 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 is upconverted to RGBA_8888 - case kARGB_4444_SkColorType: - case kN32_SkColorType: - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(needSRGB); - *outType = GL_UNSIGNED_BYTE; - break; - case kGray_8_SkColorType: - *outFormat = GL_LUMINANCE; - *outInternalFormat = GL_LUMINANCE; - *outType = GL_UNSIGNED_BYTE; - break; - case kRGBA_F16_SkColorType: - if (caches.extensions().getMajorGlVersion() >= 3) { - // This format is always linear - *outFormat = GL_RGBA; - *outInternalFormat = GL_RGBA16F; - *outType = GL_HALF_FLOAT; - } else { - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(true); - *outType = GL_UNSIGNED_BYTE; - } - break; - default: - LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); - break; - } -} - -SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp sRGB) { - SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), - bitmap.info().alphaType(), - hasLinearBlending ? sRGB : nullptr)); - rgbaBitmap.eraseColor(0); - - if (bitmap.colorType() == kRGBA_F16_SkColorType) { - // Drawing RGBA_F16 onto ARGB_8888 is not supported - bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), - rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); - } else { - SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); - } - - return rgbaBitmap; -} - -bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) { - return info.colorType() == kARGB_4444_SkColorType || - (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && - info.colorSpace()->isSRGB()) || - (info.colorType() == kRGBA_F16_SkColorType && - Caches::getInstance().extensions().getMajorGlVersion() < 3); -} - -void Texture::upload(Bitmap& bitmap) { - ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); - - // We could also enable mipmapping if both bitmap dimensions are powers - // of 2 but we'd have to deal with size changes. Let's keep this simple - const bool canMipMap = mCaches.extensions().hasNPot(); - - // If the texture had mipmap enabled but not anymore, - // force a glTexImage2D to discard the mipmap levels - bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); - bool setDefaultParams = false; - - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - setDefaultParams = true; - } - - bool hasLinearBlending = mCaches.extensions().hasLinearBlending(); - bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace()); - - GLint internalFormat, format, type; - colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending, - &internalFormat, &format, &type); - - // Some devices don't support GL_RGBA16F, so we need to compare the color type - // and internal GL format to decide what to do with 16 bit bitmaps - bool rgba16fNeedsConversion = - bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F; - - // RGBA16F is always linear extended sRGB - if (internalFormat == GL_RGBA16F) { - mIsLinear = true; - } - - mConnector.reset(); - - // Alpha masks don't have color profiles - // If an RGBA16F bitmap needs conversion, we know the target will be sRGB - if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) { - SkColorSpace* colorSpace = bitmap.info().colorSpace(); - // If the bitmap is sRGB we don't need conversion - if (colorSpace != nullptr && !colorSpace->isSRGB()) { - SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); - if (!colorSpace->toXYZD50(&xyzMatrix)) { - ALOGW("Incompatible color space!"); - } else { - SkColorSpaceTransferFn fn; - if (!colorSpace->isNumericalTransferFn(&fn)) { - ALOGW("Incompatible color space, no numerical transfer function!"); - } else { - float data[16]; - xyzMatrix.asColMajorf(data); - - ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC, - fn.fD, fn.fE, fn.fF}; - ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p); - mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB())); - - // A non-sRGB color space might have a transfer function close enough to sRGB - // that we can save shader instructions by using an sRGB sampler - // This is only possible if we have hardware support for sRGB textures - if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() && - !bitmap.isHardware()) { - internalFormat = GL_SRGB8_ALPHA8; - } - } - } - } - } - - GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; - needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target); - - blend = !bitmap.isOpaque(); - mCaches.textureState().bindTexture(mTarget, mId); - - // TODO: Handle sRGB gray bitmaps - if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) { - SkBitmap skBitmap; - bitmap.getSkBitmap(&skBitmap); - sk_sp sRGB = SkColorSpace::MakeSRGB(); - SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); - uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), - rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), - rgbaBitmap.getPixels()); - } else if (bitmap.isHardware()) { - uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); - } else { - uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), - bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), - bitmap.pixels()); - } - - if (canMipMap) { - mipMap = bitmap.hasHardwareMipMap(); - if (mipMap) { - glGenerateMipmap(GL_TEXTURE_2D); - } - } - - if (setDefaultParams) { - setFilter(GL_NEAREST); - setWrap(GL_CLAMP_TO_EDGE); - } -} - -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - mId = id; - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - mConnector.reset(); - // We're wrapping an existing texture, so don't double count this memory - notifySizeChanged(0); -} - -TransferFunctionType Texture::getTransferFunctionType() const { - if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) { - const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters(); - if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) { - if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) && - MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { - if (MathUtils::areEqual(p.g, 1.0f)) { - return TransferFunctionType::None; - } - return TransferFunctionType::Gamma; - } - return TransferFunctionType::Limited; - } - return TransferFunctionType::Full; - } - return TransferFunctionType::None; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h deleted file mode 100644 index 5b7e4e261f30..000000000000 --- a/libs/hwui/Texture.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2010 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_TEXTURE_H -#define ANDROID_HWUI_TEXTURE_H - -#include "GpuMemoryTracker.h" -#include "hwui/Bitmap.h" -#include "utils/Color.h" - -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace android { - -class GraphicBuffer; - -namespace uirenderer { - -class Caches; -class UvMapper; -class Layer; - -/** - * Represents an OpenGL texture. - */ -class Texture : public GpuMemoryTracker { -public: - static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp sRGB); - static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending); - static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, - bool needSRGB, GLint* outInternalFormat, - GLint* outFormat, GLint* outType); - - explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {} - - virtual ~Texture() {} - - inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { - setWrapST(wrap, wrap, bindTexture, force); - } - - virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false); - - inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { - setFilterMinMag(filter, filter, bindTexture, force); - } - - virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false); - - /** - * Convenience method to call glDeleteTextures() on this texture's id. - */ - void deleteTexture(); - - /** - * Sets the width, height, and format of the texture along with allocating - * the texture ID. Does nothing if the width, height, and format are already - * the requested values. - * - * The image data is undefined after calling this. - */ - void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { - upload(internalFormat, width, height, format, - internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); - } - - /** - * Updates this Texture with the contents of the provided Bitmap, - * also setting the appropriate width, height, and format. It is not necessary - * to call resize() prior to this. - * - * Note this does not set the generation from the Bitmap. - */ - void upload(Bitmap& source); - - /** - * Basically glTexImage2D/glTexSubImage2D. - */ - 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 internalFormat, GLint format, - GLenum target); - - GLuint id() const { return mId; } - - uint32_t width() const { return mWidth; } - - uint32_t height() const { return mHeight; } - - GLint format() const { return mFormat; } - - GLint internalFormat() const { return mInternalFormat; } - - GLenum target() const { return mTarget; } - - /** - * Returns nullptr if this texture does not require color space conversion - * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion - * is required. - */ - constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); } - - constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; } - - TransferFunctionType getTransferFunctionType() const; - - /** - * Returns true if this texture uses a linear encoding format. - */ - constexpr bool isLinear() const { return mIsLinear; } - - /** - * Generation of the backing bitmap, - */ - uint32_t generation = 0; - /** - * Indicates whether the texture requires blending. - */ - bool blend = false; - /** - * Indicates whether this texture should be cleaned up after use. - */ - bool cleanup = false; - /** - * Optional, size of the original bitmap. - */ - uint32_t bitmapSize = 0; - /** - * Indicates whether this texture will use trilinear filtering. - */ - bool mipMap = false; - - /** - * Optional, pointer to a texture coordinates mapper. - */ - const UvMapper* uvMapper = nullptr; - - /** - * Whether or not the Texture is marked in use and thus not evictable for - * the current frame. This is reset at the start of a new frame. - */ - void* isInUse = nullptr; - -private: - // TODO: Temporarily grant private access to GlLayer, remove once - // GlLayer can be de-tangled from being a dual-purpose render target - // and external texture wrapper - friend class GlLayer; - - // Returns true if the texture layout (size, format, etc.) changed, false if it was the same - bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target); - void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); - void resetCachedParams(); - - GLuint mId = 0; - uint32_t mWidth = 0; - uint32_t mHeight = 0; - GLint mFormat = 0; - GLint mInternalFormat = 0; - GLenum mTarget = GL_NONE; - EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR; - - /* See GLES spec section 3.8.14 - * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is - * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR. - * s, t, and r wrap modes are all set to REPEAT." - */ - GLenum mWrapS = GL_REPEAT; - GLenum mWrapT = GL_REPEAT; - GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - GLenum mMagFilter = GL_LINEAR; - - // Indicates whether the content of the texture is in linear space - bool mIsLinear = false; - - Caches& mCaches; - - std::unique_ptr mConnector; -}; // struct Texture - -class AutoTexture { -public: - explicit AutoTexture(Texture* texture) : texture(texture) {} - ~AutoTexture() { - if (texture && texture->cleanup) { - texture->deleteTexture(); - delete texture; - } - } - - Texture* const texture; -}; // class AutoTexture - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp deleted file mode 100644 index 30fba7ae7d9b..000000000000 --- a/libs/hwui/VkLayer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 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 "VkLayer.h" - -#include "renderstate/RenderState.h" - -#include -#include - -namespace android { -namespace uirenderer { - -void VkLayer::updateTexture() { - sk_sp surface; - SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType); - surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info); - surface->getCanvas()->clear(SK_ColorBLUE); - mImage = surface->makeImageSnapshot(); -} - -void VkLayer::onVkContextDestroyed() { - mImage = nullptr; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h deleted file mode 100644 index e9664d04b7a5..000000000000 --- a/libs/hwui/VkLayer.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include "Layer.h" - -#include - -namespace android { -namespace uirenderer { -/** - * A layer has dimensions and is backed by a VkImage. - */ -class VkLayer : public Layer { -public: - VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode) - , mWidth(layerWidth) - , mHeight(layerHeight) - , mBlend(blend) {} - - virtual ~VkLayer() {} - - uint32_t getWidth() const override { return mWidth; } - - uint32_t getHeight() const override { return mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - mWidth = width; - mHeight = height; - } - - void setBlend(bool blend) override { mBlend = blend; } - - bool isBlend() const override { return mBlend; } - - sk_sp getImage() { return mImage; } - - void updateTexture(); - - // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to - // destroy any VkImages that were made with that context. - void onVkContextDestroyed(); - -private: - int mWidth; - int mHeight; - bool mBlend; - - sk_sp mImage; - -}; // struct VkLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 7a8d026df3b4..e7d12de44c0c 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -15,11 +15,11 @@ */ #include "Bitmap.h" -#include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" +#include #include diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index c41f6a6f0ee6..fb66b50f0159 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -15,8 +15,6 @@ */ #include "LayerDrawable.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "GrBackendSurface.h" #include "SkColorFilter.h" @@ -41,35 +39,14 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer return false; } // transform the matrix based on the layer - SkMatrix layerTransform; - layer->getTransform().copyTo(layerTransform); - sk_sp layerImage; + SkMatrix layerTransform = layer->getTransform(); + sk_sp layerImage = layer->getImage(); const int layerWidth = layer->getWidth(); const int layerHeight = layer->getHeight(); - if (layer->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layer); - GrGLTextureInfo externalTexture; - externalTexture.fTarget = glLayer->getRenderTarget(); - externalTexture.fID = glLayer->getTextureId(); - // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't - // expose that info we use it as our default. Further, given that we only use this texture - // as a source this will not impact how Skia uses the texture. The only potential affect - // this is anticipated to have is that for some format types if we are not bound as an OES - // texture we may get invalid results for SKP capture if we read back the texture. - externalTexture.fFormat = GL_RGBA8; - GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture); - layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, - kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); - } else { - SkASSERT(layer->getApi() == Layer::Api::Vulkan); - VkLayer* vkLayer = static_cast(layer); - canvas->clear(SK_ColorGREEN); - layerImage = vkLayer->getImage(); - } if (layerImage) { SkMatrix textureMatrixInv; - layer->getTexTransform().copyTo(textureMatrixInv); + textureMatrixInv = layer->getTexTransform(); // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed // use bottom left origin and remove flipV and invert transformations. SkMatrix flipV; @@ -95,6 +72,9 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(layer->getColorSpaceWithFilter()); + if (layer->getForceFilter()) { + paint.setFilterQuality(kLow_SkFilterQuality); + } const bool nonIdentityMatrix = !matrix.isIdentity(); if (nonIdentityMatrix) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 78f5a71dee3b..2ae37233098e 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,7 +17,6 @@ #include "SkiaOpenGLPipeline.h" #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "LayerDrawable.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" @@ -187,18 +186,9 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, - bool blend) { - GlLayer* layer = - new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); - layer->generateTexture(); - return layer; -} - DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaOpenGLPipeline::onStop() { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index b2519fe59891..5f2eee4523fc 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -20,7 +20,6 @@ #include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" @@ -114,16 +113,10 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, - bool blend) { - return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); -} - DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { mVkManager.initialize(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaVulkanPipeline::onStop() {} diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp deleted file mode 100644 index 3a6efb833c47..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 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 "renderstate/PixelBufferState.h" - -namespace android { -namespace uirenderer { - -PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {} - -bool PixelBufferState::bind(GLuint buffer) { - if (mCurrentPixelBuffer != buffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); - mCurrentPixelBuffer = buffer; - return true; - } - return false; -} - -bool PixelBufferState::unbind() { - if (mCurrentPixelBuffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - mCurrentPixelBuffer = 0; - return true; - } - return false; -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h deleted file mode 100644 index f7ae6c575f6a..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2015 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 RENDERSTATE_PIXELBUFFERSTATE_H -#define RENDERSTATE_PIXELBUFFERSTATE_H - -#include - -namespace android { -namespace uirenderer { - -class PixelBufferState { - friend class Caches; // TODO: move to RenderState -public: - bool bind(GLuint buffer); - bool unbind(); - -private: - PixelBufferState(); - GLuint mCurrentPixelBuffer; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 3be84f588a20..b524bcb096da 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,8 +16,6 @@ #include "renderstate/RenderState.h" #include #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "Snapshot.h" #include "renderthread/CanvasContext.h" @@ -39,44 +37,11 @@ RenderState::RenderState(renderthread::RenderThread& thread) RenderState::~RenderState() { } -void RenderState::onGLContextCreated() { +void RenderState::onContextCreated() { GpuMemoryTracker::onGpuContextCreated(); - - // This is delayed because the first access of Caches makes GL calls - if (!mCaches) { - mCaches = &Caches::createInstance(*this); - } - mCaches->init(); } -static void layerLostGlContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, - "layerLostGlContext on non GL layer"); - static_cast(layer)->onGlContextLost(); -} - -void RenderState::onGLContextDestroyed() { - // TODO: reset all cached state in state objects - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - - mCaches->terminate(); - - destroyLayersInUpdater(); - GpuMemoryTracker::onGpuContextDestroyed(); -} - -void RenderState::onVkContextCreated() { - GpuMemoryTracker::onGpuContextCreated(); -} - -static void layerDestroyedVkContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan, - "layerLostVkContext on non Vulkan layer"); - static_cast(layer)->onVkContextDestroyed(); -} - -void RenderState::onVkContextDestroyed() { - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); +void RenderState::onContextDestroyed() { destroyLayersInUpdater(); GpuMemoryTracker::onGpuContextDestroyed(); } @@ -85,10 +50,6 @@ GrContext* RenderState::getGrContext() const { return mRenderThread.getGrContext(); } -void RenderState::flush(Caches::FlushMode mode) { - if (mCaches) mCaches->flush(mode); -} - void RenderState::onBitmapDestroyed(uint32_t pixelRefId) { // DEAD CODE } @@ -126,42 +87,6 @@ void RenderState::deleteFramebuffer(GLuint fbo) { glDeleteFramebuffers(1, &fbo); } -void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { - if (mode == DrawGlInfo::kModeProcessNoContext) { - // If there's no context we don't need to interrupt as there's - // no gl state to save/restore - (*functor)(mode, info); - } else { - interruptForFunctorInvoke(); - (*functor)(mode, info); - resumeFromFunctorInvoke(); - } -} - -void RenderState::interruptForFunctorInvoke() { - mCaches->textureState().resetActiveTexture(); - debugOverdraw(false, false); - // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glDisable(GL_FRAMEBUFFER_SRGB_EXT); - } -} - -void RenderState::resumeFromFunctorInvoke() { - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glEnable(GL_FRAMEBUFFER_SRGB_EXT); - } - - glViewport(0, 0, mViewportWidth, mViewportHeight); - glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - debugOverdraw(false, false); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - mCaches->textureState().activateTexture(0); - mCaches->textureState().resetBoundTextures(); -} - void RenderState::debugOverdraw(bool enable, bool clear) { // DEAD CODE } @@ -190,5 +115,9 @@ void RenderState::dump() { // DEAD CODE } +renderthread::RenderThread& RenderState::getRenderThread() { + return mRenderThread; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 97785a46dcd7..f39aa4b96547 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,8 +16,6 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H -#include "Caches.h" -#include "renderstate/PixelBufferState.h" #include "utils/Macros.h" #include @@ -34,7 +32,6 @@ class GrContext; namespace android { namespace uirenderer { -class Caches; class Layer; class DeferredLayerUpdater; @@ -44,22 +41,16 @@ class CanvasContext; class RenderThread; } -// TODO: Replace Cache's GL state tracking with this. For now it's more a thin // wrapper of Caches for users to migrate to. class RenderState { PREVENT_COPY_AND_ASSIGN(RenderState); friend class renderthread::RenderThread; - friend class Caches; friend class renderthread::CacheManager; public: - void onGLContextCreated(); - void onGLContextDestroyed(); + void onContextCreated(); + void onContextDestroyed(); - void onVkContextCreated(); - void onVkContextDestroyed(); - - void flush(Caches::FlushMode flushMode); void onBitmapDestroyed(uint32_t pixelRefId); void setViewport(GLsizei width, GLsizei height); @@ -70,8 +61,6 @@ public: GLuint createFramebuffer(); void deleteFramebuffer(GLuint fbo); - void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); - void debugOverdraw(bool enable, bool clear); void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } @@ -101,16 +90,15 @@ public: void dump(); + renderthread::RenderThread& getRenderThread(); + private: - void interruptForFunctorInvoke(); - void resumeFromFunctorInvoke(); void destroyLayersInUpdater(); explicit RenderState(renderthread::RenderThread& thread); ~RenderState(); renderthread::RenderThread& mRenderThread; - Caches* mCaches = nullptr; std::set mActiveLayers; std::set mActiveLayerUpdaters; diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp deleted file mode 100644 index 470b4f5de97f..000000000000 --- a/libs/hwui/renderstate/TextureState.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2015 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 "renderstate/TextureState.h" - -#include "Caches.h" -#include "utils/TraceUtils.h" - -#include -#include -#include -#include - -namespace android { -namespace uirenderer { - -// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is -static const int SHADOW_LUT_SIZE = 128; - -// Must define as many texture units as specified by kTextureUnitsCount -const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3}; - -TextureState::TextureState() : mTextureUnit(0) { - glActiveTexture(kTextureUnits[0]); - resetBoundTextures(); - - GLint maxTextureUnits; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, - "At least %d texture units are required!", kTextureUnitsCount); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); -} - -TextureState::~TextureState() { - if (mShadowLutTexture != nullptr) { - mShadowLutTexture->deleteTexture(); - } -} - -/** - * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to - * darkness at that spot. Input values of 0->1 should be mapped within the same - * range, but can affect the curve for a different visual falloff. - * - * This is used to populate the shadow LUT texture for quick lookup in the - * shadow shader. - */ -static float computeShadowOpacity(float ratio) { - // exponential falloff function provided by UX - float val = 1 - ratio; - return exp(-val * val * 4.0) - 0.018; -} - -void TextureState::constructTexture(Caches& caches) { - if (mShadowLutTexture == nullptr) { - mShadowLutTexture.reset(new Texture(caches)); - - unsigned char bytes[SHADOW_LUT_SIZE]; - for (int i = 0; i < SHADOW_LUT_SIZE; i++) { - float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f); - bytes[i] = computeShadowOpacity(inputRatio) * 255; - } - mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes); - mShadowLutTexture->setFilter(GL_LINEAR); - mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE); - } -} - -void TextureState::activateTexture(GLuint textureUnit) { - LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, - "Tried to use texture unit index %d, only %d exist", textureUnit, - kTextureUnitsCount); - if (mTextureUnit != textureUnit) { - glActiveTexture(kTextureUnits[textureUnit]); - mTextureUnit = textureUnit; - } -} - -void TextureState::resetActiveTexture() { - mTextureUnit = -1; -} - -void TextureState::bindTexture(GLuint texture) { - if (mBoundTextures[mTextureUnit] != texture) { - glBindTexture(GL_TEXTURE_2D, texture); - mBoundTextures[mTextureUnit] = texture; - } -} - -void TextureState::bindTexture(GLenum target, GLuint texture) { - if (target == GL_TEXTURE_2D) { - bindTexture(texture); - } else { - // GLConsumer directly calls glBindTexture() with - // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target - // since the cached state could be stale - glBindTexture(target, texture); - } -} - -void TextureState::deleteTexture(GLuint texture) { - // When glDeleteTextures() is called on a currently bound texture, - // OpenGL ES specifies that the texture is then considered unbound - // Consider the following series of calls: - // - // glGenTextures -> creates texture name 2 - // glBindTexture(2) - // glDeleteTextures(2) -> 2 is now unbound - // glGenTextures -> can return 2 again - // - // If we don't call glBindTexture(2) after the second glGenTextures - // call, any texture operation will be performed on the default - // texture (name=0) - - unbindTexture(texture); - - glDeleteTextures(1, &texture); -} - -void TextureState::resetBoundTextures() { - for (int i = 0; i < kTextureUnitsCount; i++) { - mBoundTextures[i] = 0; - } -} - -void TextureState::unbindTexture(GLuint texture) { - for (int i = 0; i < kTextureUnitsCount; i++) { - if (mBoundTextures[i] == texture) { - mBoundTextures[i] = 0; - } - } -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h deleted file mode 100644 index f1996d431fa2..000000000000 --- a/libs/hwui/renderstate/TextureState.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2015 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 RENDERSTATE_TEXTURESTATE_H -#define RENDERSTATE_TEXTURESTATE_H - -#include "Texture.h" -#include "Vertex.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -class Texture; - -class TextureState { - friend class Caches; // TODO: move to RenderState -public: - void constructTexture(Caches& caches); - - /** - * Activate the specified texture unit. The texture unit must - * be specified using an integer number (0 for GL_TEXTURE0 etc.) - */ - void activateTexture(GLuint textureUnit); - - /** - * Invalidate the cached value of the active texture unit. - */ - void resetActiveTexture(); - - /** - * Binds the specified texture as a GL_TEXTURE_2D texture. - * All texture bindings must be performed with this method or - * bindTexture(GLenum, GLuint). - */ - void bindTexture(GLuint texture); - - /** - * Binds the specified texture with the specified render target. - * All texture bindings must be performed with this method or - * bindTexture(GLuint). - */ - void bindTexture(GLenum target, GLuint texture); - - /** - * Deletes the specified texture and clears it from the cache - * of bound textures. - * All textures must be deleted using this method. - */ - void deleteTexture(GLuint texture); - - /** - * Signals that the cache of bound textures should be cleared. - * Other users of the context may have altered which textures are bound. - */ - void resetBoundTextures(); - - /** - * Clear the cache of bound textures. - */ - void unbindTexture(GLuint texture); - - Texture* getShadowLutTexture() { return mShadowLutTexture.get(); } - -private: - // total number of texture units available for use - static const int kTextureUnitsCount = 4; - - TextureState(); - ~TextureState(); - GLuint mTextureUnit; - - // Caches texture bindings for the GL_TEXTURE_2D target - GLuint mBoundTextures[kTextureUnitsCount]; - - std::unique_ptr mShadowLutTexture; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index bec80b1e6011..c45eedad775c 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -21,6 +21,7 @@ #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" +#include "Properties.h" #include "renderstate/RenderState.h" #include @@ -214,11 +215,12 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat(" Layer Info:\n"); } + const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL + ? "GlLayer" : "VkLayer"; size_t layerMemoryTotal = 0; for (std::set::iterator it = renderState->mActiveLayers.begin(); it != renderState->mActiveLayers.end(); it++) { const Layer* layer = *it; - const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5d7252304bf2..8b07d1dadeb6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,7 +18,6 @@ #include #include "AnimationContext.h" -#include "Caches.h" #include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" @@ -495,13 +494,6 @@ void CanvasContext::draw() { } GpuMemoryTracker::onFrameCompleted(); -#ifdef BUGREPORT_FONT_CACHE_USAGE - auto renderType = Properties::getRenderPipelineType(); - if (RenderPipelineType::OpenGL == renderType) { - Caches& caches = Caches::getInstance(); - caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); - } -#endif } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index cd21822df5b1..5f8d7ad3373a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "utils/StringUtils.h" @@ -464,6 +465,109 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } +status_t EglManager::fenceWait(sp& fence) { + if (!hasEglContext()) { + ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Block GPU on the fence. + // Create an EGLSyncKHR from the current fence. + int fenceFd = fence->dup(); + if (fenceFd == -1) { + ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, + EGL_NONE + }; + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(mEglDisplay, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(mEglDisplay, sync); + if (eglErr != EGL_SUCCESS) { + ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + // Block CPU on the fence. + status_t err = fence->waitForever("EglManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); + return err; + } + } + return OK; +} + +status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, + sp& nativeFence) { + if (!hasEglContext()) { + ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync); + eglDestroySyncKHR(mEglDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGE("EglManager::createReleaseFence: error dup'ing native fence " + "fd: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + nativeFence = new Fence(fenceFd); + *eglFence = EGL_NO_SYNC_KHR; + } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { + if (*eglFence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000); + if (result == EGL_FALSE) { + ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(mEglDisplay, *eglFence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr); + if (*eglFence == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + } + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 8e8bb8b68a1c..507673adf26e 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -17,8 +17,10 @@ #define EGLMANAGER_H #include +#include #include #include +#include #include #include @@ -66,6 +68,14 @@ public: EGLDisplay eglDisplay() const { return mEglDisplay; } + // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension + // support is missing, block the CPU on the fence. + status_t fenceWait(sp& fence); + + // Creates a fence that is signaled, when all the pending GL commands are flushed. + // Depending on installed extensions, the result is either Android native fence or EGL fence. + status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp& nativeFence); + private: void initExtensions(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 3eaf43b65f69..51be54ced5ec 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -178,7 +178,7 @@ void RenderThread::requireGlContext() { return; } mEglManager->initialize(); - renderState().onGLContextCreated(); + renderState().onContextCreated(); #ifdef HWUI_GLES_WRAP_ENABLED debug::GlesDriver* driver = debug::GlesDriver::get(); @@ -199,7 +199,7 @@ void RenderThread::requireGlContext() { void RenderThread::destroyGlContext() { if (mEglManager->hasEglContext()) { setGrContext(nullptr); - renderState().onGLContextDestroyed(); + renderState().onContextDestroyed(); mEglManager->destroy(); } } diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index a19edae26cb3..67b1972e874f 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -38,7 +38,7 @@ namespace renderthread { VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { - mRenderThread.renderState().onVkContextDestroyed(); + mRenderThread.renderState().onContextDestroyed(); mRenderThread.setGrContext(nullptr); if (VK_NULL_HANDLE != mCommandPool) { @@ -367,7 +367,7 @@ void VulkanManager::initialize() { mSwapBehavior = SwapBehavior::BufferAge; } - mRenderThread.renderState().onVkContextCreated(); + mRenderThread.renderState().onContextCreated(); } // Returns the next BackbufferInfo to use for the next draw. The function will make sure all @@ -944,6 +944,22 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } +status_t VulkanManager::fenceWait(sp& fence) { + //TODO: Insert a wait on fence command into the Vulkan command buffer. + // Block CPU on the fence. + status_t err = fence->waitForever("VulkanManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); + return err; + } + return OK; +} + +status_t VulkanManager::createReleaseFence(sp& nativeFence) { + //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 69641d57ccbb..7051ee686659 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -23,6 +23,8 @@ #include #include +#include +#include #include namespace android { @@ -108,6 +110,12 @@ public: // Presents the current VkImage. void swapBuffers(VulkanSurface* surface); + // Inserts a wait on fence command into the Vulkan command buffer. + status_t fenceWait(sp& fence); + + // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. + status_t createReleaseFence(sp& nativeFence); + private: friend class RenderThread; diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp new file mode 100644 index 000000000000..c8220c6cb0d4 --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2018 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include "EGLConsumer.h" +#include "SurfaceTexture.h" + +#include +#include +#include + +#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" +#define EGL_PROTECTED_CONTENT_EXT 0x32C0 + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +static const struct { + uint32_t width, height; + char const* bits; +} kDebugData = {15, 12, + "_______________" + "_______________" + "_____XX_XX_____" + "__X_X_____X_X__" + "__X_XXXXXXX_X__" + "__XXXXXXXXXXX__" + "___XX_XXX_XX___" + "____XXXXXXX____" + "_____X___X_____" + "____X_____X____" + "_______________" + "_______________"}; + +Mutex EGLConsumer::sStaticInitLock; +sp EGLConsumer::sReleasedTexImageBuffer; + +static bool hasEglProtectedContentImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); + bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); + bool atEnd = (cropExtLen + 1) < extsLen && + !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); + bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} + +static bool hasEglProtectedContent() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglProtectedContentImpl(); + return hasIt; +} + +EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} + +status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + return err; + } + + BufferItem item; + + // Acquire the next buffer. + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + err = st.acquireBufferLocked(&item, 0); + if (err != NO_ERROR) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // We always bind the texture even if we don't update its contents. + EGC_LOGV("updateTexImage: no buffers were available"); + glBindTexture(st.mTexTarget, st.mTexName); + err = NO_ERROR; + } else { + EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); + } + return err; + } + + // Release the previous buffer. + err = updateAndReleaseLocked(item, nullptr, st); + if (err != NO_ERROR) { + // We always bind the texture. + glBindTexture(st.mTexTarget, st.mTexName); + return err; + } + + // Bind the new buffer to the GL texture, and wait until it's ready. + return bindTextureImageLocked(st); +} + +status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = NO_ERROR; + + // if we're detached, no need to validate EGL's state -- we won't use it. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + err = checkAndUpdateEglStateLocked(st, true); + if (err != NO_ERROR) { + return err; + } + } + + // Update the EGLConsumer state. + int buf = st.mCurrentTexture; + if (buf != BufferQueue::INVALID_BUFFER_SLOT) { + EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); + + // if we're detached, we just use the fence that was created in detachFromContext() + // so... basically, nothing more to do here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // Do whatever sync ops we need to do before releasing the slot. + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); + return err; + } + } + + err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { + EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); + return err; + } + + if (mReleasedTexImage == nullptr) { + mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + } + + st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureImage = mReleasedTexImage; + st.mCurrentCrop.makeInvalid(); + st.mCurrentTransform = 0; + st.mCurrentTimestamp = 0; + st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; + st.mCurrentFence = Fence::NO_FENCE; + st.mCurrentFenceTime = FenceTime::NO_FENCE; + + // detached, don't touch the texture (and we may not even have an + // EGLDisplay here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // This binds a dummy buffer (mReleasedTexImage). + status_t result = bindTextureImageLocked(st); + if (result != NO_ERROR) { + return result; + } + } + } + + return NO_ERROR; +} + +sp EGLConsumer::getDebugTexImageBuffer() { + Mutex::Autolock _l(sStaticInitLock); + if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { + // The first time, create the debug texture in case the application + // continues to use it. + sp buffer = new GraphicBuffer( + kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); + uint32_t* bits; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast(&bits)); + uint32_t stride = buffer->getStride(); + uint32_t height = buffer->getHeight(); + memset(bits, 0, stride * height * 4); + for (uint32_t y = 0; y < kDebugData.height; y++) { + for (uint32_t x = 0; x < kDebugData.width; x++) { + bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 + : 0xFFFFFFFF; + } + bits += stride; + } + buffer->unlock(); + sReleasedTexImageBuffer = buffer; + } + return sReleasedTexImageBuffer; +} + +void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior EglImage created is using a stale buffer. This + // replaces any old EglImage with a new one (using the new buffer). + int slot = item->mSlot; + if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } +} + +void EGLConsumer::onReleaseBufferLocked(int buf) { + mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st) { + status_t err = NO_ERROR; + + int slot = item.mSlot; + + if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { + EGC_LOGE( + "updateAndRelease: EGLConsumer is not attached to an OpenGL " + "ES context"); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return INVALID_OPERATION; + } + + // Confirm state. + err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return err; + } + + // Ensure we have a valid EglImageKHR for the slot, creating an EglImage + // if nessessary, for the gralloc buffer currently in the slot in + // ConsumerBase. + // We may have to do this even when item.mGraphicBuffer == NULL (which + // means the buffer was previously acquired). + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, + slot); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return UNKNOWN_ERROR; + } + + // Do whatever sync ops we need to do before releasing the old slot. + if (slot != st.mCurrentTexture) { + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + // Release the buffer we just acquired. It's not safe to + // release the old buffer, so instead we just drop the new frame. + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + return err; + } + } + + EGC_LOGV( + "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, + mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, + slot, st.mSlots[slot].mGraphicBuffer->handle); + + // Hang onto the pointer so that it isn't freed in the call to + // releaseBufferLocked() if we're in shared buffer mode and both buffers are + // the same. + sp nextTextureImage = mEglSlots[slot].mEglImage; + + // release old buffer + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (pendingRelease == nullptr) { + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, + mEglSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), + status); + err = status; + // keep going, with error raised [?] + } + } else { + pendingRelease->currentTexture = st.mCurrentTexture; + pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); + pendingRelease->display = mEglDisplay; + pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; + pendingRelease->isPending = true; + } + } + + // Update the EGLConsumer state. + st.mCurrentTexture = slot; + mCurrentTextureImage = nextTextureImage; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + + st.computeCurrentTransformMatrixLocked(); + + return err; +} + +status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { + if (mEglDisplay == EGL_NO_DISPLAY) { + ALOGE("bindTextureImage: invalid display"); + return INVALID_OPERATION; + } + + GLenum error; + while ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); + } + + glBindTexture(st.mTexTarget, st.mTexName); + if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { + EGC_LOGE("bindTextureImage: no currently-bound texture"); + return NO_INIT; + } + + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + + // In the rare case that the display is terminated and then initialized + // again, we can't detect that the display changed (it didn't), but the + // image is invalid. In this case, repeat the exact same steps while + // forcing the creation of a new image. + if ((error = glGetError()) != GL_NO_ERROR) { + glBindTexture(st.mTexTarget, st.mTexName); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); + if (result != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + if ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); + return UNKNOWN_ERROR; + } + } + + // Wait for the new buffer to be ready. + return doGLFenceWaitLocked(st); +} + +status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (!contextCheck) { + // if this is the first time we're called, mEglDisplay/mEglContext have + // never been set, so don't error out (below). + if (mEglDisplay == EGL_NO_DISPLAY) { + mEglDisplay = dpy; + } + if (mEglContext == EGL_NO_CONTEXT) { + mEglContext = ctx; + } + } + + if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); + return INVALID_OPERATION; + } + + mEglDisplay = dpy; + mEglContext = ctx; + return NO_ERROR; +} + +status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { + EGC_LOGE("detachFromContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { + EGC_LOGE("detachFromContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { + status_t err = syncForReleaseLocked(dpy, st); + if (err != OK) { + return err; + } + + glDeleteTextures(1, &st.mTexName); + } + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + + return OK; +} + +status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { + // Initialize mCurrentTextureImage if there is a current buffer from past attached state. + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + if (!mEglSlots[slot].mEglImage.get()) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } + mCurrentTextureImage = mEglSlots[slot].mEglImage; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (dpy == EGL_NO_DISPLAY) { + EGC_LOGE("attachToContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (ctx == EGL_NO_CONTEXT) { + EGC_LOGE("attachToContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + // We need to bind the texture regardless of whether there's a current + // buffer. + glBindTexture(st.mTexTarget, GLuint(tex)); + + mEglDisplay = dpy; + mEglContext = ctx; + st.mTexName = tex; + st.mOpMode = SurfaceTexture::OpMode::attachedToGL; + + if (mCurrentTextureImage != nullptr) { + // This may wait for a buffer a second time. This is likely required if + // this is a different context, since otherwise the wait could be skipped + // by bouncing through another context. For the same context the extra + // wait is redundant. + status_t err = bindTextureImageLocked(st); + if (err != NO_ERROR) { + return err; + } + } + + return OK; +} + +status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { + EGC_LOGV("syncForReleaseLocked"); + + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); + eglDestroySyncKHR(dpy, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + EGC_LOGE( + "syncForReleaseLocked: error dup'ing native fence " + "fd: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + sp fence(new Fence(fenceFd)); + status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, + mCurrentTextureImage->graphicBuffer(), fence); + if (err != OK) { + EGC_LOGE( + "syncForReleaseLocked: error adding release fence: " + "%s (%d)", + strerror(-err), err); + return err; + } + } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { + EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; + if (fence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (result == EGL_FALSE) { + EGC_LOGE( + "syncForReleaseLocked: error waiting for previous " + "fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + EGC_LOGE( + "syncForReleaseLocked: timeout waiting for previous " + "fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(dpy, fence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); + if (fence == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + mEglSlots[st.mCurrentTexture].mEglFence = fence; + } + } + + return OK; +} + +status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + EGC_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (st.mCurrentFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = st.mCurrentFence->dup(); + if (fenceFd == -1) { + EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); + if (err != NO_ERROR) { + EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +void EGLConsumer::onFreeBufferLocked(int slotIndex) { + mEglSlots[slotIndex].mEglImage.clear(); +} + +void EGLConsumer::onAbandonLocked() { + mCurrentTextureImage.clear(); +} + +EGLConsumer::EglImage::EglImage(sp graphicBuffer) + : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} + +EGLConsumer::EglImage::~EglImage() { + if (mEglImage != EGL_NO_IMAGE_KHR) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("~EglImage: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + } +} + +status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { + // If there's an image and it's no longer valid, destroy it. + bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; + bool displayInvalid = mEglDisplay != eglDisplay; + if (haveImage && (displayInvalid || forceCreation)) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("createIfNeeded: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + mEglImage = EGL_NO_IMAGE_KHR; + mEglDisplay = EGL_NO_DISPLAY; + } + + // If there's no image, create one. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = eglDisplay; + mEglImage = createImage(mEglDisplay, mGraphicBuffer); + } + + // Fail if we can't create a valid image. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = EGL_NO_DISPLAY; + const sp& buffer = mGraphicBuffer; + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), + buffer->getPixelFormat()); + return UNKNOWN_ERROR; + } + + return OK; +} + +void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { + glEGLImageTargetTexture2DOES(texTarget, static_cast(mEglImage)); +} + +EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, + const sp& graphicBuffer) { + EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); + const bool createProtectedImage = + (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, + EGL_TRUE, + createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + createProtectedImage ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + eglInitialize(dpy, nullptr, nullptr); + EGLImageKHR image = + eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + if (image == EGL_NO_IMAGE_KHR) { + EGLint error = eglGetError(); + ALOGE("error creating EGLImage: %#x", error); + eglTerminate(dpy); + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h new file mode 100644 index 000000000000..eccb08298f6f --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +namespace android { + +class SurfaceTexture; + +/* + * EGLConsumer implements the parts of SurfaceTexture that deal with + * textures attached to an GL context. + */ +class EGLConsumer { +public: + EGLConsumer(); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(SurfaceTexture& st); + + /* + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(SurfaceTexture& st); + + /** + * detachFromContext detaches the EGLConsumer from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a EGLConsumer from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a EGLConsumer has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the EGLConsumer is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(SurfaceTexture& st); + + /** + * attachToContext attaches a EGLConsumer that is currently in the + * 'detached' state to the current OpenGL ES context. A EGLConsumer is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * EGLConsumer that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex, SurfaceTexture& st); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + + /** + * onAbandonLocked amends the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + void onAbandonLocked(); + +protected: + struct PendingRelease { + PendingRelease() + : isPending(false) + , currentTexture(-1) + , graphicBuffer() + , display(nullptr) + , fence(nullptr) {} + + bool isPending; + int currentTexture; + sp graphicBuffer; + EGLDisplay display; + EGLSyncKHR fence; + }; + + /** + * This releases the buffer in the slot referenced by mCurrentTexture, + * then updates state to refer to the BufferItem, which must be a + * newly-acquired buffer. If pendingRelease is not null, the parameters + * which would have been passed to releaseBufferLocked upon the successful + * completion of the method will instead be returned to the caller, so that + * it may call releaseBufferLocked itself later. + */ + status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st); + + /** + * Binds mTexName and the current buffer to mTexTarget. Uses + * mCurrentTexture if it's set, mCurrentTextureImage if not. If the + * bind succeeds, this calls doGLFenceWait. + */ + status_t bindTextureImageLocked(SurfaceTexture& st); + + /** + * Gets the current EGLDisplay and EGLContext values, and compares them + * to mEglDisplay and mEglContext. If the fields have been previously + * set, the values must match; if not, the fields are set to the current + * values. + * The contextCheck argument is used to ensure that a GL context is + * properly set; when set to false, the check is not performed. + */ + status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); + + /** + * EglImage is a utility class for tracking and creating EGLImageKHRs. There + * is primarily just one image per slot, but there is also special cases: + * - For releaseTexImage, we use a debug image (mReleasedTexImage) + * - After freeBuffer, we must still keep the current image/buffer + * Reference counting EGLImages lets us handle all these cases easily while + * also only creating new EGLImages from buffers when required. + */ + class EglImage : public LightRefBase { + public: + EglImage(sp graphicBuffer); + + /** + * createIfNeeded creates an EGLImage if required (we haven't created + * one yet, or the EGLDisplay or crop-rect has changed). + */ + status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); + + /** + * This calls glEGLImageTargetTexture2DOES to bind the image to the + * texture in the specified texture target. + */ + void bindToTextureTarget(uint32_t texTarget); + + const sp& graphicBuffer() { return mGraphicBuffer; } + const native_handle* graphicBufferHandle() { + return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; + } + + private: + // Only allow instantiation using ref counting. + friend class LightRefBase; + virtual ~EglImage(); + + // createImage creates a new EGLImage from a GraphicBuffer. + EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer); + + // Disallow copying + EglImage(const EglImage& rhs); + void operator=(const EglImage& rhs); + + // mGraphicBuffer is the buffer that was used to create this image. + sp mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEGLDisplay is the EGLDisplay that was used to create mEglImage. + EGLDisplay mEglDisplay; + + // mCropRect is the crop rectangle passed to EGL when mEglImage + // was created. + Rect mCropRect; + }; + + /** + * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command + * stream to ensure that it is safe for future OpenGL ES commands to + * access the current texture buffer. + */ + status_t doGLFenceWaitLocked(SurfaceTexture& st) const; + + /** + * syncForReleaseLocked performs the synchronization needed to release the + * current slot from an OpenGL ES context. If needed it will set the + * current slot's fence to guard against a producer accessing the buffer + * before the outstanding accesses have completed. + */ + status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); + + /** + * returns a graphic buffer used when the texture image has been released + */ + static sp getDebugTexImageBuffer(); + + /** + * The default consumer usage flags that EGLConsumer always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the EGLConsumer user. In particular, EGLConsumer will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentTextureImage is the EglImage/buffer of the current texture. It's + * possible that this buffer is not associated with any buffer slot, so we + * must track it separately in order to support the getCurrentBuffer method. + */ + sp mCurrentTextureImage; + + /** + * EGLSlot contains the information and object references that + * EGLConsumer maintains about a BufferQueue buffer slot. + */ + struct EglSlot { + EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + /** + * mEglImage is the EGLImage created from mGraphicBuffer. + */ + sp mEglImage; + + /** + * mFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. It is initialized + * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + * on a compile-time option) set to a new sync object in updateTexImage. + */ + EGLSyncKHR mEglFence; + }; + + /** + * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently + * associated. It is intialized to EGL_NO_DISPLAY and gets set to the + * current display when updateTexImage is called for the first time and when + * attachToContext is called. + */ + EGLDisplay mEglDisplay; + + /** + * mEglContext is the OpenGL ES context with which this EGLConsumer is + * currently associated. It is initialized to EGL_NO_CONTEXT and gets set + * to the current GL context when updateTexImage is called for the first + * time and when attachToContext is called. + */ + EGLContext mEglContext; + + /** + * mEGLSlots stores the buffers that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; + + /** + * protects static initialization + */ + static Mutex sStaticInitLock; + + /** + * mReleasedTexImageBuffer is a dummy buffer used when in single buffer + * mode and releaseTexImage() has been called + */ + static sp sReleasedTexImageBuffer; + sp mReleasedTexImage; +}; + +}; // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp new file mode 100644 index 000000000000..c86cd962ebed --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 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 "ImageConsumer.h" +#include +#include "Properties.h" +#include "SurfaceTexture.h" +#include "renderstate/RenderState.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "renderthread/VulkanManager.h" + +// Macro for including the SurfaceTexture name in log messages +#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +namespace android { + +void ImageConsumer::onFreeBufferLocked(int slotIndex) { + mImageSlots[slotIndex].mImage.reset(); +} + +void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. + if (item->mGraphicBuffer != nullptr) { + mImageSlots[item->mSlot].mImage.reset(); + } +} + +void ImageConsumer::onReleaseBufferLocked(int buf) { + mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer) { + if (!mImage.get()) { + mImage = graphicBuffer.get() + ? SkImage::MakeFromAHardwareBuffer( + reinterpret_cast(graphicBuffer.get()), + kPremul_SkAlphaType, SkColorSpace::MakeSRGB()) + : nullptr; + } +} + +sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, + uirenderer::RenderState& renderState) { + BufferItem item; + status_t err; + err = st.acquireBufferLocked(&item, 0); + if (err != OK) { + if (err != BufferQueue::NO_BUFFER_AVAILABLE) { + IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } else { + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + *queueEmpty = true; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; + } + } + return nullptr; + } + + int slot = item.mSlot; + if (item.mFence->isValid()) { + // Wait on the producer fence for the buffer to be ready. + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); + } else { + err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence); + } + if (err != OK) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Release old buffer. + if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { + // If needed, set the released slot's fence to guard against a producer accessing the + // buffer before the outstanding accesses have completed. + sp releaseFence; + EGLDisplay display = EGL_NO_DISPLAY; + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + auto& eglManager = renderState.getRenderThread().eglManager(); + display = eglManager.eglDisplay(); + err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence, + releaseFence); + } else { + err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence); + } + if (OK != err) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + + if (releaseFence.get()) { + status_t err = st.addReleaseFenceLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); + if (err != OK) { + IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Finally release the old buffer. + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, + mImageSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); + err = status; + // Keep going, with error raised. + } + } + + // Update the state. + st.mCurrentTexture = slot; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + st.computeCurrentTransformMatrixLocked(); + + *queueEmpty = false; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; +} + +} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h new file mode 100644 index 000000000000..31ee8db52874 --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + +namespace android { + +namespace uirenderer { +class RenderState; +} + +class SurfaceTexture; + +/* + * ImageConsumer implements the parts of SurfaceTexture that deal with + * images consumed by HWUI view system. + */ +class ImageConsumer { +public: + sk_sp dequeueImage(bool* queueEmpty, SurfaceTexture& cb, + uirenderer::RenderState& renderState); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the SkImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + +private: + /** + * ImageSlot contains the information and object references that + * ImageConsumer maintains about a BufferQueue buffer slot. + */ + struct ImageSlot { + ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + // mImage is the SkImage created from mGraphicBuffer. + sk_sp mImage; + + /** + * mEglFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. + */ + EGLSyncKHR mEglFence; + + void createIfNeeded(sp graphicBuffer); + }; + + /** + * ImageConsumer stores the SkImages that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; +}; + +}; /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp new file mode 100644 index 000000000000..4bff715822e8 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2018 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 +#include +#include +#include + +#include + +#include "Matrix.h" +#include "SurfaceTexture.h" + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) + +static const mat4 mtxIdentity; + +SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t tex, + uint32_t texTarget, bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(tex) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::attachedToGL) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t texTarget, + bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(0) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::detached) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); + return NO_INIT; + } + mDefaultWidth = w; + mDefaultHeight = h; + return mConsumer->setDefaultBufferSize(w, h); +} + +status_t SurfaceTexture::updateTexImage() { + ATRACE_CALL(); + SFT_LOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.updateTexImage(*this); +} + +status_t SurfaceTexture::releaseTexImage() { + // releaseTexImage can be invoked even when not attached to a GL context. + ATRACE_CALL(); + SFT_LOGV("releaseTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.releaseTexImage(*this); +} + +status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber) { + status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); + if (err != NO_ERROR) { + return err; + } + + switch (mOpMode) { + case OpMode::attachedToView: + mImageConsumer.onAcquireBufferLocked(item); + break; + case OpMode::attachedToGL: + mEGLConsumer.onAcquireBufferLocked(item, *this); + break; + case OpMode::detached: + break; + } + + return NO_ERROR; +} + +status_t SurfaceTexture::releaseBufferLocked(int buf, sp graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); + // We could be releasing an EGL buffer, even if not currently attached to a GL context. + mImageConsumer.onReleaseBufferLocked(buf); + mEGLConsumer.onReleaseBufferLocked(buf); + return err; +} + +status_t SurfaceTexture::detachFromContext() { + ATRACE_CALL(); + SFT_LOGV("detachFromContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::attachedToGL) { + SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); + return INVALID_OPERATION; + } + + status_t err = mEGLConsumer.detachFromContext(*this); + if (err == OK) { + mOpMode = OpMode::detached; + } + + return err; +} + +status_t SurfaceTexture::attachToContext(uint32_t tex) { + ATRACE_CALL(); + SFT_LOGV("attachToContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("attachToContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::detached) { + SFT_LOGE( + "attachToContext: SurfaceTexture is already attached to a " + "context"); + return INVALID_OPERATION; + } + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible ImageConsumer cache + mImageConsumer.onFreeBufferLocked(mCurrentTexture); + } + + return mEGLConsumer.attachToContext(tex, *this); +} + +void SurfaceTexture::attachToView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + if (mAbandoned) { + SFT_LOGE("attachToView: abandoned SurfaceTexture"); + return; + } + if (mOpMode == OpMode::detached) { + mOpMode = OpMode::attachedToView; + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible EGLConsumer texture cache + mEGLConsumer.onFreeBufferLocked(mCurrentTexture); + mEGLConsumer.onAbandonLocked(); + } + } else { + SFT_LOGE("attachToView: already attached"); + } +} + +void SurfaceTexture::detachFromView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromView: abandoned SurfaceTexture"); + return; + } + + if (mOpMode == OpMode::attachedToView) { + mOpMode = OpMode::detached; + } else { + SFT_LOGE("detachFromView: not attached to View"); + } +} + +uint32_t SurfaceTexture::getCurrentTextureTarget() const { + return mTexTarget; +} + +void SurfaceTexture::getTransformMatrix(float mtx[16]) { + Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void SurfaceTexture::setFilteringEnabled(bool enabled) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); + return; + } + bool needsRecompute = mFilteringEnabled != enabled; + mFilteringEnabled = enabled; + + if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { + SFT_LOGD("setFilteringEnabled called with no current item"); + } + + if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + computeCurrentTransformMatrixLocked(); + } +} + +void SurfaceTexture::computeCurrentTransformMatrixLocked() { + SFT_LOGV("computeCurrentTransformMatrixLocked"); + sp buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) + ? nullptr + : mSlots[mCurrentTexture].mGraphicBuffer; + if (buf == nullptr) { + SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); + } + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, + mFilteringEnabled); +} + +void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp& buf, + const Rect& cropRect, uint32_t transform, + bool filtering) { + // Transform matrices + static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); + static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + + mat4 xform; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + xform *= mtxFlipH; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + xform *= mtxFlipV; + } + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + xform *= mtxRot90; + } + + if (!cropRect.isEmpty() && buf.get()) { + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + float shrinkAmount = 0.0f; + if (filtering) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; + + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; + } + + mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); + xform = crop * xform; + } + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so SurfaceTexture must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + xform = mtxFlipV * xform; + + memcpy(outTransform, xform.asArray(), sizeof(xform)); +} + +Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { + Rect outCrop = crop; + + uint32_t newWidth = static_cast(crop.width()); + uint32_t newHeight = static_cast(crop.height()); + + if (newWidth * bufferHeight > newHeight * bufferWidth) { + newWidth = newHeight * bufferWidth / bufferHeight; + ALOGV("too wide: newWidth = %d", newWidth); + } else if (newWidth * bufferHeight < newHeight * bufferWidth) { + newHeight = newWidth * bufferHeight / bufferWidth; + ALOGV("too tall: newHeight = %d", newHeight); + } + + uint32_t currentWidth = static_cast(crop.width()); + uint32_t currentHeight = static_cast(crop.height()); + + // The crop is too wide + if (newWidth < currentWidth) { + uint32_t dw = currentWidth - newWidth; + auto halfdw = dw / 2; + outCrop.left += halfdw; + // Not halfdw because it would subtract 1 too few when dw is odd + outCrop.right -= (dw - halfdw); + // The crop is too tall + } else if (newHeight < currentHeight) { + uint32_t dh = currentHeight - newHeight; + auto halfdh = dh / 2; + outCrop.top += halfdh; + // Not halfdh because it would subtract 1 too few when dh is odd + outCrop.bottom -= (dh - halfdh); + } + + ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, + outCrop.bottom); + + return outCrop; +} + +nsecs_t SurfaceTexture::getTimestamp() { + SFT_LOGV("getTimestamp"); + Mutex::Autolock lock(mMutex); + return mCurrentTimestamp; +} + +android_dataspace SurfaceTexture::getCurrentDataSpace() { + SFT_LOGV("getCurrentDataSpace"); + Mutex::Autolock lock(mMutex); + return mCurrentDataSpace; +} + +uint64_t SurfaceTexture::getFrameNumber() { + SFT_LOGV("getFrameNumber"); + Mutex::Autolock lock(mMutex); + return mCurrentFrameNumber; +} + +Rect SurfaceTexture::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) + ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) + : mCurrentCrop; +} + +uint32_t SurfaceTexture::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + +uint32_t SurfaceTexture::getCurrentScalingMode() const { + Mutex::Autolock lock(mMutex); + return mCurrentScalingMode; +} + +sp SurfaceTexture::getCurrentFence() const { + Mutex::Autolock lock(mMutex); + return mCurrentFence; +} + +std::shared_ptr SurfaceTexture::getCurrentFenceTime() const { + Mutex::Autolock lock(mMutex); + return mCurrentFenceTime; +} + +void SurfaceTexture::freeBufferLocked(int slotIndex) { + SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + if (slotIndex == mCurrentTexture) { + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + } + // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. + // Buffers can be freed after SurfaceTexture has detached from GL context or View. + mImageConsumer.onFreeBufferLocked(slotIndex); + mEGLConsumer.onFreeBufferLocked(slotIndex); + ConsumerBase::freeBufferLocked(slotIndex); +} + +void SurfaceTexture::abandonLocked() { + SFT_LOGV("abandonLocked"); + mEGLConsumer.onAbandonLocked(); + ConsumerBase::abandonLocked(); +} + +status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { + return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); +} + +void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat( + "%smTexName=%d mCurrentTexture=%d\n" + "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", + prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, + mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); + + ConsumerBase::dumpLocked(result, prefix); +} + +sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, + uirenderer::RenderState& renderState) { + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); + return nullptr; + } + + if (mOpMode != OpMode::attachedToView) { + SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); + return nullptr; + } + + auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); + if (image.get()) { + uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); + dataSpace = mCurrentDataSpace; + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h new file mode 100644 index 000000000000..db392a9f8476 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.h @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include +#include + +#include +#include + +#include "EGLConsumer.h" +#include "ImageConsumer.h" + +namespace android { + +namespace uirenderer { +class RenderState; +} + +/* + * SurfaceTexture consumes buffers of graphics data from a BufferQueue, + * and makes them available to HWUI render thread as a SkImage and to + * an application GL render thread as an OpenGL texture. + * + * When attached to an application GL render thread, a typical usage + * pattern is to set up the SurfaceTexture with the + * desired options, and call updateTexImage() when a new frame is desired. + * If a new frame is available, the texture will be updated. If not, + * the previous contents are retained. + * + * When attached to a HWUI render thread, the TextureView implementation + * calls dequeueImage, which either pulls a new SkImage or returns the + * last cached SkImage if BufferQueue is empty. + * When attached to HWUI render thread, SurfaceTexture is compatible to + * both Vulkan and GL drawing pipelines. + */ +class ANDROID_API SurfaceTexture : public ConsumerBase { +public: + enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES + typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; + + /** + * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with + * the tex parameter is used, tex indicates the name of the OpenGL ES + * texture to which images are to be streamed. texTarget specifies the + * OpenGL ES texture target to which the texture will be bound in + * updateTexImage. useFenceSync specifies whether fences should be used to + * synchronize access to buffers if that behavior is enabled at + * compile-time. + * + * A SurfaceTexture may be detached from one OpenGL ES context and then + * attached to a different context using the detachFromContext and + * attachToContext methods, respectively. The intention of these methods is + * purely to allow a SurfaceTexture to be transferred from one consumer + * context to another. If such a transfer is not needed there is no + * requirement that either of these methods be called. + * + * If the constructor with the tex parameter is used, the SurfaceTexture is + * created in a state where it is considered attached to an OpenGL ES + * context for the purposes of the attachToContext and detachFromContext + * methods. However, despite being considered "attached" to a context, the + * specific OpenGL ES context doesn't get latched until the first call to + * updateTexImage. After that point, all calls to updateTexImage must be + * made with the same OpenGL ES context current. + * + * If the constructor without the tex parameter is used, the SurfaceTexture is + * created in a detached state, and attachToContext must be called before + * calls to updateTexImage. + */ + SurfaceTexture(const sp& bq, uint32_t tex, uint32_t texureTarget, + bool useFenceSync, bool isControlledByApp); + + SurfaceTexture(const sp& bq, uint32_t texureTarget, bool useFenceSync, + bool isControlledByApp); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(); + + /** + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(); + + /** + * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix + * associated with the texture image set by the most recent call to + * updateTexImage. + * + * This transform matrix maps 2D homogeneous texture coordinates of the form + * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture + * coordinate that should be used to sample that location from the texture. + * Sampling the texture outside of the range of this transform is undefined. + * + * This transform is necessary to compensate for transforms that the stream + * content producer may implicitly apply to the content. By forcing users of + * a SurfaceTexture to apply this transform we avoid performing an extra + * copy of the data that would be needed to hide the transform from the + * user. + * + * The matrix is stored in column-major order so that it may be passed + * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv + * functions. + */ + void getTransformMatrix(float mtx[16]); + + /** + * Computes the transform matrix documented by getTransformMatrix + * from the BufferItem sub parts. + */ + static void computeTransformMatrix(float outTransform[16], const sp& buf, + const Rect& cropRect, uint32_t transform, bool filtering); + + /** + * Scale the crop down horizontally or vertically such that it has the + * same aspect ratio as the buffer does. + */ + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + + /** + * getTimestamp retrieves the timestamp associated with the texture image + * set by the most recent call to updateTexImage. + * + * The timestamp is in nanoseconds, and is monotonically increasing. Its + * other semantics (zero point, etc) are source-dependent and should be + * documented by the source. + */ + int64_t getTimestamp(); + + /** + * getDataSpace retrieves the DataSpace associated with the texture image + * set by the most recent call to updateTexImage. + */ + android_dataspace getCurrentDataSpace(); + + /** + * getFrameNumber retrieves the frame number associated with the texture + * image set by the most recent call to updateTexImage. + * + * The frame number is an incrementing counter set to 0 at the creation of + * the BufferQueue associated with this consumer. + */ + uint64_t getFrameNumber(); + + /** + * setDefaultBufferSize is used to set the size of buffers returned by + * requestBuffers when a with and height of zero is requested. + * A call to setDefaultBufferSize() may trigger requestBuffers() to + * be called from the client. + * The width and height parameters must be no greater than the minimum of + * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + * An error due to invalid dimensions might not be reported until + * updateTexImage() is called. + */ + status_t setDefaultBufferSize(uint32_t width, uint32_t height); + + /** + * setFilteringEnabled sets whether the transform matrix should be computed + * for use with bilinear filtering. + */ + void setFilteringEnabled(bool enabled); + + /** + * getCurrentTextureTarget returns the texture target of the current + * texture as returned by updateTexImage(). + */ + uint32_t getCurrentTextureTarget() const; + + /** + * getCurrentCrop returns the cropping rectangle of the current buffer. + */ + Rect getCurrentCrop() const; + + /** + * getCurrentTransform returns the transform of the current buffer. + */ + uint32_t getCurrentTransform() const; + + /** + * getCurrentScalingMode returns the scaling mode of the current buffer. + */ + uint32_t getCurrentScalingMode() const; + + /** + * getCurrentFence returns the fence indicating when the current buffer is + * ready to be read from. + */ + sp getCurrentFence() const; + + /** + * getCurrentFence returns the FenceTime indicating when the current + * buffer is ready to be read from. + */ + std::shared_ptr getCurrentFenceTime() const; + + /** + * setConsumerUsageBits overrides the ConsumerBase method to OR + * DEFAULT_USAGE_FLAGS to usage. + */ + status_t setConsumerUsageBits(uint64_t usage); + + /** + * detachFromContext detaches the SurfaceTexture from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a SurfaceTexture from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a SurfaceTexture has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the SurfaceTexture is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(); + + /** + * attachToContext attaches a SurfaceTexture that is currently in the + * 'detached' state to the current OpenGL ES context. A SurfaceTexture is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * SurfaceTexture that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex); + + sk_sp dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, uirenderer::RenderState& renderState); + + /** + * attachToView attaches a SurfaceTexture that is currently in the + * 'detached' state to HWUI View system. + */ + void attachToView(); + + /** + * detachFromView detaches a SurfaceTexture from HWUI View system. + */ + void detachFromView(); + +protected: + /** + * abandonLocked overrides the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + virtual void abandonLocked(); + + /** + * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- + * specific info in addition to the ConsumerBase behavior. + */ + virtual void dumpLocked(String8& result, const char* prefix) const override; + + /** + * acquireBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) override; + + /** + * releaseBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) override; + + /** + * freeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + * + * This method must be called with mMutex locked. + */ + virtual void freeBufferLocked(int slotIndex); + + /** + * computeCurrentTransformMatrixLocked computes the transform matrix for the + * current texture. It uses mCurrentTransform and the current GraphicBuffer + * to compute this matrix and stores it in mCurrentTransformMatrix. + * mCurrentTextureImage must not be NULL. + */ + void computeCurrentTransformMatrixLocked(); + + /** + * The default consumer usage flags that SurfaceTexture always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the SurfaceTexture user. In particular, SurfaceTexture will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentCrop is the crop rectangle that applies to the current texture. + * It gets set each time updateTexImage is called. + */ + Rect mCurrentCrop; + + /** + * mCurrentTransform is the transform identifier for the current texture. It + * gets set each time updateTexImage is called. + */ + uint32_t mCurrentTransform; + + /** + * mCurrentScalingMode is the scaling mode for the current texture. It gets + * set each time updateTexImage is called. + */ + uint32_t mCurrentScalingMode; + + /** + * mCurrentFence is the fence received from BufferQueue in updateTexImage. + */ + sp mCurrentFence; + + /** + * The FenceTime wrapper around mCurrentFence. + */ + std::shared_ptr mCurrentFenceTime{FenceTime::NO_FENCE}; + + /** + * mCurrentTransformMatrix is the transform matrix for the current texture. + * It gets computed by computeTransformMatrix each time updateTexImage is + * called. + */ + float mCurrentTransformMatrix[16]; + + /** + * mCurrentTimestamp is the timestamp for the current texture. It + * gets set each time updateTexImage is called. + */ + int64_t mCurrentTimestamp; + + /** + * mCurrentDataSpace is the dataspace for the current texture. It + * gets set each time updateTexImage is called. + */ + android_dataspace mCurrentDataSpace; + + /** + * mCurrentFrameNumber is the frame counter for the current texture. + * It gets set each time updateTexImage is called. + */ + uint64_t mCurrentFrameNumber; + + uint32_t mDefaultWidth, mDefaultHeight; + + /** + * mFilteringEnabled indicates whether the transform matrix is computed for + * use with bilinear filtering. It defaults to true and is changed by + * setFilteringEnabled(). + */ + bool mFilteringEnabled; + + /** + * mTexName is the name of the OpenGL texture to which streamed images will + * be bound when updateTexImage is called. It is set at construction time + * and can be changed with a call to attachToContext. + */ + uint32_t mTexName; + + /** + * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync + * extension should be used to prevent buffers from being dequeued before + * it's safe for them to be written. It gets set at construction time and + * never changes. + */ + const bool mUseFenceSync; + + /** + * mTexTarget is the GL texture target with which the GL texture object is + * associated. It is set in the constructor and never changed. It is + * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android + * Browser. In that case it is set to GL_TEXTURE_2D to allow + * glCopyTexSubImage to read from the texture. This is a hack to work + * around a GL driver limitation on the number of FBO attachments, which the + * browser's tile cache exceeds. + */ + const uint32_t mTexTarget; + + /** + * mCurrentTexture is the buffer slot index of the buffer that is currently + * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + * indicating that no buffer slot is currently bound to the texture. Note, + * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + * that no buffer is bound to the texture. A call to setBufferCount will + * reset mCurrentTexture to INVALID_BUFFER_SLOT. + */ + int mCurrentTexture; + + enum class OpMode { detached, attachedToView, attachedToGL }; + /** + * mOpMode indicates whether the SurfaceTexture is currently attached to + * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, + * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to + * whatever GL context is current at the time of the first updateTexImage call. + * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by + * attachToContext. + * attachToView/detachFromView are used to attach/detach from HWUI view system. + */ + OpMode mOpMode; + + /** + * mEGLConsumer has SurfaceTexture logic used when attached to GL context. + */ + EGLConsumer mEGLConsumer; + + /** + * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. + */ + ImageConsumer mImageConsumer; + + friend class ImageConsumer; + friend class EGLConsumer; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp index 5b361548eeda..d2d37dcb34f2 100644 --- a/libs/hwui/tests/common/LeakChecker.cpp +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -16,7 +16,6 @@ #include "LeakChecker.h" -#include "Caches.h" #include "TestUtils.h" #include @@ -71,9 +70,6 @@ void LeakChecker::checkForLeaks() { // thread-local caches so some leaks will not be properly tagged as leaks UnreachableMemoryInfo rtMemInfo; TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { - if (Caches::hasInstance()) { - Caches::getInstance().tasks.stop(); - } // Check for leaks if (!GetUnreachableMemory(rtMemInfo)) { cerr << "Failed to get unreachable memory!" << endl; diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 69586345319e..66b9b85bdbe7 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -67,16 +67,14 @@ sp TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { sp layerUpdater = createTextureLayerUpdater(renderThread); - layerUpdater->backingLayer()->getTransform().load(transform); + layerUpdater->backingLayer()->getTransform() = transform; layerUpdater->setSize(width, height); layerUpdater->setTransform(&transform); // updateLayer so it's ready to draw - layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - static_cast(layerUpdater->backingLayer()) - ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix identity; + identity.setIdentity(); + layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr); return layerUpdater; } @@ -117,7 +115,6 @@ void TestUtils::TestTask::run() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { renderThread.vulkanManager().destroy(); } else { - renderThread.renderState().flush(Caches::FlushMode::Full); renderThread.destroyGlContext(); } } diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 743f8093bfa8..0e6582c59a36 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -18,7 +18,6 @@ #include #include -#include #include #include #include diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index f29830f0e34b..6c8775b1bdbb 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -15,12 +15,13 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "Properties.h" #include "tests/common/TestUtils.h" #include +#include +#include using namespace android; using namespace android::uirenderer; @@ -31,10 +32,6 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { layerUpdater->setBlend(true); // updates are deferred so the backing layer should still be in its default state - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget()); - } EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight()); EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter()); @@ -42,19 +39,13 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); // push the deferred updates to the layer - Matrix4 scaledMatrix; - scaledMatrix.loadScale(0.5, 0.5, 0.0); - layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5); + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + sk_sp layerImage = SkImage::MakeFromBitmap(bitmap); + layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage); // the backing layer should now have all the properties applied. - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget()); - } EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight()); EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter()); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 9e6d9a8c27de..aecceb3609f5 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -17,12 +17,13 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "Caches.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "Properties.h" #include "tests/common/LeakChecker.h" +#include "thread/TaskProcessor.h" +#include "thread/Task.h" #include "thread/TaskManager.h" #include diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index f8e8a0a18284..ebf2343c5518 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -16,6 +16,7 @@ #ifndef PAINT_UTILS_H #define PAINT_UTILS_H +#include #include #include diff --git a/native/android/Android.bp b/native/android/Android.bp index 4fb5e748aaac..43847cc4ab06 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -64,6 +64,7 @@ cc_library_shared { "libsensor", "libandroid_runtime", "libnetd_client", + "libhwui", ], static_libs: [ diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp index b26688190ccd..ced2792775d4 100644 --- a/native/android/surface_texture.cpp +++ b/native/android/surface_texture.cpp @@ -21,15 +21,16 @@ #include -#include #include #include +#include "surfacetexture/SurfaceTexture.h" + using namespace android; struct ASurfaceTexture { - sp consumer; + sp consumer; sp producer; }; -- cgit v1.2.3-59-g8ed1b From 867c43de0544217d26c3ee18f4d6603bb2ea97ce Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 30 Aug 2018 16:47:59 +0000 Subject: Revert "TextureView Vulkan support and optimized OpenGL draw" This reverts commit c8e22a653297837da9a80b0ba65f6854c8986c96. Reason for revert: broke camera, b/113555199 Bug: 113555199 Change-Id: Iae9b462694d5de0cd99427afead63b567fb4d71d --- core/jni/android/graphics/ColorFilter.cpp | 2 + core/jni/android/graphics/Matrix.cpp | 1 + core/jni/android/graphics/Shader.cpp | 1 + core/jni/android/graphics/SurfaceTexture.cpp | 47 +- core/jni/android_view_TextureLayer.cpp | 3 +- .../android_graphics_SurfaceTexture.h | 6 +- libs/hwui/Android.bp | 10 +- libs/hwui/Caches.cpp | 103 ++++ libs/hwui/Caches.h | 135 +++++ libs/hwui/DeferredLayerUpdater.cpp | 123 +++- libs/hwui/DeferredLayerUpdater.h | 34 +- libs/hwui/GlLayer.cpp | 68 +++ libs/hwui/GlLayer.h | 75 +++ libs/hwui/GpuMemoryTracker.cpp | 17 + libs/hwui/Layer.cpp | 8 +- libs/hwui/Layer.h | 57 +- libs/hwui/PixelBuffer.cpp | 156 +++++ libs/hwui/PixelBuffer.h | 198 ++++++ libs/hwui/RenderProperties.h | 1 + libs/hwui/ResourceCache.cpp | 23 +- libs/hwui/Texture.cpp | 413 +++++++++++++ libs/hwui/Texture.h | 228 +++++++ libs/hwui/VkLayer.cpp | 40 ++ libs/hwui/VkLayer.h | 70 +++ libs/hwui/hwui/Bitmap.cpp | 2 +- libs/hwui/pipeline/skia/LayerDrawable.cpp | 32 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 12 +- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 9 +- libs/hwui/renderstate/PixelBufferState.cpp | 42 ++ libs/hwui/renderstate/PixelBufferState.h | 38 ++ libs/hwui/renderstate/RenderState.cpp | 83 ++- libs/hwui/renderstate/RenderState.h | 20 +- libs/hwui/renderstate/TextureState.cpp | 147 +++++ libs/hwui/renderstate/TextureState.h | 98 +++ libs/hwui/renderthread/CacheManager.cpp | 4 +- libs/hwui/renderthread/CanvasContext.cpp | 8 + libs/hwui/renderthread/EglManager.cpp | 104 ---- libs/hwui/renderthread/EglManager.h | 10 - libs/hwui/renderthread/RenderThread.cpp | 4 +- libs/hwui/renderthread/VulkanManager.cpp | 20 +- libs/hwui/renderthread/VulkanManager.h | 8 - libs/hwui/surfacetexture/EGLConsumer.cpp | 675 --------------------- libs/hwui/surfacetexture/EGLConsumer.h | 311 ---------- libs/hwui/surfacetexture/ImageConsumer.cpp | 152 ----- libs/hwui/surfacetexture/ImageConsumer.h | 97 --- libs/hwui/surfacetexture/SurfaceTexture.cpp | 496 --------------- libs/hwui/surfacetexture/SurfaceTexture.h | 452 -------------- libs/hwui/tests/common/LeakChecker.cpp | 4 + libs/hwui/tests/common/TestUtils.cpp | 11 +- libs/hwui/tests/common/TestUtils.h | 1 + libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp | 23 +- libs/hwui/tests/unit/main.cpp | 3 +- libs/hwui/utils/PaintUtils.h | 1 - native/android/Android.bp | 1 - native/android/surface_texture.cpp | 5 +- 55 files changed, 2202 insertions(+), 2490 deletions(-) create mode 100644 libs/hwui/Caches.cpp create mode 100644 libs/hwui/Caches.h create mode 100644 libs/hwui/GlLayer.cpp create mode 100644 libs/hwui/GlLayer.h create mode 100644 libs/hwui/PixelBuffer.cpp create mode 100644 libs/hwui/PixelBuffer.h create mode 100644 libs/hwui/Texture.cpp create mode 100644 libs/hwui/Texture.h create mode 100644 libs/hwui/VkLayer.cpp create mode 100644 libs/hwui/VkLayer.h create mode 100644 libs/hwui/renderstate/PixelBufferState.cpp create mode 100644 libs/hwui/renderstate/PixelBufferState.h create mode 100644 libs/hwui/renderstate/TextureState.cpp create mode 100644 libs/hwui/renderstate/TextureState.h delete mode 100644 libs/hwui/surfacetexture/EGLConsumer.cpp delete mode 100644 libs/hwui/surfacetexture/EGLConsumer.h delete mode 100644 libs/hwui/surfacetexture/ImageConsumer.cpp delete mode 100644 libs/hwui/surfacetexture/ImageConsumer.h delete mode 100644 libs/hwui/surfacetexture/SurfaceTexture.cpp delete mode 100644 libs/hwui/surfacetexture/SurfaceTexture.h (limited to 'libs/hwui/ResourceCache.cpp') diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index 6ebf35c8e1dc..3fcedd0264ae 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -22,6 +22,8 @@ #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" +#include + namespace android { using namespace uirenderer; diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index 755fcfb27141..f8bb77a9650c 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -20,6 +20,7 @@ #include "SkMatrix.h" #include "core_jni_helpers.h" +#include #include namespace android { diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 68f5bef18de1..cff772002b14 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -6,6 +6,7 @@ #include "SkBlendMode.h" #include "core_jni_helpers.h" +#include #include using namespace android::uirenderer; diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index 3e464c61665f..d098a355085e 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -36,7 +36,6 @@ #include "jni.h" #include #include -#include "surfacetexture/SurfaceTexture.h" // ---------------------------------------------------------------------------- @@ -81,10 +80,10 @@ static bool isProtectedContext() { // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp& surfaceTexture) + const sp& surfaceTexture) { - SurfaceTexture* const p = - (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); + GLConsumer* const p = + (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); if (surfaceTexture.get()) { surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture); } @@ -109,10 +108,10 @@ static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz, } static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, - jobject thiz, sp listener) + jobject thiz, sp listener) { - SurfaceTexture::FrameAvailableListener* const p = - (SurfaceTexture::FrameAvailableListener*) + GLConsumer::FrameAvailableListener* const p = + (GLConsumer::FrameAvailableListener*) env->GetLongField(thiz, fields.frameAvailableListener); if (listener.get()) { listener->incStrong((void*)SurfaceTexture_setSurfaceTexture); @@ -123,8 +122,8 @@ static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get()); } -sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { - return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); +sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { + return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); } sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) { @@ -132,7 +131,7 @@ sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) } sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); sp producer(SurfaceTexture_getProducer(env, thiz)); sp surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL); return surfaceTextureClient; @@ -145,7 +144,7 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) { // ---------------------------------------------------------------------------- -class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener +class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener { public: JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz); @@ -267,12 +266,12 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, consumer->setMaxBufferCount(1); } - sp surfaceTexture; + sp surfaceTexture; if (isDetached) { - surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES, + surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } else { - surfaceTexture = new SurfaceTexture(consumer, texName, + surfaceTexture = new GLConsumer(consumer, texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } @@ -307,7 +306,7 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setFrameAvailableListener(0); SurfaceTexture_setFrameAvailableListener(env, thiz, 0); SurfaceTexture_setSurfaceTexture(env, thiz, 0); @@ -316,13 +315,13 @@ static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) static void SurfaceTexture_setDefaultBufferSize( JNIEnv* env, jobject thiz, jint width, jint height) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->updateTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to update texture contents (see " @@ -334,7 +333,7 @@ static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->releaseTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to release texture contents (see " @@ -346,20 +345,20 @@ static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); float* mtx = env->GetFloatArrayElements(jmtx, NULL); surfaceTexture->getTransformMatrix(mtx); env->ReleaseFloatArrayElements(jmtx, mtx, 0); @@ -367,19 +366,19 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->isAbandoned(); } diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp index 1ccb6a8f610c..d3a447f1f7dc 100644 --- a/core/jni/android_view_TextureLayer.cpp +++ b/core/jni/android_view_TextureLayer.cpp @@ -67,7 +67,8 @@ static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr, jobject surface) { DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); - layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); + layer->setSurfaceTexture(surfaceTexture); } static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h index 0ad25876a008..c534d4bb9e0a 100644 --- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h +++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h @@ -23,14 +23,14 @@ namespace android { +class GLConsumer; class IGraphicBufferProducer; -class SurfaceTexture; extern sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz); extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz); -/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */ -extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */ +extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); /* gets the producer end of the SurfaceTexture */ extern sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 62ab7900737e..83e90b654ff6 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -175,7 +175,9 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", + "renderstate/PixelBufferState.cpp", "renderstate/RenderState.cpp", + "renderstate/TextureState.cpp", "renderthread/CacheManager.cpp", "renderthread/CanvasContext.cpp", "renderthread/DrawFrameTask.cpp", @@ -187,9 +189,6 @@ cc_defaults { "renderthread/TimeLord.cpp", "renderthread/Frame.cpp", "service/GraphicsStatsService.cpp", - "surfacetexture/EGLConsumer.cpp", - "surfacetexture/ImageConsumer.cpp", - "surfacetexture/SurfaceTexture.cpp", "thread/TaskManager.cpp", "utils/Blur.cpp", "utils/Color.cpp", @@ -201,6 +200,7 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", + "Caches.cpp", "CanvasState.cpp", "CanvasTransform.cpp", "ClipArea.cpp", @@ -209,6 +209,7 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", + "GlLayer.cpp", "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "Interpolator.cpp", @@ -218,6 +219,7 @@ cc_defaults { "Matrix.cpp", "EglReadback.cpp", "PathParser.cpp", + "PixelBuffer.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Properties.cpp", @@ -228,7 +230,9 @@ cc_defaults { "ResourceCache.cpp", "SkiaCanvas.cpp", "Snapshot.cpp", + "Texture.cpp", "VectorDrawable.cpp", + "VkLayer.cpp", "protos/graphicsstats.proto", ], diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp new file mode 100644 index 000000000000..254144448859 --- /dev/null +++ b/libs/hwui/Caches.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 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 "GlLayer.h" +#include "Properties.h" +#include "renderstate/RenderState.h" +#include "utils/GLUtils.h" + +#include +#include +#include + +namespace android { +namespace uirenderer { + +Caches* Caches::sInstance = nullptr; + +/////////////////////////////////////////////////////////////////////////////// +// Macros +/////////////////////////////////////////////////////////////////////////////// + +#if DEBUG_CACHE_FLUSH +#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) +#else +#define FLUSH_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +Caches::Caches(RenderState& renderState) : mInitialized(false) { + INIT_LOGD("Creating OpenGL renderer caches"); + init(); + initStaticProperties(); +} + +bool Caches::init() { + if (mInitialized) return false; + + ATRACE_NAME("Caches::init"); + + mRegionMesh = nullptr; + + mInitialized = true; + + mPixelBufferState = new PixelBufferState(); + mTextureState = new TextureState(); + mTextureState->constructTexture(*this); + + return true; +} + +void Caches::initStaticProperties() { + // OpenGL ES 3.0+ specific features + gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() && + property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); +} + +void Caches::terminate() { + if (!mInitialized) return; + mRegionMesh.reset(nullptr); + + clearGarbage(); + + delete mPixelBufferState; + mPixelBufferState = nullptr; + delete mTextureState; + mTextureState = nullptr; + mInitialized = false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Memory management +/////////////////////////////////////////////////////////////////////////////// + +void Caches::clearGarbage() {} + +void Caches::flush(FlushMode mode) { + clearGarbage(); + glFinish(); + // Errors during cleanup should be considered non-fatal, dump them and + // and move on. TODO: All errors or just errors like bad surface? + GLUtils::dumpGLErrors(); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h new file mode 100644 index 000000000000..642f9dc50eb1 --- /dev/null +++ b/libs/hwui/Caches.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 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. + */ + +#pragma once + +#include "DeviceInfo.h" +#include "Extensions.h" +#include "ResourceCache.h" +#include "renderstate/PixelBufferState.h" +#include "renderstate/TextureState.h" +#include "thread/TaskManager.h" +#include "thread/TaskProcessor.h" + +#include +#include + +#include + +#include + +#include + +#include + +#include + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Caches +/////////////////////////////////////////////////////////////////////////////// + +class RenderNode; +class RenderState; + +class ANDROID_API Caches { +public: + static Caches& createInstance(RenderState& renderState) { + LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted"); + sInstance = new Caches(renderState); + return *sInstance; + } + + static Caches& getInstance() { + LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created"); + return *sInstance; + } + + static bool hasInstance() { return sInstance != nullptr; } + +private: + explicit Caches(RenderState& renderState); + static Caches* sInstance; + +public: + enum class FlushMode { Layers = 0, Moderate, Full }; + + /** + * Initialize caches. + */ + bool init(); + + bool isInitialized() { return mInitialized; } + + /** + * Flush the cache. + * + * @param mode Indicates how much of the cache should be flushed + */ + void flush(FlushMode mode); + + /** + * Destroys all resources associated with this cache. This should + * be called after a flush(FlushMode::Full). + */ + void terminate(); + + /** + * Call this on each frame to ensure that garbage is deleted from + * GPU memory. + */ + void clearGarbage(); + + /** + * 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().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + } + +public: + TaskManager tasks; + + bool gpuPixelBuffersEnabled; + + const Extensions& extensions() const { return DeviceInfo::get()->extensions(); } + PixelBufferState& pixelBufferState() { return *mPixelBufferState; } + TextureState& textureState() { return *mTextureState; } + +private: + void initStaticProperties(); + + static void eventMarkNull(GLsizei length, const GLchar* marker) {} + static void startMarkNull(GLsizei length, const GLchar* marker) {} + static void endMarkNull() {} + + // Used to render layers + std::unique_ptr mRegionMesh; + + bool mInitialized; + + // TODO: move below to RenderState + PixelBufferState* mPixelBufferState = nullptr; + TextureState* mTextureState = nullptr; + +}; // class Caches + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 00916559a9c2..569de76f294e 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,20 +15,27 @@ */ #include "DeferredLayerUpdater.h" +#include "GlLayer.h" +#include "VkLayer.h" #include "renderstate/RenderState.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderTask.h" #include "utils/PaintUtils.h" namespace android { namespace uirenderer { -DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) +DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, + Layer::Api layerApi) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) - , mLayer(nullptr) { + , mLayer(nullptr) + , mLayerApi(layerApi) + , mCreateLayerFn(createLayerFn) { renderState.registerDeferredLayerUpdater(this); } @@ -43,9 +50,13 @@ void DeferredLayerUpdater::destroyLayer() { return; } - if (mSurfaceTexture.get() && mGLContextAttached) { - mSurfaceTexture->detachFromView(); + if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) { + status_t err = mSurfaceTexture->detachFromContext(); mGLContextAttached = false; + if (err != 0) { + // TODO: Elevate to fatal exception + ALOGE("Failed to detach SurfaceTexture from context %d", err); + } } mLayer->postDecStrong(); @@ -64,53 +75,99 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { void DeferredLayerUpdater::apply() { if (!mLayer) { - mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); + mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend); } mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { - if (!mGLContextAttached) { - mGLContextAttached = true; - mUpdateTexImage = true; - mSurfaceTexture->attachToView(); - } - if (mUpdateTexImage) { - mUpdateTexImage = false; - sk_sp layerImage; - SkMatrix textureTransform; - android_dataspace dataSpace; - bool queueEmpty = true; - // If the SurfaceTexture queue is in synchronous mode, need to discard all - // but latest frame. Since we can't tell which mode it is in, - // do this unconditionally. - do { - layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty, - mRenderState); - } while (layerImage.get() && (!queueEmpty)); - if (layerImage.get()) { - // force filtration if buffer size != layer size - bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); - updateLayer(forceFilter, textureTransform, dataSpace, layerImage); + if (mLayer->getApi() == Layer::Api::Vulkan) { + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateVkTexImage(); + } + } else { + LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, + "apply surfaceTexture with non GL backend %x, GL %x, VK %x", + mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); + if (!mGLContextAttached) { + mGLContextAttached = true; + mUpdateTexImage = true; + mSurfaceTexture->attachToContext(static_cast(mLayer)->getTextureId()); } + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateTexImage(); + } + GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); + static_cast(mLayer)->setRenderTarget(renderTarget); } - if (mTransform) { - mLayer->getTransform() = *mTransform; + mLayer->getTransform().load(*mTransform); setTransform(nullptr); } } } -void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, - android_dataspace dataspace, const sk_sp& layerImage) { +void DeferredLayerUpdater::doUpdateTexImage() { + LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, + "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), + Layer::Api::OpenGL, Layer::Api::Vulkan); + if (mSurfaceTexture->updateTexImage() == NO_ERROR) { + float transform[16]; + + int64_t frameNumber = mSurfaceTexture->getFrameNumber(); + // If the GLConsumer queue is in synchronous mode, need to discard all + // but latest frame, using the frame number to tell when we no longer + // have newer frames to target. Since we can't tell which mode it is in, + // do this unconditionally. + int dropCounter = 0; + while (mSurfaceTexture->updateTexImage() == NO_ERROR) { + int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); + if (newFrameNumber == frameNumber) break; + frameNumber = newFrameNumber; + dropCounter++; + } + + bool forceFilter = false; + sp buffer = mSurfaceTexture->getCurrentBuffer(); + if (buffer != nullptr) { + // force filtration if buffer size != layer size + forceFilter = mWidth != static_cast(buffer->getWidth()) || + mHeight != static_cast(buffer->getHeight()); + } + +#if DEBUG_RENDERER + if (dropCounter > 0) { + RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); + } +#endif + mSurfaceTexture->getTransformMatrix(transform); + + updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace()); + } +} + +void DeferredLayerUpdater::doUpdateVkTexImage() { + LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, + "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), + Layer::Api::OpenGL, Layer::Api::Vulkan); + + static const mat4 identityMatrix; + updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN); + + VkLayer* vkLayer = static_cast(mLayer); + vkLayer->updateTexture(); +} + +void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform, + android_dataspace dataspace) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); - mLayer->getTexTransform() = textureTransform; + mLayer->getTexTransform().load(textureTransform); mLayer->setDataSpace(dataspace); - mLayer->setImage(layerImage); } void DeferredLayerUpdater::detachSurfaceTexture() { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 4c323b861002..fe3ee7a2b4c6 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -17,19 +17,18 @@ #pragma once #include -#include #include #include -#include +#include #include #include #include #include -#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" +#include "renderthread/RenderThread.h" namespace android { namespace uirenderer { @@ -42,7 +41,12 @@ class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState); + typedef std::function colorFilter, int alpha, + SkBlendMode mode, bool blend)> + CreateLayerFn; + ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, + Layer::Api layerApi); ANDROID_API ~DeferredLayerUpdater(); @@ -66,13 +70,13 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp& consumer) { - if (consumer.get() != mSurfaceTexture.get()) { - mSurfaceTexture = consumer; + ANDROID_API void setSurfaceTexture(const sp& texture) { + if (texture.get() != mSurfaceTexture.get()) { + mSurfaceTexture = texture; - GLenum target = consumer->getCurrentTextureTarget(); + GLenum target = texture->getCurrentTextureTarget(); LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported SurfaceTexture with target %x", target); + "set unsupported GLConsumer with target %x", target); } } @@ -93,11 +97,12 @@ public: void detachSurfaceTexture(); - void updateLayer(bool forceFilter, const SkMatrix& textureTransform, - android_dataspace dataspace, const sk_sp& layerImage); + void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace); void destroyLayer(); + Layer::Api getBackingLayerApi() { return mLayerApi; } + private: RenderState& mRenderState; @@ -108,12 +113,17 @@ private: sk_sp mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp mSurfaceTexture; + sp mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; Layer* mLayer; + Layer::Api mLayerApi; + CreateLayerFn mCreateLayerFn; + + void doUpdateTexImage(); + void doUpdateVkTexImage(); }; } /* namespace uirenderer */ diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp new file mode 100644 index 000000000000..432bb8526465 --- /dev/null +++ b/libs/hwui/GlLayer.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 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 "GlLayer.h" + +#include "Caches.h" +#include "RenderNode.h" +#include "renderstate/RenderState.h" + +namespace android { +namespace uirenderer { + +GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) + : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode) + , caches(Caches::getInstance()) + , texture(caches) { + texture.mWidth = layerWidth; + texture.mHeight = layerHeight; + texture.blend = blend; +} + +GlLayer::~GlLayer() { + // There's a rare possibility that Caches could have been destroyed already + // since this method is queued up as a task. + // Since this is a reset method, treat this as non-fatal. + if (caches.isInitialized() && texture.mId) { + texture.deleteTexture(); + } +} + +void GlLayer::onGlContextLost() { + texture.deleteTexture(); +} + +void GlLayer::setRenderTarget(GLenum renderTarget) { + if (renderTarget != getRenderTarget()) { + // new render target: bind with new target, and update filter/wrap + texture.mTarget = renderTarget; + if (texture.mId) { + caches.textureState().bindTexture(texture.target(), texture.mId); + } + texture.setFilter(GL_NEAREST, false, true); + texture.setWrap(GL_CLAMP_TO_EDGE, false, true); + } +} + +void GlLayer::generateTexture() { + if (!texture.mId) { + glGenTextures(1, &texture.mId); + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h new file mode 100644 index 000000000000..9f70fdae6790 --- /dev/null +++ b/libs/hwui/GlLayer.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 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. + */ + +#pragma once + +#include "Layer.h" + +#include "Texture.h" + +namespace android { +namespace uirenderer { + +// Forward declarations +class Caches; + +/** + * A layer has dimensions and is backed by an OpenGL texture or FBO. + */ +class GlLayer : public Layer { +public: + GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend); + virtual ~GlLayer(); + + uint32_t getWidth() const override { return texture.mWidth; } + + uint32_t getHeight() const override { return texture.mHeight; } + + void setSize(uint32_t width, uint32_t height) override { + texture.updateLayout(width, height, texture.internalFormat(), texture.format(), + texture.target()); + } + + void setBlend(bool blend) override { texture.blend = blend; } + + bool isBlend() const override { return texture.blend; } + + inline GLuint getTextureId() const { return texture.id(); } + + inline GLenum getRenderTarget() const { return texture.target(); } + + void setRenderTarget(GLenum renderTarget); + + void generateTexture(); + + /** + * Lost the GL context but the layer is still around, mark it invalid internally + * so the dtor knows not to do any GL work + */ + void onGlContextLost(); + +private: + Caches& caches; + + /** + * The texture backing this layer. + */ + Texture texture; +}; // struct GlLayer + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index a9a7af8f22f3..612bfde1a3fa 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "Texture.h" #include "utils/StringUtils.h" #include @@ -116,6 +117,22 @@ void GpuMemoryTracker::onFrameCompleted() { ATRACE_INT(buf, stats.count); } } + + std::vector freeList; + for (const auto& obj : gObjectSet) { + if (obj->objectType() == GpuObjectType::Texture) { + const Texture* texture = static_cast(obj); + if (texture->cleanup) { + ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(), + texture->width(), texture->height()); + freeList.push_back(texture); + } + } + } + for (auto& texture : freeList) { + const_cast(texture)->deleteTexture(); + delete texture; + } } } // namespace uirenderer diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index f59a2e6ee5c1..fb8f0337c95e 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -17,17 +17,17 @@ #include "Layer.h" #include "renderstate/RenderState.h" -#include "utils/Color.h" #include namespace android { namespace uirenderer { -Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alpha, - SkBlendMode mode) +Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter, int alpha, + SkBlendMode mode) : GpuMemoryTracker(GpuObjectType::Layer) , mRenderState(renderState) + , mApi(api) , mColorFilter(colorFilter) , alpha(alpha) , mode(mode) { @@ -36,8 +36,6 @@ Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alp incStrong(nullptr); buildColorSpaceWithFilter(); renderState.registerLayer(this); - texTransform.setIdentity(); - transform.setIdentity(); } Layer::~Layer() { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index c4e4c1c96ba6..31878ac23642 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -23,9 +23,8 @@ #include #include #include -#include -#include -#include + +#include "Matrix.h" namespace android { namespace uirenderer { @@ -41,19 +40,24 @@ class RenderState; */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - Layer(RenderState& renderState, sk_sp, int alpha, SkBlendMode mode); + enum class Api { + OpenGL = 0, + Vulkan = 1, + }; + + Api getApi() const { return mApi; } ~Layer(); - virtual uint32_t getWidth() const { return mWidth; } + virtual uint32_t getWidth() const = 0; - virtual uint32_t getHeight() const { return mHeight; } + virtual uint32_t getHeight() const = 0; - virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } + virtual void setSize(uint32_t width, uint32_t height) = 0; - virtual void setBlend(bool blend) { mBlend = blend; } + virtual void setBlend(bool blend) = 0; - virtual bool isBlend() const { return mBlend; } + virtual bool isBlend() const = 0; inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } @@ -80,9 +84,9 @@ public: inline sk_sp getColorSpaceWithFilter() const { return mColorSpaceWithFilter; } - inline SkMatrix& getTexTransform() { return texTransform; } + inline mat4& getTexTransform() { return texTransform; } - inline SkMatrix& getTransform() { return transform; } + inline mat4& getTransform() { return transform; } /** * Posts a decStrong call to the appropriate thread. @@ -90,17 +94,16 @@ public: */ void postDecStrong(); - inline void setImage(const sk_sp& image) { this->layerImage = image; } - - inline sk_sp getImage() const { return this->layerImage; } - protected: + Layer(RenderState& renderState, Api api, sk_sp, int alpha, SkBlendMode mode); RenderState& mRenderState; private: void buildColorSpaceWithFilter(); + Api mApi; + /** * Color filter used to draw this layer. Optional. */ @@ -134,32 +137,12 @@ private: /** * Optional texture coordinates transform. */ - SkMatrix texTransform; + mat4 texTransform; /** * Optional transform. */ - SkMatrix transform; - - /** - * An image backing the layer. - */ - sk_sp layerImage; - - /** - * layer width. - */ - uint32_t mWidth = 0; - - /** - * layer height. - */ - uint32_t mHeight = 0; - - /** - * enable blending - */ - bool mBlend = false; + mat4 transform; }; // struct Layer diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp new file mode 100644 index 000000000000..910a9889db1f --- /dev/null +++ b/libs/hwui/PixelBuffer.cpp @@ -0,0 +1,156 @@ +/* + * 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 "PixelBuffer.h" + +#include "Debug.h" +#include "Extensions.h" +#include "Properties.h" +#include "renderstate/RenderState.h" +#include "utils/GLUtils.h" + +#include + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// CPU pixel buffer +/////////////////////////////////////////////////////////////////////////////// + +class CpuPixelBuffer : public PixelBuffer { +public: + CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); + + uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; + + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; + +protected: + void unmap() override; + +private: + std::unique_ptr mBuffer; +}; + +CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) + : PixelBuffer(format, width, height) + , mBuffer(new uint8_t[width * height * formatSize(format)]) {} + +uint8_t* CpuPixelBuffer::map(AccessMode mode) { + if (mAccessMode == kAccessMode_None) { + mAccessMode = mode; + } + return mBuffer.get(); +} + +void CpuPixelBuffer::unmap() { + mAccessMode = kAccessMode_None; +} + +void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, + &mBuffer[offset]); +} + +/////////////////////////////////////////////////////////////////////////////// +// GPU pixel buffer +/////////////////////////////////////////////////////////////////////////////// + +class GpuPixelBuffer : public PixelBuffer { +public: + GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); + ~GpuPixelBuffer(); + + uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; + + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; + +protected: + void unmap() override; + +private: + GLuint mBuffer; + uint8_t* mMappedPointer; + Caches& mCaches; +}; + +GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) + : PixelBuffer(format, width, height) + , mMappedPointer(nullptr) + , mCaches(Caches::getInstance()) { + glGenBuffers(1, &mBuffer); + + mCaches.pixelBufferState().bind(mBuffer); + glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW); + mCaches.pixelBufferState().unbind(); +} + +GpuPixelBuffer::~GpuPixelBuffer() { + glDeleteBuffers(1, &mBuffer); +} + +uint8_t* GpuPixelBuffer::map(AccessMode mode) { + if (mAccessMode == kAccessMode_None) { + mCaches.pixelBufferState().bind(mBuffer); + mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); + if (CC_UNLIKELY(!mMappedPointer)) { + GLUtils::dumpGLErrors(); + LOG_ALWAYS_FATAL("Failed to map PBO"); + } + mAccessMode = mode; + mCaches.pixelBufferState().unbind(); + } + + return mMappedPointer; +} + +void GpuPixelBuffer::unmap() { + if (mAccessMode != kAccessMode_None) { + if (mMappedPointer) { + mCaches.pixelBufferState().bind(mBuffer); + GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + if (status == GL_FALSE) { + ALOGE("Corrupted GPU pixel buffer"); + } + } + mAccessMode = kAccessMode_None; + mMappedPointer = nullptr; + } +} + +void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { + // If the buffer is not mapped, unmap() will not bind it + mCaches.pixelBufferState().bind(mBuffer); + unmap(); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, + reinterpret_cast(offset)); + mCaches.pixelBufferState().unbind(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Factory +/////////////////////////////////////////////////////////////////////////////// + +PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { + if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { + return new GpuPixelBuffer(format, width, height); + } + return new CpuPixelBuffer(format, width, height); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h new file mode 100644 index 000000000000..e7e341b90ad3 --- /dev/null +++ b/libs/hwui/PixelBuffer.h @@ -0,0 +1,198 @@ +/* + * 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_PIXEL_BUFFER_H +#define ANDROID_HWUI_PIXEL_BUFFER_H + +#include + +#include + +namespace android { +namespace uirenderer { + +/** + * Represents a pixel buffer. A pixel buffer will be backed either by a + * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other + * versions. If the buffer is backed by a PBO it will of type + * GL_PIXEL_UNPACK_BUFFER. + * + * To read from or write into a PixelBuffer you must first map the + * buffer using the map(AccessMode) method. This method returns a + * pointer to the beginning of the buffer. + * + * Before the buffer can be used by the GPU, for instance to upload + * a texture, you must first unmap the buffer. To do so, call the + * unmap() method. + * + * Mapping and unmapping a PixelBuffer can have the side effect of + * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is + * therefore recommended to call Caches::unbindPixelbuffer() after + * using a PixelBuffer to upload to a texture. + */ +class PixelBuffer { +public: + enum BufferType { kBufferType_Auto, kBufferType_CPU }; + + enum AccessMode { + kAccessMode_None = 0, + kAccessMode_Read = GL_MAP_READ_BIT, + kAccessMode_Write = GL_MAP_WRITE_BIT, + kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT + }; + + /** + * Creates a new PixelBuffer object with the specified format and + * dimensions. The buffer is immediately allocated. + * + * The buffer type specifies how the buffer should be allocated. + * By default this method will automatically choose whether to allocate + * a CPU or GPU buffer. + */ + static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height, + BufferType type = kBufferType_Auto); + + virtual ~PixelBuffer() {} + + /** + * Returns the format of this render buffer. + */ + GLenum getFormat() const { return mFormat; } + + /** + * Maps this before with the specified access mode. This method + * returns a pointer to the region of memory where the buffer was + * mapped. + * + * If the buffer is already mapped when this method is invoked, + * this method will return the previously mapped pointer. The + * access mode can only be changed by calling unmap() first. + * + * The specified access mode cannot be kAccessMode_None. + */ + virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0; + + /** + * Returns the current access mode for this buffer. If the buffer + * is not mapped, this method returns kAccessMode_None. + */ + AccessMode getAccessMode() const { return mAccessMode; } + + /** + * Upload the specified rectangle of this pixel buffer as a + * GL_TEXTURE_2D texture. Calling this method will trigger + * an unmap() if necessary. + */ + virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0; + + /** + * Upload the specified rectangle of this pixel buffer as a + * GL_TEXTURE_2D texture. Calling this method will trigger + * an unmap() if necessary. + * + * This is a convenience function provided to save callers the + * trouble of computing the offset parameter. + */ + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + upload(x, y, width, height, getOffset(x, y)); + } + + /** + * Returns the width of the render buffer in pixels. + */ + uint32_t getWidth() const { return mWidth; } + + /** + * Returns the height of the render buffer in pixels. + */ + uint32_t getHeight() const { return mHeight; } + + /** + * Returns the size of this pixel buffer in bytes. + */ + uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); } + + /** + * Returns the offset of a pixel in this pixel buffer, in bytes. + */ + uint32_t getOffset(uint32_t x, uint32_t y) const { + return (y * mWidth + x) * formatSize(mFormat); + } + + /** + * Returns the number of bytes per pixel in the specified format. + * + * Supported formats: + * GL_ALPHA + * GL_RGBA + */ + static uint32_t formatSize(GLenum format) { + switch (format) { + case GL_ALPHA: + return 1; + case GL_RGBA: + return 4; + } + return 0; + } + + /** + * Returns the alpha channel offset in the specified format. + * + * Supported formats: + * GL_ALPHA + * GL_RGBA + */ + static uint32_t formatAlphaOffset(GLenum format) { + switch (format) { + case GL_ALPHA: + return 0; + case GL_RGBA: + return 3; + } + + ALOGE("unsupported format: %d", format); + return 0; + } + +protected: + /** + * Creates a new render buffer in the specified format and dimensions. + * The format must be GL_ALPHA or GL_RGBA. + */ + PixelBuffer(GLenum format, uint32_t width, uint32_t height) + : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {} + + /** + * Unmaps this buffer, if needed. After the buffer is unmapped, + * the pointer previously returned by map() becomes invalid and + * should not be used. + */ + virtual void unmap() = 0; + + GLenum mFormat; + + uint32_t mWidth; + uint32_t mHeight; + + AccessMode mAccessMode; + +}; // class PixelBuffer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_PIXEL_BUFFER_H diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 7966845ff814..0766e3b7ed28 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,6 +16,7 @@ #pragma once +#include "Caches.h" #include "DeviceInfo.h" #include "Outline.h" #include "Rect.h" diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 65bee476f14d..464a58d0c0f8 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -15,6 +15,7 @@ */ #include "ResourceCache.h" +#include "Caches.h" namespace android { @@ -111,9 +112,13 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; if (ref == nullptr) { // If we're not tracking this resource, just delete it - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; + if (Caches::hasInstance()) { + // DEAD CODE + } else { + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + delete[](int8_t*) resource; + } return; } ref->destroyed = true; @@ -130,10 +135,14 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource if (ref->destroyed) { switch (ref->resourceType) { case kNinePatch: { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; + if (Caches::hasInstance()) { + // DEAD CODE + } else { + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + int8_t* patch = (int8_t*)resource; + delete[] patch; + } } break; } } diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp new file mode 100644 index 000000000000..1e90eebe3bb8 --- /dev/null +++ b/libs/hwui/Texture.cpp @@ -0,0 +1,413 @@ +/* + * 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 "Texture.h" +#include "Caches.h" +#include "utils/GLUtils.h" +#include "utils/MathUtils.h" +#include "utils/TraceUtils.h" + +#include + +#include + +#include + +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 8; + default: + LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); + } +} + +void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { + if (force || wrapS != mWrapS || wrapT != mWrapT) { + mWrapS = wrapS; + mWrapT = wrapT; + + if (bindTexture) { + mCaches.textureState().bindTexture(mTarget, mId); + } + + glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); + } +} + +void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { + if (force || min != mMinFilter || mag != mMagFilter) { + mMinFilter = min; + mMagFilter = mag; + + if (bindTexture) { + mCaches.textureState().bindTexture(mTarget, mId); + } + + if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; + + glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); + glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); + } +} + +void Texture::deleteTexture() { + mCaches.textureState().deleteTexture(mId); + mId = 0; + mTarget = GL_NONE; + if (mEglImageHandle != EGL_NO_IMAGE_KHR) { + EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); + eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); + mEglImageHandle = EGL_NO_IMAGE_KHR; + } +} + +bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target) { + if (mWidth == width && mHeight == height && mFormat == format && + mInternalFormat == internalFormat && mTarget == target) { + return false; + } + mWidth = width; + mHeight = height; + mFormat = format; + mInternalFormat = internalFormat; + mTarget = target; + notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); + return true; +} + +void Texture::resetCachedParams() { + mWrapS = GL_REPEAT; + mWrapT = GL_REPEAT; + mMinFilter = GL_NEAREST_MIPMAP_LINEAR; + mMagFilter = GL_LINEAR; +} + +void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, + GLenum type, const void* pixels) { + GL_CHECKPOINT(MODERATE); + + // We don't have color space information, we assume the data is gamma encoded + mIsLinear = false; + + bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D); + if (!mId) { + glGenTextures(1, &mId); + needsAlloc = true; + resetCachedParams(); + } + mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); + if (needsAlloc) { + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); + } else if (pixels) { + glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); + } + GL_CHECKPOINT(MODERATE); +} + +void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { + EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); + if (mEglImageHandle != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); + mEglImageHandle = EGL_NO_IMAGE_KHR; + } + mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + buffer->getNativeBuffer(), 0); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); +} + +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(); + if ((stride == width) || useStride) { + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + } + + if (resize) { + 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); + } + + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + } else { + // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer + // if the stride doesn't match the width + + GLvoid* temp = (GLvoid*)malloc(width * height * bpp); + if (!temp) return; + + uint8_t* pDst = (uint8_t*)temp; + uint8_t* pSrc = (uint8_t*)data; + for (GLsizei i = 0; i < height; i++) { + memcpy(pDst, pSrc, width * bpp); + pDst += width * bpp; + pSrc += stride * bpp; + } + + if (resize) { + 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); + } + + free(temp); + } +} + +void Texture::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: + 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 is upconverted to RGBA_8888 + case kARGB_4444_SkColorType: + case kN32_SkColorType: + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(needSRGB); + *outType = GL_UNSIGNED_BYTE; + break; + case kGray_8_SkColorType: + *outFormat = GL_LUMINANCE; + *outInternalFormat = GL_LUMINANCE; + *outType = GL_UNSIGNED_BYTE; + break; + case kRGBA_F16_SkColorType: + if (caches.extensions().getMajorGlVersion() >= 3) { + // This format is always linear + *outFormat = GL_RGBA; + *outInternalFormat = GL_RGBA16F; + *outType = GL_HALF_FLOAT; + } else { + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(true); + *outType = GL_UNSIGNED_BYTE; + } + break; + default: + LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); + break; + } +} + +SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, + sk_sp sRGB) { + SkBitmap rgbaBitmap; + rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), + bitmap.info().alphaType(), + hasLinearBlending ? sRGB : nullptr)); + rgbaBitmap.eraseColor(0); + + if (bitmap.colorType() == kRGBA_F16_SkColorType) { + // Drawing RGBA_F16 onto ARGB_8888 is not supported + bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), + rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); + } else { + SkCanvas canvas(rgbaBitmap); + canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); + } + + return rgbaBitmap; +} + +bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) { + return info.colorType() == kARGB_4444_SkColorType || + (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && + info.colorSpace()->isSRGB()) || + (info.colorType() == kRGBA_F16_SkColorType && + Caches::getInstance().extensions().getMajorGlVersion() < 3); +} + +void Texture::upload(Bitmap& bitmap) { + ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); + + // We could also enable mipmapping if both bitmap dimensions are powers + // of 2 but we'd have to deal with size changes. Let's keep this simple + const bool canMipMap = mCaches.extensions().hasNPot(); + + // If the texture had mipmap enabled but not anymore, + // force a glTexImage2D to discard the mipmap levels + bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); + bool setDefaultParams = false; + + if (!mId) { + glGenTextures(1, &mId); + needsAlloc = true; + setDefaultParams = true; + } + + bool hasLinearBlending = mCaches.extensions().hasLinearBlending(); + bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace()); + + GLint internalFormat, format, type; + colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending, + &internalFormat, &format, &type); + + // Some devices don't support GL_RGBA16F, so we need to compare the color type + // and internal GL format to decide what to do with 16 bit bitmaps + bool rgba16fNeedsConversion = + bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F; + + // RGBA16F is always linear extended sRGB + if (internalFormat == GL_RGBA16F) { + mIsLinear = true; + } + + mConnector.reset(); + + // Alpha masks don't have color profiles + // If an RGBA16F bitmap needs conversion, we know the target will be sRGB + if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) { + SkColorSpace* colorSpace = bitmap.info().colorSpace(); + // If the bitmap is sRGB we don't need conversion + if (colorSpace != nullptr && !colorSpace->isSRGB()) { + SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); + if (!colorSpace->toXYZD50(&xyzMatrix)) { + ALOGW("Incompatible color space!"); + } else { + SkColorSpaceTransferFn fn; + if (!colorSpace->isNumericalTransferFn(&fn)) { + ALOGW("Incompatible color space, no numerical transfer function!"); + } else { + float data[16]; + xyzMatrix.asColMajorf(data); + + ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC, + fn.fD, fn.fE, fn.fF}; + ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p); + mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB())); + + // A non-sRGB color space might have a transfer function close enough to sRGB + // that we can save shader instructions by using an sRGB sampler + // This is only possible if we have hardware support for sRGB textures + if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() && + !bitmap.isHardware()) { + internalFormat = GL_SRGB8_ALPHA8; + } + } + } + } + } + + GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target); + + blend = !bitmap.isOpaque(); + mCaches.textureState().bindTexture(mTarget, mId); + + // TODO: Handle sRGB gray bitmaps + if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) { + SkBitmap skBitmap; + bitmap.getSkBitmap(&skBitmap); + sk_sp sRGB = SkColorSpace::MakeSRGB(); + SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); + uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), + rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), + rgbaBitmap.getPixels()); + } else if (bitmap.isHardware()) { + uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); + } else { + uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), + bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), + bitmap.pixels()); + } + + if (canMipMap) { + mipMap = bitmap.hasHardwareMipMap(); + if (mipMap) { + glGenerateMipmap(GL_TEXTURE_2D); + } + } + + if (setDefaultParams) { + setFilter(GL_NEAREST); + setWrap(GL_CLAMP_TO_EDGE); + } +} + +void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target) { + mId = id; + mWidth = width; + mHeight = height; + mFormat = format; + mInternalFormat = internalFormat; + mTarget = target; + mConnector.reset(); + // We're wrapping an existing texture, so don't double count this memory + notifySizeChanged(0); +} + +TransferFunctionType Texture::getTransferFunctionType() const { + if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) { + const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters(); + if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) { + if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) && + MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { + if (MathUtils::areEqual(p.g, 1.0f)) { + return TransferFunctionType::None; + } + return TransferFunctionType::Gamma; + } + return TransferFunctionType::Limited; + } + return TransferFunctionType::Full; + } + return TransferFunctionType::None; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h new file mode 100644 index 000000000000..5b7e4e261f30 --- /dev/null +++ b/libs/hwui/Texture.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010 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_TEXTURE_H +#define ANDROID_HWUI_TEXTURE_H + +#include "GpuMemoryTracker.h" +#include "hwui/Bitmap.h" +#include "utils/Color.h" + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +namespace android { + +class GraphicBuffer; + +namespace uirenderer { + +class Caches; +class UvMapper; +class Layer; + +/** + * Represents an OpenGL texture. + */ +class Texture : public GpuMemoryTracker { +public: + static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, + sk_sp sRGB); + static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending); + static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, + bool needSRGB, GLint* outInternalFormat, + GLint* outFormat, GLint* outType); + + explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {} + + virtual ~Texture() {} + + inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { + setWrapST(wrap, wrap, bindTexture, force); + } + + virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, + bool force = false); + + inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { + setFilterMinMag(filter, filter, bindTexture, force); + } + + virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, + bool force = false); + + /** + * Convenience method to call glDeleteTextures() on this texture's id. + */ + void deleteTexture(); + + /** + * Sets the width, height, and format of the texture along with allocating + * the texture ID. Does nothing if the width, height, and format are already + * the requested values. + * + * The image data is undefined after calling this. + */ + void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { + upload(internalFormat, width, height, format, + internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); + } + + /** + * Updates this Texture with the contents of the provided Bitmap, + * also setting the appropriate width, height, and format. It is not necessary + * to call resize() prior to this. + * + * Note this does not set the generation from the Bitmap. + */ + void upload(Bitmap& source); + + /** + * Basically glTexImage2D/glTexSubImage2D. + */ + 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 internalFormat, GLint format, + GLenum target); + + GLuint id() const { return mId; } + + uint32_t width() const { return mWidth; } + + uint32_t height() const { return mHeight; } + + GLint format() const { return mFormat; } + + GLint internalFormat() const { return mInternalFormat; } + + GLenum target() const { return mTarget; } + + /** + * Returns nullptr if this texture does not require color space conversion + * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion + * is required. + */ + constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); } + + constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; } + + TransferFunctionType getTransferFunctionType() const; + + /** + * Returns true if this texture uses a linear encoding format. + */ + constexpr bool isLinear() const { return mIsLinear; } + + /** + * Generation of the backing bitmap, + */ + uint32_t generation = 0; + /** + * Indicates whether the texture requires blending. + */ + bool blend = false; + /** + * Indicates whether this texture should be cleaned up after use. + */ + bool cleanup = false; + /** + * Optional, size of the original bitmap. + */ + uint32_t bitmapSize = 0; + /** + * Indicates whether this texture will use trilinear filtering. + */ + bool mipMap = false; + + /** + * Optional, pointer to a texture coordinates mapper. + */ + const UvMapper* uvMapper = nullptr; + + /** + * Whether or not the Texture is marked in use and thus not evictable for + * the current frame. This is reset at the start of a new frame. + */ + void* isInUse = nullptr; + +private: + // TODO: Temporarily grant private access to GlLayer, remove once + // GlLayer can be de-tangled from being a dual-purpose render target + // and external texture wrapper + friend class GlLayer; + + // Returns true if the texture layout (size, format, etc.) changed, false if it was the same + bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target); + void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); + void resetCachedParams(); + + GLuint mId = 0; + uint32_t mWidth = 0; + uint32_t mHeight = 0; + GLint mFormat = 0; + GLint mInternalFormat = 0; + GLenum mTarget = GL_NONE; + EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR; + + /* See GLES spec section 3.8.14 + * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is + * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR. + * s, t, and r wrap modes are all set to REPEAT." + */ + GLenum mWrapS = GL_REPEAT; + GLenum mWrapT = GL_REPEAT; + GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR; + GLenum mMagFilter = GL_LINEAR; + + // Indicates whether the content of the texture is in linear space + bool mIsLinear = false; + + Caches& mCaches; + + std::unique_ptr mConnector; +}; // struct Texture + +class AutoTexture { +public: + explicit AutoTexture(Texture* texture) : texture(texture) {} + ~AutoTexture() { + if (texture && texture->cleanup) { + texture->deleteTexture(); + delete texture; + } + } + + Texture* const texture; +}; // class AutoTexture + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp new file mode 100644 index 000000000000..30fba7ae7d9b --- /dev/null +++ b/libs/hwui/VkLayer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 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 "VkLayer.h" + +#include "renderstate/RenderState.h" + +#include +#include + +namespace android { +namespace uirenderer { + +void VkLayer::updateTexture() { + sk_sp surface; + SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType); + surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info); + surface->getCanvas()->clear(SK_ColorBLUE); + mImage = surface->makeImageSnapshot(); +} + +void VkLayer::onVkContextDestroyed() { + mImage = nullptr; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h new file mode 100644 index 000000000000..e9664d04b7a5 --- /dev/null +++ b/libs/hwui/VkLayer.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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. + */ + +#pragma once + +#include "Layer.h" + +#include + +namespace android { +namespace uirenderer { +/** + * A layer has dimensions and is backed by a VkImage. + */ +class VkLayer : public Layer { +public: + VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) + : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode) + , mWidth(layerWidth) + , mHeight(layerHeight) + , mBlend(blend) {} + + virtual ~VkLayer() {} + + uint32_t getWidth() const override { return mWidth; } + + uint32_t getHeight() const override { return mHeight; } + + void setSize(uint32_t width, uint32_t height) override { + mWidth = width; + mHeight = height; + } + + void setBlend(bool blend) override { mBlend = blend; } + + bool isBlend() const override { return mBlend; } + + sk_sp getImage() { return mImage; } + + void updateTexture(); + + // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to + // destroy any VkImages that were made with that context. + void onVkContextDestroyed(); + +private: + int mWidth; + int mHeight; + bool mBlend; + + sk_sp mImage; + +}; // struct VkLayer + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index e7d12de44c0c..7a8d026df3b4 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -15,11 +15,11 @@ */ #include "Bitmap.h" +#include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" -#include #include diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index fb66b50f0159..c41f6a6f0ee6 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -15,6 +15,8 @@ */ #include "LayerDrawable.h" +#include "GlLayer.h" +#include "VkLayer.h" #include "GrBackendSurface.h" #include "SkColorFilter.h" @@ -39,14 +41,35 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer return false; } // transform the matrix based on the layer - SkMatrix layerTransform = layer->getTransform(); - sk_sp layerImage = layer->getImage(); + SkMatrix layerTransform; + layer->getTransform().copyTo(layerTransform); + sk_sp layerImage; const int layerWidth = layer->getWidth(); const int layerHeight = layer->getHeight(); + if (layer->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layer); + GrGLTextureInfo externalTexture; + externalTexture.fTarget = glLayer->getRenderTarget(); + externalTexture.fID = glLayer->getTextureId(); + // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't + // expose that info we use it as our default. Further, given that we only use this texture + // as a source this will not impact how Skia uses the texture. The only potential affect + // this is anticipated to have is that for some format types if we are not bound as an OES + // texture we may get invalid results for SKP capture if we read back the texture. + externalTexture.fFormat = GL_RGBA8; + GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture); + layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); + } else { + SkASSERT(layer->getApi() == Layer::Api::Vulkan); + VkLayer* vkLayer = static_cast(layer); + canvas->clear(SK_ColorGREEN); + layerImage = vkLayer->getImage(); + } if (layerImage) { SkMatrix textureMatrixInv; - textureMatrixInv = layer->getTexTransform(); + layer->getTexTransform().copyTo(textureMatrixInv); // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed // use bottom left origin and remove flipV and invert transformations. SkMatrix flipV; @@ -72,9 +95,6 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(layer->getColorSpaceWithFilter()); - if (layer->getForceFilter()) { - paint.setFilterQuality(kLow_SkFilterQuality); - } const bool nonIdentityMatrix = !matrix.isIdentity(); if (nonIdentityMatrix) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 2ae37233098e..78f5a71dee3b 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,6 +17,7 @@ #include "SkiaOpenGLPipeline.h" #include "DeferredLayerUpdater.h" +#include "GlLayer.h" #include "LayerDrawable.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" @@ -186,9 +187,18 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi return false; } +static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, + bool blend) { + GlLayer* layer = + new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); + layer->generateTexture(); + return layer; +} + DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); - return new DeferredLayerUpdater(mRenderThread.renderState()); + return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL); } void SkiaOpenGLPipeline::onStop() { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 5f2eee4523fc..b2519fe59891 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -20,6 +20,7 @@ #include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" +#include "VkLayer.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" @@ -113,10 +114,16 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi return false; } +static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, + bool blend) { + return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); +} + DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { mVkManager.initialize(); - return new DeferredLayerUpdater(mRenderThread.renderState()); + return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan); } void SkiaVulkanPipeline::onStop() {} diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp new file mode 100644 index 000000000000..3a6efb833c47 --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 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 "renderstate/PixelBufferState.h" + +namespace android { +namespace uirenderer { + +PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {} + +bool PixelBufferState::bind(GLuint buffer) { + if (mCurrentPixelBuffer != buffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); + mCurrentPixelBuffer = buffer; + return true; + } + return false; +} + +bool PixelBufferState::unbind() { + if (mCurrentPixelBuffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + mCurrentPixelBuffer = 0; + return true; + } + return false; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h new file mode 100644 index 000000000000..f7ae6c575f6a --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 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 RENDERSTATE_PIXELBUFFERSTATE_H +#define RENDERSTATE_PIXELBUFFERSTATE_H + +#include + +namespace android { +namespace uirenderer { + +class PixelBufferState { + friend class Caches; // TODO: move to RenderState +public: + bool bind(GLuint buffer); + bool unbind(); + +private: + PixelBufferState(); + GLuint mCurrentPixelBuffer; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index b524bcb096da..3be84f588a20 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,6 +16,8 @@ #include "renderstate/RenderState.h" #include #include "DeferredLayerUpdater.h" +#include "GlLayer.h" +#include "VkLayer.h" #include "Snapshot.h" #include "renderthread/CanvasContext.h" @@ -37,11 +39,44 @@ RenderState::RenderState(renderthread::RenderThread& thread) RenderState::~RenderState() { } -void RenderState::onContextCreated() { +void RenderState::onGLContextCreated() { GpuMemoryTracker::onGpuContextCreated(); + + // This is delayed because the first access of Caches makes GL calls + if (!mCaches) { + mCaches = &Caches::createInstance(*this); + } + mCaches->init(); } -void RenderState::onContextDestroyed() { +static void layerLostGlContext(Layer* layer) { + LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, + "layerLostGlContext on non GL layer"); + static_cast(layer)->onGlContextLost(); +} + +void RenderState::onGLContextDestroyed() { + // TODO: reset all cached state in state objects + std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); + + mCaches->terminate(); + + destroyLayersInUpdater(); + GpuMemoryTracker::onGpuContextDestroyed(); +} + +void RenderState::onVkContextCreated() { + GpuMemoryTracker::onGpuContextCreated(); +} + +static void layerDestroyedVkContext(Layer* layer) { + LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan, + "layerLostVkContext on non Vulkan layer"); + static_cast(layer)->onVkContextDestroyed(); +} + +void RenderState::onVkContextDestroyed() { + std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); destroyLayersInUpdater(); GpuMemoryTracker::onGpuContextDestroyed(); } @@ -50,6 +85,10 @@ GrContext* RenderState::getGrContext() const { return mRenderThread.getGrContext(); } +void RenderState::flush(Caches::FlushMode mode) { + if (mCaches) mCaches->flush(mode); +} + void RenderState::onBitmapDestroyed(uint32_t pixelRefId) { // DEAD CODE } @@ -87,6 +126,42 @@ void RenderState::deleteFramebuffer(GLuint fbo) { glDeleteFramebuffers(1, &fbo); } +void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { + if (mode == DrawGlInfo::kModeProcessNoContext) { + // If there's no context we don't need to interrupt as there's + // no gl state to save/restore + (*functor)(mode, info); + } else { + interruptForFunctorInvoke(); + (*functor)(mode, info); + resumeFromFunctorInvoke(); + } +} + +void RenderState::interruptForFunctorInvoke() { + mCaches->textureState().resetActiveTexture(); + debugOverdraw(false, false); + // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) + if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + } +} + +void RenderState::resumeFromFunctorInvoke() { + if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { + glEnable(GL_FRAMEBUFFER_SRGB_EXT); + } + + glViewport(0, 0, mViewportWidth, mViewportHeight); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + debugOverdraw(false, false); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + mCaches->textureState().activateTexture(0); + mCaches->textureState().resetBoundTextures(); +} + void RenderState::debugOverdraw(bool enable, bool clear) { // DEAD CODE } @@ -115,9 +190,5 @@ void RenderState::dump() { // DEAD CODE } -renderthread::RenderThread& RenderState::getRenderThread() { - return mRenderThread; -} - } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index f39aa4b96547..97785a46dcd7 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,6 +16,8 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H +#include "Caches.h" +#include "renderstate/PixelBufferState.h" #include "utils/Macros.h" #include @@ -32,6 +34,7 @@ class GrContext; namespace android { namespace uirenderer { +class Caches; class Layer; class DeferredLayerUpdater; @@ -41,16 +44,22 @@ class CanvasContext; class RenderThread; } +// TODO: Replace Cache's GL state tracking with this. For now it's more a thin // wrapper of Caches for users to migrate to. class RenderState { PREVENT_COPY_AND_ASSIGN(RenderState); friend class renderthread::RenderThread; + friend class Caches; friend class renderthread::CacheManager; public: - void onContextCreated(); - void onContextDestroyed(); + void onGLContextCreated(); + void onGLContextDestroyed(); + void onVkContextCreated(); + void onVkContextDestroyed(); + + void flush(Caches::FlushMode flushMode); void onBitmapDestroyed(uint32_t pixelRefId); void setViewport(GLsizei width, GLsizei height); @@ -61,6 +70,8 @@ public: GLuint createFramebuffer(); void deleteFramebuffer(GLuint fbo); + void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); + void debugOverdraw(bool enable, bool clear); void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } @@ -90,15 +101,16 @@ public: void dump(); - renderthread::RenderThread& getRenderThread(); - private: + void interruptForFunctorInvoke(); + void resumeFromFunctorInvoke(); void destroyLayersInUpdater(); explicit RenderState(renderthread::RenderThread& thread); ~RenderState(); renderthread::RenderThread& mRenderThread; + Caches* mCaches = nullptr; std::set mActiveLayers; std::set mActiveLayerUpdaters; diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp new file mode 100644 index 000000000000..470b4f5de97f --- /dev/null +++ b/libs/hwui/renderstate/TextureState.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 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 "renderstate/TextureState.h" + +#include "Caches.h" +#include "utils/TraceUtils.h" + +#include +#include +#include +#include + +namespace android { +namespace uirenderer { + +// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is +static const int SHADOW_LUT_SIZE = 128; + +// Must define as many texture units as specified by kTextureUnitsCount +const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3}; + +TextureState::TextureState() : mTextureUnit(0) { + glActiveTexture(kTextureUnits[0]); + resetBoundTextures(); + + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, + "At least %d texture units are required!", kTextureUnitsCount); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +} + +TextureState::~TextureState() { + if (mShadowLutTexture != nullptr) { + mShadowLutTexture->deleteTexture(); + } +} + +/** + * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to + * darkness at that spot. Input values of 0->1 should be mapped within the same + * range, but can affect the curve for a different visual falloff. + * + * This is used to populate the shadow LUT texture for quick lookup in the + * shadow shader. + */ +static float computeShadowOpacity(float ratio) { + // exponential falloff function provided by UX + float val = 1 - ratio; + return exp(-val * val * 4.0) - 0.018; +} + +void TextureState::constructTexture(Caches& caches) { + if (mShadowLutTexture == nullptr) { + mShadowLutTexture.reset(new Texture(caches)); + + unsigned char bytes[SHADOW_LUT_SIZE]; + for (int i = 0; i < SHADOW_LUT_SIZE; i++) { + float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f); + bytes[i] = computeShadowOpacity(inputRatio) * 255; + } + mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes); + mShadowLutTexture->setFilter(GL_LINEAR); + mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE); + } +} + +void TextureState::activateTexture(GLuint textureUnit) { + LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, + "Tried to use texture unit index %d, only %d exist", textureUnit, + kTextureUnitsCount); + if (mTextureUnit != textureUnit) { + glActiveTexture(kTextureUnits[textureUnit]); + mTextureUnit = textureUnit; + } +} + +void TextureState::resetActiveTexture() { + mTextureUnit = -1; +} + +void TextureState::bindTexture(GLuint texture) { + if (mBoundTextures[mTextureUnit] != texture) { + glBindTexture(GL_TEXTURE_2D, texture); + mBoundTextures[mTextureUnit] = texture; + } +} + +void TextureState::bindTexture(GLenum target, GLuint texture) { + if (target == GL_TEXTURE_2D) { + bindTexture(texture); + } else { + // GLConsumer directly calls glBindTexture() with + // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target + // since the cached state could be stale + glBindTexture(target, texture); + } +} + +void TextureState::deleteTexture(GLuint texture) { + // When glDeleteTextures() is called on a currently bound texture, + // OpenGL ES specifies that the texture is then considered unbound + // Consider the following series of calls: + // + // glGenTextures -> creates texture name 2 + // glBindTexture(2) + // glDeleteTextures(2) -> 2 is now unbound + // glGenTextures -> can return 2 again + // + // If we don't call glBindTexture(2) after the second glGenTextures + // call, any texture operation will be performed on the default + // texture (name=0) + + unbindTexture(texture); + + glDeleteTextures(1, &texture); +} + +void TextureState::resetBoundTextures() { + for (int i = 0; i < kTextureUnitsCount; i++) { + mBoundTextures[i] = 0; + } +} + +void TextureState::unbindTexture(GLuint texture) { + for (int i = 0; i < kTextureUnitsCount; i++) { + if (mBoundTextures[i] == texture) { + mBoundTextures[i] = 0; + } + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h new file mode 100644 index 000000000000..f1996d431fa2 --- /dev/null +++ b/libs/hwui/renderstate/TextureState.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 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 RENDERSTATE_TEXTURESTATE_H +#define RENDERSTATE_TEXTURESTATE_H + +#include "Texture.h" +#include "Vertex.h" + +#include +#include +#include + +namespace android { +namespace uirenderer { + +class Texture; + +class TextureState { + friend class Caches; // TODO: move to RenderState +public: + void constructTexture(Caches& caches); + + /** + * Activate the specified texture unit. The texture unit must + * be specified using an integer number (0 for GL_TEXTURE0 etc.) + */ + void activateTexture(GLuint textureUnit); + + /** + * Invalidate the cached value of the active texture unit. + */ + void resetActiveTexture(); + + /** + * Binds the specified texture as a GL_TEXTURE_2D texture. + * All texture bindings must be performed with this method or + * bindTexture(GLenum, GLuint). + */ + void bindTexture(GLuint texture); + + /** + * Binds the specified texture with the specified render target. + * All texture bindings must be performed with this method or + * bindTexture(GLuint). + */ + void bindTexture(GLenum target, GLuint texture); + + /** + * Deletes the specified texture and clears it from the cache + * of bound textures. + * All textures must be deleted using this method. + */ + void deleteTexture(GLuint texture); + + /** + * Signals that the cache of bound textures should be cleared. + * Other users of the context may have altered which textures are bound. + */ + void resetBoundTextures(); + + /** + * Clear the cache of bound textures. + */ + void unbindTexture(GLuint texture); + + Texture* getShadowLutTexture() { return mShadowLutTexture.get(); } + +private: + // total number of texture units available for use + static const int kTextureUnitsCount = 4; + + TextureState(); + ~TextureState(); + GLuint mTextureUnit; + + // Caches texture bindings for the GL_TEXTURE_2D target + GLuint mBoundTextures[kTextureUnitsCount]; + + std::unique_ptr mShadowLutTexture; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index c45eedad775c..bec80b1e6011 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -21,7 +21,6 @@ #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" -#include "Properties.h" #include "renderstate/RenderState.h" #include @@ -215,12 +214,11 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat(" Layer Info:\n"); } - const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL - ? "GlLayer" : "VkLayer"; size_t layerMemoryTotal = 0; for (std::set::iterator it = renderState->mActiveLayers.begin(); it != renderState->mActiveLayers.end(); it++) { const Layer* layer = *it; + const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8b07d1dadeb6..5d7252304bf2 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,6 +18,7 @@ #include #include "AnimationContext.h" +#include "Caches.h" #include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" @@ -494,6 +495,13 @@ void CanvasContext::draw() { } GpuMemoryTracker::onFrameCompleted(); +#ifdef BUGREPORT_FONT_CACHE_USAGE + auto renderType = Properties::getRenderPipelineType(); + if (RenderPipelineType::OpenGL == renderType) { + Caches& caches = Caches::getInstance(); + caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); + } +#endif } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 5f8d7ad3373a..cd21822df5b1 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include "utils/StringUtils.h" @@ -465,109 +464,6 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } -status_t EglManager::fenceWait(sp& fence) { - if (!hasEglContext()) { - ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); - return INVALID_OPERATION; - } - - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { - // Block GPU on the fence. - // Create an EGLSyncKHR from the current fence. - int fenceFd = fence->dup(); - if (fenceFd == -1) { - ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - EGLint attribs[] = { - EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, - EGL_NONE - }; - EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, - EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - close(fenceFd); - ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - - // XXX: The spec draft is inconsistent as to whether this should - // return an EGLint or void. Ignore the return value for now, as - // it's not strictly needed. - eglWaitSyncKHR(mEglDisplay, sync, 0); - EGLint eglErr = eglGetError(); - eglDestroySyncKHR(mEglDisplay, sync); - if (eglErr != EGL_SUCCESS) { - ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr); - return UNKNOWN_ERROR; - } - } else { - // Block CPU on the fence. - status_t err = fence->waitForever("EglManager::fenceWait"); - if (err != NO_ERROR) { - ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); - return err; - } - } - return OK; -} - -status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, - sp& nativeFence) { - if (!hasEglContext()) { - ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); - return INVALID_OPERATION; - } - - if (SyncFeatures::getInstance().useNativeFenceSync()) { - EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, - EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync); - eglDestroySyncKHR(mEglDisplay, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - ALOGE("EglManager::createReleaseFence: error dup'ing native fence " - "fd: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - nativeFence = new Fence(fenceFd); - *eglFence = EGL_NO_SYNC_KHR; - } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { - if (*eglFence != EGL_NO_SYNC_KHR) { - // There is already a fence for the current slot. We need to - // wait on that before replacing it with another fence to - // ensure that all outstanding buffer accesses have completed - // before the producer accesses it. - EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000); - if (result == EGL_FALSE) { - ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); - return TIMED_OUT; - } - eglDestroySyncKHR(mEglDisplay, *eglFence); - } - - // Create a fence for the outstanding accesses in the current - // OpenGL ES context. - *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr); - if (*eglFence == EGL_NO_SYNC_KHR) { - ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - } - return OK; -} - } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 507673adf26e..8e8bb8b68a1c 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -17,10 +17,8 @@ #define EGLMANAGER_H #include -#include #include #include -#include #include #include @@ -68,14 +66,6 @@ public: EGLDisplay eglDisplay() const { return mEglDisplay; } - // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension - // support is missing, block the CPU on the fence. - status_t fenceWait(sp& fence); - - // Creates a fence that is signaled, when all the pending GL commands are flushed. - // Depending on installed extensions, the result is either Android native fence or EGL fence. - status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp& nativeFence); - private: void initExtensions(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 51be54ced5ec..3eaf43b65f69 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -178,7 +178,7 @@ void RenderThread::requireGlContext() { return; } mEglManager->initialize(); - renderState().onContextCreated(); + renderState().onGLContextCreated(); #ifdef HWUI_GLES_WRAP_ENABLED debug::GlesDriver* driver = debug::GlesDriver::get(); @@ -199,7 +199,7 @@ void RenderThread::requireGlContext() { void RenderThread::destroyGlContext() { if (mEglManager->hasEglContext()) { setGrContext(nullptr); - renderState().onContextDestroyed(); + renderState().onGLContextDestroyed(); mEglManager->destroy(); } } diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 67b1972e874f..a19edae26cb3 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -38,7 +38,7 @@ namespace renderthread { VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { - mRenderThread.renderState().onContextDestroyed(); + mRenderThread.renderState().onVkContextDestroyed(); mRenderThread.setGrContext(nullptr); if (VK_NULL_HANDLE != mCommandPool) { @@ -367,7 +367,7 @@ void VulkanManager::initialize() { mSwapBehavior = SwapBehavior::BufferAge; } - mRenderThread.renderState().onContextCreated(); + mRenderThread.renderState().onVkContextCreated(); } // Returns the next BackbufferInfo to use for the next draw. The function will make sure all @@ -944,22 +944,6 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } -status_t VulkanManager::fenceWait(sp& fence) { - //TODO: Insert a wait on fence command into the Vulkan command buffer. - // Block CPU on the fence. - status_t err = fence->waitForever("VulkanManager::fenceWait"); - if (err != NO_ERROR) { - ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); - return err; - } - return OK; -} - -status_t VulkanManager::createReleaseFence(sp& nativeFence) { - //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. - return OK; -} - } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 7051ee686659..69641d57ccbb 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -23,8 +23,6 @@ #include #include -#include -#include #include namespace android { @@ -110,12 +108,6 @@ public: // Presents the current VkImage. void swapBuffers(VulkanSurface* surface); - // Inserts a wait on fence command into the Vulkan command buffer. - status_t fenceWait(sp& fence); - - // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. - status_t createReleaseFence(sp& nativeFence); - private: friend class RenderThread; diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp deleted file mode 100644 index c8220c6cb0d4..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.cpp +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (C) 2018 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include "EGLConsumer.h" -#include "SurfaceTexture.h" - -#include -#include -#include - -#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" -#define EGL_PROTECTED_CONTENT_EXT 0x32C0 - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -static const struct { - uint32_t width, height; - char const* bits; -} kDebugData = {15, 12, - "_______________" - "_______________" - "_____XX_XX_____" - "__X_X_____X_X__" - "__X_XXXXXXX_X__" - "__XXXXXXXXXXX__" - "___XX_XXX_XX___" - "____XXXXXXX____" - "_____X___X_____" - "____X_____X____" - "_______________" - "_______________"}; - -Mutex EGLConsumer::sStaticInitLock; -sp EGLConsumer::sReleasedTexImageBuffer; - -static bool hasEglProtectedContentImpl() { - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); - bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); - bool atEnd = (cropExtLen + 1) < extsLen && - !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); - bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); - return equal || atStart || atEnd || inMiddle; -} - -static bool hasEglProtectedContent() { - // Only compute whether the extension is present once the first time this - // function is called. - static bool hasIt = hasEglProtectedContentImpl(); - return hasIt; -} - -EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} - -status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - return err; - } - - BufferItem item; - - // Acquire the next buffer. - // In asynchronous mode the list is guaranteed to be one buffer - // deep, while in synchronous mode we use the oldest buffer. - err = st.acquireBufferLocked(&item, 0); - if (err != NO_ERROR) { - if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - // We always bind the texture even if we don't update its contents. - EGC_LOGV("updateTexImage: no buffers were available"); - glBindTexture(st.mTexTarget, st.mTexName); - err = NO_ERROR; - } else { - EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); - } - return err; - } - - // Release the previous buffer. - err = updateAndReleaseLocked(item, nullptr, st); - if (err != NO_ERROR) { - // We always bind the texture. - glBindTexture(st.mTexTarget, st.mTexName); - return err; - } - - // Bind the new buffer to the GL texture, and wait until it's ready. - return bindTextureImageLocked(st); -} - -status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = NO_ERROR; - - // if we're detached, no need to validate EGL's state -- we won't use it. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - err = checkAndUpdateEglStateLocked(st, true); - if (err != NO_ERROR) { - return err; - } - } - - // Update the EGLConsumer state. - int buf = st.mCurrentTexture; - if (buf != BufferQueue::INVALID_BUFFER_SLOT) { - EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); - - // if we're detached, we just use the fence that was created in detachFromContext() - // so... basically, nothing more to do here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // Do whatever sync ops we need to do before releasing the slot. - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); - return err; - } - } - - err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - if (err < NO_ERROR) { - EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); - return err; - } - - if (mReleasedTexImage == nullptr) { - mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); - } - - st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - mCurrentTextureImage = mReleasedTexImage; - st.mCurrentCrop.makeInvalid(); - st.mCurrentTransform = 0; - st.mCurrentTimestamp = 0; - st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; - st.mCurrentFence = Fence::NO_FENCE; - st.mCurrentFenceTime = FenceTime::NO_FENCE; - - // detached, don't touch the texture (and we may not even have an - // EGLDisplay here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // This binds a dummy buffer (mReleasedTexImage). - status_t result = bindTextureImageLocked(st); - if (result != NO_ERROR) { - return result; - } - } - } - - return NO_ERROR; -} - -sp EGLConsumer::getDebugTexImageBuffer() { - Mutex::Autolock _l(sStaticInitLock); - if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { - // The first time, create the debug texture in case the application - // continues to use it. - sp buffer = new GraphicBuffer( - kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); - uint32_t* bits; - buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast(&bits)); - uint32_t stride = buffer->getStride(); - uint32_t height = buffer->getHeight(); - memset(bits, 0, stride * height * 4); - for (uint32_t y = 0; y < kDebugData.height; y++) { - for (uint32_t x = 0; x < kDebugData.width; x++) { - bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 - : 0xFFFFFFFF; - } - bits += stride; - } - buffer->unlock(); - sReleasedTexImageBuffer = buffer; - } - return sReleasedTexImageBuffer; -} - -void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior EglImage created is using a stale buffer. This - // replaces any old EglImage with a new one (using the new buffer). - int slot = item->mSlot; - if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } -} - -void EGLConsumer::onReleaseBufferLocked(int buf) { - mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; -} - -status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st) { - status_t err = NO_ERROR; - - int slot = item.mSlot; - - if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { - EGC_LOGE( - "updateAndRelease: EGLConsumer is not attached to an OpenGL " - "ES context"); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return INVALID_OPERATION; - } - - // Confirm state. - err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return err; - } - - // Ensure we have a valid EglImageKHR for the slot, creating an EglImage - // if nessessary, for the gralloc buffer currently in the slot in - // ConsumerBase. - // We may have to do this even when item.mGraphicBuffer == NULL (which - // means the buffer was previously acquired). - err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, - slot); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return UNKNOWN_ERROR; - } - - // Do whatever sync ops we need to do before releasing the old slot. - if (slot != st.mCurrentTexture) { - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - // Release the buffer we just acquired. It's not safe to - // release the old buffer, so instead we just drop the new frame. - // As we are still under lock since acquireBuffer, it is safe to - // release by slot. - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - return err; - } - } - - EGC_LOGV( - "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, - mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, - slot, st.mSlots[slot].mGraphicBuffer->handle); - - // Hang onto the pointer so that it isn't freed in the call to - // releaseBufferLocked() if we're in shared buffer mode and both buffers are - // the same. - sp nextTextureImage = mEglSlots[slot].mEglImage; - - // release old buffer - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (pendingRelease == nullptr) { - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, - mEglSlots[st.mCurrentTexture].mEglFence); - if (status < NO_ERROR) { - EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), - status); - err = status; - // keep going, with error raised [?] - } - } else { - pendingRelease->currentTexture = st.mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; - pendingRelease->isPending = true; - } - } - - // Update the EGLConsumer state. - st.mCurrentTexture = slot; - mCurrentTextureImage = nextTextureImage; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - - st.computeCurrentTransformMatrixLocked(); - - return err; -} - -status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { - if (mEglDisplay == EGL_NO_DISPLAY) { - ALOGE("bindTextureImage: invalid display"); - return INVALID_OPERATION; - } - - GLenum error; - while ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); - } - - glBindTexture(st.mTexTarget, st.mTexName); - if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { - EGC_LOGE("bindTextureImage: no currently-bound texture"); - return NO_INIT; - } - - status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - - // In the rare case that the display is terminated and then initialized - // again, we can't detect that the display changed (it didn't), but the - // image is invalid. In this case, repeat the exact same steps while - // forcing the creation of a new image. - if ((error = glGetError()) != GL_NO_ERROR) { - glBindTexture(st.mTexTarget, st.mTexName); - status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); - if (result != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - if ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); - return UNKNOWN_ERROR; - } - } - - // Wait for the new buffer to be ready. - return doGLFenceWaitLocked(st); -} - -status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (!contextCheck) { - // if this is the first time we're called, mEglDisplay/mEglContext have - // never been set, so don't error out (below). - if (mEglDisplay == EGL_NO_DISPLAY) { - mEglDisplay = dpy; - } - if (mEglContext == EGL_NO_CONTEXT) { - mEglContext = ctx; - } - } - - if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); - return INVALID_OPERATION; - } - - mEglDisplay = dpy; - mEglContext = ctx; - return NO_ERROR; -} - -status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { - EGC_LOGE("detachFromContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { - EGC_LOGE("detachFromContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { - status_t err = syncForReleaseLocked(dpy, st); - if (err != OK) { - return err; - } - - glDeleteTextures(1, &st.mTexName); - } - - mEglDisplay = EGL_NO_DISPLAY; - mEglContext = EGL_NO_CONTEXT; - - return OK; -} - -status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { - // Initialize mCurrentTextureImage if there is a current buffer from past attached state. - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - if (!mEglSlots[slot].mEglImage.get()) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } - mCurrentTextureImage = mEglSlots[slot].mEglImage; - } - - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (dpy == EGL_NO_DISPLAY) { - EGC_LOGE("attachToContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (ctx == EGL_NO_CONTEXT) { - EGC_LOGE("attachToContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - // We need to bind the texture regardless of whether there's a current - // buffer. - glBindTexture(st.mTexTarget, GLuint(tex)); - - mEglDisplay = dpy; - mEglContext = ctx; - st.mTexName = tex; - st.mOpMode = SurfaceTexture::OpMode::attachedToGL; - - if (mCurrentTextureImage != nullptr) { - // This may wait for a buffer a second time. This is likely required if - // this is a different context, since otherwise the wait could be skipped - // by bouncing through another context. For the same context the extra - // wait is redundant. - status_t err = bindTextureImageLocked(st); - if (err != NO_ERROR) { - return err; - } - } - - return OK; -} - -status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { - EGC_LOGV("syncForReleaseLocked"); - - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (SyncFeatures::getInstance().useNativeFenceSync()) { - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); - eglDestroySyncKHR(dpy, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - EGC_LOGE( - "syncForReleaseLocked: error dup'ing native fence " - "fd: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - sp fence(new Fence(fenceFd)); - status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, - mCurrentTextureImage->graphicBuffer(), fence); - if (err != OK) { - EGC_LOGE( - "syncForReleaseLocked: error adding release fence: " - "%s (%d)", - strerror(-err), err); - return err; - } - } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { - EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; - if (fence != EGL_NO_SYNC_KHR) { - // There is already a fence for the current slot. We need to - // wait on that before replacing it with another fence to - // ensure that all outstanding buffer accesses have completed - // before the producer accesses it. - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - if (result == EGL_FALSE) { - EGC_LOGE( - "syncForReleaseLocked: error waiting for previous " - "fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - EGC_LOGE( - "syncForReleaseLocked: timeout waiting for previous " - "fence"); - return TIMED_OUT; - } - eglDestroySyncKHR(dpy, fence); - } - - // Create a fence for the outstanding accesses in the current - // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); - if (fence == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - mEglSlots[st.mCurrentTexture].mEglFence = fence; - } - } - - return OK; -} - -status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { - EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { - EGC_LOGE("doGLFenceWait: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (st.mCurrentFence->isValid()) { - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { - // Create an EGLSyncKHR from the current fence. - int fenceFd = st.mCurrentFence->dup(); - if (fenceFd == -1) { - EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - close(fenceFd); - EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - - // XXX: The spec draft is inconsistent as to whether this should - // return an EGLint or void. Ignore the return value for now, as - // it's not strictly needed. - eglWaitSyncKHR(dpy, sync, 0); - EGLint eglErr = eglGetError(); - eglDestroySyncKHR(dpy, sync); - if (eglErr != EGL_SUCCESS) { - EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); - return UNKNOWN_ERROR; - } - } else { - status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); - if (err != NO_ERROR) { - EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); - return err; - } - } - } - - return NO_ERROR; -} - -void EGLConsumer::onFreeBufferLocked(int slotIndex) { - mEglSlots[slotIndex].mEglImage.clear(); -} - -void EGLConsumer::onAbandonLocked() { - mCurrentTextureImage.clear(); -} - -EGLConsumer::EglImage::EglImage(sp graphicBuffer) - : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} - -EGLConsumer::EglImage::~EglImage() { - if (mEglImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("~EglImage: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - } -} - -status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { - // If there's an image and it's no longer valid, destroy it. - bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; - bool displayInvalid = mEglDisplay != eglDisplay; - if (haveImage && (displayInvalid || forceCreation)) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("createIfNeeded: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - mEglImage = EGL_NO_IMAGE_KHR; - mEglDisplay = EGL_NO_DISPLAY; - } - - // If there's no image, create one. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = eglDisplay; - mEglImage = createImage(mEglDisplay, mGraphicBuffer); - } - - // Fail if we can't create a valid image. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = EGL_NO_DISPLAY; - const sp& buffer = mGraphicBuffer; - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); - return UNKNOWN_ERROR; - } - - return OK; -} - -void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { - glEGLImageTargetTexture2DOES(texTarget, static_cast(mEglImage)); -} - -EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp& graphicBuffer) { - EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); - const bool createProtectedImage = - (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); - EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, - EGL_TRUE, - createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, - createProtectedImage ? EGL_TRUE : EGL_NONE, - EGL_NONE, - }; - eglInitialize(dpy, nullptr, nullptr); - EGLImageKHR image = - eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); - if (image == EGL_NO_IMAGE_KHR) { - EGLint error = eglGetError(); - ALOGE("error creating EGLImage: %#x", error); - eglTerminate(dpy); - } - return image; -} - -}; // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h deleted file mode 100644 index eccb08298f6f..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include - -#include -#include -#include - -namespace android { - -class SurfaceTexture; - -/* - * EGLConsumer implements the parts of SurfaceTexture that deal with - * textures attached to an GL context. - */ -class EGLConsumer { -public: - EGLConsumer(); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(SurfaceTexture& st); - - /* - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(SurfaceTexture& st); - - /** - * detachFromContext detaches the EGLConsumer from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a EGLConsumer from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a EGLConsumer has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the EGLConsumer is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(SurfaceTexture& st); - - /** - * attachToContext attaches a EGLConsumer that is currently in the - * 'detached' state to the current OpenGL ES context. A EGLConsumer is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * EGLConsumer that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex, SurfaceTexture& st); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - - /** - * onAbandonLocked amends the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - void onAbandonLocked(); - -protected: - struct PendingRelease { - PendingRelease() - : isPending(false) - , currentTexture(-1) - , graphicBuffer() - , display(nullptr) - , fence(nullptr) {} - - bool isPending; - int currentTexture; - sp graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; - }; - - /** - * This releases the buffer in the slot referenced by mCurrentTexture, - * then updates state to refer to the BufferItem, which must be a - * newly-acquired buffer. If pendingRelease is not null, the parameters - * which would have been passed to releaseBufferLocked upon the successful - * completion of the method will instead be returned to the caller, so that - * it may call releaseBufferLocked itself later. - */ - status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st); - - /** - * Binds mTexName and the current buffer to mTexTarget. Uses - * mCurrentTexture if it's set, mCurrentTextureImage if not. If the - * bind succeeds, this calls doGLFenceWait. - */ - status_t bindTextureImageLocked(SurfaceTexture& st); - - /** - * Gets the current EGLDisplay and EGLContext values, and compares them - * to mEglDisplay and mEglContext. If the fields have been previously - * set, the values must match; if not, the fields are set to the current - * values. - * The contextCheck argument is used to ensure that a GL context is - * properly set; when set to false, the check is not performed. - */ - status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); - - /** - * EglImage is a utility class for tracking and creating EGLImageKHRs. There - * is primarily just one image per slot, but there is also special cases: - * - For releaseTexImage, we use a debug image (mReleasedTexImage) - * - After freeBuffer, we must still keep the current image/buffer - * Reference counting EGLImages lets us handle all these cases easily while - * also only creating new EGLImages from buffers when required. - */ - class EglImage : public LightRefBase { - public: - EglImage(sp graphicBuffer); - - /** - * createIfNeeded creates an EGLImage if required (we haven't created - * one yet, or the EGLDisplay or crop-rect has changed). - */ - status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); - - /** - * This calls glEGLImageTargetTexture2DOES to bind the image to the - * texture in the specified texture target. - */ - void bindToTextureTarget(uint32_t texTarget); - - const sp& graphicBuffer() { return mGraphicBuffer; } - const native_handle* graphicBufferHandle() { - return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; - } - - private: - // Only allow instantiation using ref counting. - friend class LightRefBase; - virtual ~EglImage(); - - // createImage creates a new EGLImage from a GraphicBuffer. - EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer); - - // Disallow copying - EglImage(const EglImage& rhs); - void operator=(const EglImage& rhs); - - // mGraphicBuffer is the buffer that was used to create this image. - sp mGraphicBuffer; - - // mEglImage is the EGLImage created from mGraphicBuffer. - EGLImageKHR mEglImage; - - // mEGLDisplay is the EGLDisplay that was used to create mEglImage. - EGLDisplay mEglDisplay; - - // mCropRect is the crop rectangle passed to EGL when mEglImage - // was created. - Rect mCropRect; - }; - - /** - * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command - * stream to ensure that it is safe for future OpenGL ES commands to - * access the current texture buffer. - */ - status_t doGLFenceWaitLocked(SurfaceTexture& st) const; - - /** - * syncForReleaseLocked performs the synchronization needed to release the - * current slot from an OpenGL ES context. If needed it will set the - * current slot's fence to guard against a producer accessing the buffer - * before the outstanding accesses have completed. - */ - status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); - - /** - * returns a graphic buffer used when the texture image has been released - */ - static sp getDebugTexImageBuffer(); - - /** - * The default consumer usage flags that EGLConsumer always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the EGLConsumer user. In particular, EGLConsumer will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentTextureImage is the EglImage/buffer of the current texture. It's - * possible that this buffer is not associated with any buffer slot, so we - * must track it separately in order to support the getCurrentBuffer method. - */ - sp mCurrentTextureImage; - - /** - * EGLSlot contains the information and object references that - * EGLConsumer maintains about a BufferQueue buffer slot. - */ - struct EglSlot { - EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - - /** - * mEglImage is the EGLImage created from mGraphicBuffer. - */ - sp mEglImage; - - /** - * mFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. It is initialized - * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based - * on a compile-time option) set to a new sync object in updateTexImage. - */ - EGLSyncKHR mEglFence; - }; - - /** - * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently - * associated. It is intialized to EGL_NO_DISPLAY and gets set to the - * current display when updateTexImage is called for the first time and when - * attachToContext is called. - */ - EGLDisplay mEglDisplay; - - /** - * mEglContext is the OpenGL ES context with which this EGLConsumer is - * currently associated. It is initialized to EGL_NO_CONTEXT and gets set - * to the current GL context when updateTexImage is called for the first - * time and when attachToContext is called. - */ - EGLContext mEglContext; - - /** - * mEGLSlots stores the buffers that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; - - /** - * protects static initialization - */ - static Mutex sStaticInitLock; - - /** - * mReleasedTexImageBuffer is a dummy buffer used when in single buffer - * mode and releaseTexImage() has been called - */ - static sp sReleasedTexImageBuffer; - sp mReleasedTexImage; -}; - -}; // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp deleted file mode 100644 index c86cd962ebed..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2018 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 "ImageConsumer.h" -#include -#include "Properties.h" -#include "SurfaceTexture.h" -#include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" -#include "renderthread/VulkanManager.h" - -// Macro for including the SurfaceTexture name in log messages -#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -namespace android { - -void ImageConsumer::onFreeBufferLocked(int slotIndex) { - mImageSlots[slotIndex].mImage.reset(); -} - -void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. - if (item->mGraphicBuffer != nullptr) { - mImageSlots[item->mSlot].mImage.reset(); - } -} - -void ImageConsumer::onReleaseBufferLocked(int buf) { - mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR; -} - -void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer) { - if (!mImage.get()) { - mImage = graphicBuffer.get() - ? SkImage::MakeFromAHardwareBuffer( - reinterpret_cast(graphicBuffer.get()), - kPremul_SkAlphaType, SkColorSpace::MakeSRGB()) - : nullptr; - } -} - -sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, - uirenderer::RenderState& renderState) { - BufferItem item; - status_t err; - err = st.acquireBufferLocked(&item, 0); - if (err != OK) { - if (err != BufferQueue::NO_BUFFER_AVAILABLE) { - IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); - } else { - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - *queueEmpty = true; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); - return mImageSlots[slot].mImage; - } - } - return nullptr; - } - - int slot = item.mSlot; - if (item.mFence->isValid()) { - // Wait on the producer fence for the buffer to be ready. - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); - } else { - err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence); - } - if (err != OK) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Release old buffer. - if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { - // If needed, set the released slot's fence to guard against a producer accessing the - // buffer before the outstanding accesses have completed. - sp releaseFence; - EGLDisplay display = EGL_NO_DISPLAY; - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - auto& eglManager = renderState.getRenderThread().eglManager(); - display = eglManager.eglDisplay(); - err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence, - releaseFence); - } else { - err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence); - } - if (OK != err) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - - if (releaseFence.get()) { - status_t err = st.addReleaseFenceLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); - if (err != OK) { - IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Finally release the old buffer. - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, - mImageSlots[st.mCurrentTexture].mEglFence); - if (status < NO_ERROR) { - IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); - err = status; - // Keep going, with error raised. - } - } - - // Update the state. - st.mCurrentTexture = slot; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - st.computeCurrentTransformMatrixLocked(); - - *queueEmpty = false; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); - return mImageSlots[slot].mImage; -} - -} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h deleted file mode 100644 index 31ee8db52874..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include - -#include -#include -#include -#include - -namespace android { - -namespace uirenderer { -class RenderState; -} - -class SurfaceTexture; - -/* - * ImageConsumer implements the parts of SurfaceTexture that deal with - * images consumed by HWUI view system. - */ -class ImageConsumer { -public: - sk_sp dequeueImage(bool* queueEmpty, SurfaceTexture& cb, - uirenderer::RenderState& renderState); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the SkImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - -private: - /** - * ImageSlot contains the information and object references that - * ImageConsumer maintains about a BufferQueue buffer slot. - */ - struct ImageSlot { - ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - - // mImage is the SkImage created from mGraphicBuffer. - sk_sp mImage; - - /** - * mEglFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. - */ - EGLSyncKHR mEglFence; - - void createIfNeeded(sp graphicBuffer); - }; - - /** - * ImageConsumer stores the SkImages that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; -}; - -}; /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp deleted file mode 100644 index 4bff715822e8..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.cpp +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2018 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 -#include -#include -#include - -#include - -#include "Matrix.h" -#include "SurfaceTexture.h" - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) - -static const mat4 mtxIdentity; - -SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t tex, - uint32_t texTarget, bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(tex) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::attachedToGL) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t texTarget, - bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(0) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::detached) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); - return NO_INIT; - } - mDefaultWidth = w; - mDefaultHeight = h; - return mConsumer->setDefaultBufferSize(w, h); -} - -status_t SurfaceTexture::updateTexImage() { - ATRACE_CALL(); - SFT_LOGV("updateTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.updateTexImage(*this); -} - -status_t SurfaceTexture::releaseTexImage() { - // releaseTexImage can be invoked even when not attached to a GL context. - ATRACE_CALL(); - SFT_LOGV("releaseTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.releaseTexImage(*this); -} - -status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber) { - status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); - if (err != NO_ERROR) { - return err; - } - - switch (mOpMode) { - case OpMode::attachedToView: - mImageConsumer.onAcquireBufferLocked(item); - break; - case OpMode::attachedToGL: - mEGLConsumer.onAcquireBufferLocked(item, *this); - break; - case OpMode::detached: - break; - } - - return NO_ERROR; -} - -status_t SurfaceTexture::releaseBufferLocked(int buf, sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) { - // release the buffer if it hasn't already been discarded by the - // BufferQueue. This can happen, for example, when the producer of this - // buffer has reallocated the original buffer slot after this buffer - // was acquired. - status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); - // We could be releasing an EGL buffer, even if not currently attached to a GL context. - mImageConsumer.onReleaseBufferLocked(buf); - mEGLConsumer.onReleaseBufferLocked(buf); - return err; -} - -status_t SurfaceTexture::detachFromContext() { - ATRACE_CALL(); - SFT_LOGV("detachFromContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::attachedToGL) { - SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); - return INVALID_OPERATION; - } - - status_t err = mEGLConsumer.detachFromContext(*this); - if (err == OK) { - mOpMode = OpMode::detached; - } - - return err; -} - -status_t SurfaceTexture::attachToContext(uint32_t tex) { - ATRACE_CALL(); - SFT_LOGV("attachToContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("attachToContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::detached) { - SFT_LOGE( - "attachToContext: SurfaceTexture is already attached to a " - "context"); - return INVALID_OPERATION; - } - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible ImageConsumer cache - mImageConsumer.onFreeBufferLocked(mCurrentTexture); - } - - return mEGLConsumer.attachToContext(tex, *this); -} - -void SurfaceTexture::attachToView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - if (mAbandoned) { - SFT_LOGE("attachToView: abandoned SurfaceTexture"); - return; - } - if (mOpMode == OpMode::detached) { - mOpMode = OpMode::attachedToView; - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible EGLConsumer texture cache - mEGLConsumer.onFreeBufferLocked(mCurrentTexture); - mEGLConsumer.onAbandonLocked(); - } - } else { - SFT_LOGE("attachToView: already attached"); - } -} - -void SurfaceTexture::detachFromView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromView: abandoned SurfaceTexture"); - return; - } - - if (mOpMode == OpMode::attachedToView) { - mOpMode = OpMode::detached; - } else { - SFT_LOGE("detachFromView: not attached to View"); - } -} - -uint32_t SurfaceTexture::getCurrentTextureTarget() const { - return mTexTarget; -} - -void SurfaceTexture::getTransformMatrix(float mtx[16]) { - Mutex::Autolock lock(mMutex); - memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); -} - -void SurfaceTexture::setFilteringEnabled(bool enabled) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); - return; - } - bool needsRecompute = mFilteringEnabled != enabled; - mFilteringEnabled = enabled; - - if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { - SFT_LOGD("setFilteringEnabled called with no current item"); - } - - if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - computeCurrentTransformMatrixLocked(); - } -} - -void SurfaceTexture::computeCurrentTransformMatrixLocked() { - SFT_LOGV("computeCurrentTransformMatrixLocked"); - sp buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) - ? nullptr - : mSlots[mCurrentTexture].mGraphicBuffer; - if (buf == nullptr) { - SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); - } - computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, - mFilteringEnabled); -} - -void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp& buf, - const Rect& cropRect, uint32_t transform, - bool filtering) { - // Transform matrices - static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); - static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - - mat4 xform; - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { - xform *= mtxFlipH; - } - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { - xform *= mtxFlipV; - } - if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - xform *= mtxRot90; - } - - if (!cropRect.isEmpty() && buf.get()) { - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - float shrinkAmount = 0.0f; - if (filtering) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGBA_FP16: - case PIXEL_FORMAT_RGBA_1010102: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - // We know there's no subsampling of any channels, so we - // only need to shrink by a half a pixel. - shrinkAmount = 0.5; - break; - - default: - // If we don't recognize the format, we must assume the - // worst case (that we care about), which is YUV420. - shrinkAmount = 1.0; - break; - } - } - - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; - } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; - } - - mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); - xform = crop * xform; - } - - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so SurfaceTexture must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. - xform = mtxFlipV * xform; - - memcpy(outTransform, xform.asArray(), sizeof(xform)); -} - -Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { - Rect outCrop = crop; - - uint32_t newWidth = static_cast(crop.width()); - uint32_t newHeight = static_cast(crop.height()); - - if (newWidth * bufferHeight > newHeight * bufferWidth) { - newWidth = newHeight * bufferWidth / bufferHeight; - ALOGV("too wide: newWidth = %d", newWidth); - } else if (newWidth * bufferHeight < newHeight * bufferWidth) { - newHeight = newWidth * bufferHeight / bufferWidth; - ALOGV("too tall: newHeight = %d", newHeight); - } - - uint32_t currentWidth = static_cast(crop.width()); - uint32_t currentHeight = static_cast(crop.height()); - - // The crop is too wide - if (newWidth < currentWidth) { - uint32_t dw = currentWidth - newWidth; - auto halfdw = dw / 2; - outCrop.left += halfdw; - // Not halfdw because it would subtract 1 too few when dw is odd - outCrop.right -= (dw - halfdw); - // The crop is too tall - } else if (newHeight < currentHeight) { - uint32_t dh = currentHeight - newHeight; - auto halfdh = dh / 2; - outCrop.top += halfdh; - // Not halfdh because it would subtract 1 too few when dh is odd - outCrop.bottom -= (dh - halfdh); - } - - ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, - outCrop.bottom); - - return outCrop; -} - -nsecs_t SurfaceTexture::getTimestamp() { - SFT_LOGV("getTimestamp"); - Mutex::Autolock lock(mMutex); - return mCurrentTimestamp; -} - -android_dataspace SurfaceTexture::getCurrentDataSpace() { - SFT_LOGV("getCurrentDataSpace"); - Mutex::Autolock lock(mMutex); - return mCurrentDataSpace; -} - -uint64_t SurfaceTexture::getFrameNumber() { - SFT_LOGV("getFrameNumber"); - Mutex::Autolock lock(mMutex); - return mCurrentFrameNumber; -} - -Rect SurfaceTexture::getCurrentCrop() const { - Mutex::Autolock lock(mMutex); - return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) - ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) - : mCurrentCrop; -} - -uint32_t SurfaceTexture::getCurrentTransform() const { - Mutex::Autolock lock(mMutex); - return mCurrentTransform; -} - -uint32_t SurfaceTexture::getCurrentScalingMode() const { - Mutex::Autolock lock(mMutex); - return mCurrentScalingMode; -} - -sp SurfaceTexture::getCurrentFence() const { - Mutex::Autolock lock(mMutex); - return mCurrentFence; -} - -std::shared_ptr SurfaceTexture::getCurrentFenceTime() const { - Mutex::Autolock lock(mMutex); - return mCurrentFenceTime; -} - -void SurfaceTexture::freeBufferLocked(int slotIndex) { - SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - if (slotIndex == mCurrentTexture) { - mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - } - // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. - // Buffers can be freed after SurfaceTexture has detached from GL context or View. - mImageConsumer.onFreeBufferLocked(slotIndex); - mEGLConsumer.onFreeBufferLocked(slotIndex); - ConsumerBase::freeBufferLocked(slotIndex); -} - -void SurfaceTexture::abandonLocked() { - SFT_LOGV("abandonLocked"); - mEGLConsumer.onAbandonLocked(); - ConsumerBase::abandonLocked(); -} - -status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { - return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); -} - -void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { - result.appendFormat( - "%smTexName=%d mCurrentTexture=%d\n" - "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", - prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, - mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); - - ConsumerBase::dumpLocked(result, prefix); -} - -sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, - bool* queueEmpty, - uirenderer::RenderState& renderState) { - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); - return nullptr; - } - - if (mOpMode != OpMode::attachedToView) { - SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); - return nullptr; - } - - auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); - if (image.get()) { - uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); - dataSpace = mCurrentDataSpace; - } - return image; -} - -}; // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h deleted file mode 100644 index db392a9f8476..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.h +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include -#include - -#include -#include - -#include "EGLConsumer.h" -#include "ImageConsumer.h" - -namespace android { - -namespace uirenderer { -class RenderState; -} - -/* - * SurfaceTexture consumes buffers of graphics data from a BufferQueue, - * and makes them available to HWUI render thread as a SkImage and to - * an application GL render thread as an OpenGL texture. - * - * When attached to an application GL render thread, a typical usage - * pattern is to set up the SurfaceTexture with the - * desired options, and call updateTexImage() when a new frame is desired. - * If a new frame is available, the texture will be updated. If not, - * the previous contents are retained. - * - * When attached to a HWUI render thread, the TextureView implementation - * calls dequeueImage, which either pulls a new SkImage or returns the - * last cached SkImage if BufferQueue is empty. - * When attached to HWUI render thread, SurfaceTexture is compatible to - * both Vulkan and GL drawing pipelines. - */ -class ANDROID_API SurfaceTexture : public ConsumerBase { -public: - enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES - typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; - - /** - * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with - * the tex parameter is used, tex indicates the name of the OpenGL ES - * texture to which images are to be streamed. texTarget specifies the - * OpenGL ES texture target to which the texture will be bound in - * updateTexImage. useFenceSync specifies whether fences should be used to - * synchronize access to buffers if that behavior is enabled at - * compile-time. - * - * A SurfaceTexture may be detached from one OpenGL ES context and then - * attached to a different context using the detachFromContext and - * attachToContext methods, respectively. The intention of these methods is - * purely to allow a SurfaceTexture to be transferred from one consumer - * context to another. If such a transfer is not needed there is no - * requirement that either of these methods be called. - * - * If the constructor with the tex parameter is used, the SurfaceTexture is - * created in a state where it is considered attached to an OpenGL ES - * context for the purposes of the attachToContext and detachFromContext - * methods. However, despite being considered "attached" to a context, the - * specific OpenGL ES context doesn't get latched until the first call to - * updateTexImage. After that point, all calls to updateTexImage must be - * made with the same OpenGL ES context current. - * - * If the constructor without the tex parameter is used, the SurfaceTexture is - * created in a detached state, and attachToContext must be called before - * calls to updateTexImage. - */ - SurfaceTexture(const sp& bq, uint32_t tex, uint32_t texureTarget, - bool useFenceSync, bool isControlledByApp); - - SurfaceTexture(const sp& bq, uint32_t texureTarget, bool useFenceSync, - bool isControlledByApp); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(); - - /** - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(); - - /** - * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix - * associated with the texture image set by the most recent call to - * updateTexImage. - * - * This transform matrix maps 2D homogeneous texture coordinates of the form - * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture - * coordinate that should be used to sample that location from the texture. - * Sampling the texture outside of the range of this transform is undefined. - * - * This transform is necessary to compensate for transforms that the stream - * content producer may implicitly apply to the content. By forcing users of - * a SurfaceTexture to apply this transform we avoid performing an extra - * copy of the data that would be needed to hide the transform from the - * user. - * - * The matrix is stored in column-major order so that it may be passed - * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv - * functions. - */ - void getTransformMatrix(float mtx[16]); - - /** - * Computes the transform matrix documented by getTransformMatrix - * from the BufferItem sub parts. - */ - static void computeTransformMatrix(float outTransform[16], const sp& buf, - const Rect& cropRect, uint32_t transform, bool filtering); - - /** - * Scale the crop down horizontally or vertically such that it has the - * same aspect ratio as the buffer does. - */ - static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); - - /** - * getTimestamp retrieves the timestamp associated with the texture image - * set by the most recent call to updateTexImage. - * - * The timestamp is in nanoseconds, and is monotonically increasing. Its - * other semantics (zero point, etc) are source-dependent and should be - * documented by the source. - */ - int64_t getTimestamp(); - - /** - * getDataSpace retrieves the DataSpace associated with the texture image - * set by the most recent call to updateTexImage. - */ - android_dataspace getCurrentDataSpace(); - - /** - * getFrameNumber retrieves the frame number associated with the texture - * image set by the most recent call to updateTexImage. - * - * The frame number is an incrementing counter set to 0 at the creation of - * the BufferQueue associated with this consumer. - */ - uint64_t getFrameNumber(); - - /** - * setDefaultBufferSize is used to set the size of buffers returned by - * requestBuffers when a with and height of zero is requested. - * A call to setDefaultBufferSize() may trigger requestBuffers() to - * be called from the client. - * The width and height parameters must be no greater than the minimum of - * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - * An error due to invalid dimensions might not be reported until - * updateTexImage() is called. - */ - status_t setDefaultBufferSize(uint32_t width, uint32_t height); - - /** - * setFilteringEnabled sets whether the transform matrix should be computed - * for use with bilinear filtering. - */ - void setFilteringEnabled(bool enabled); - - /** - * getCurrentTextureTarget returns the texture target of the current - * texture as returned by updateTexImage(). - */ - uint32_t getCurrentTextureTarget() const; - - /** - * getCurrentCrop returns the cropping rectangle of the current buffer. - */ - Rect getCurrentCrop() const; - - /** - * getCurrentTransform returns the transform of the current buffer. - */ - uint32_t getCurrentTransform() const; - - /** - * getCurrentScalingMode returns the scaling mode of the current buffer. - */ - uint32_t getCurrentScalingMode() const; - - /** - * getCurrentFence returns the fence indicating when the current buffer is - * ready to be read from. - */ - sp getCurrentFence() const; - - /** - * getCurrentFence returns the FenceTime indicating when the current - * buffer is ready to be read from. - */ - std::shared_ptr getCurrentFenceTime() const; - - /** - * setConsumerUsageBits overrides the ConsumerBase method to OR - * DEFAULT_USAGE_FLAGS to usage. - */ - status_t setConsumerUsageBits(uint64_t usage); - - /** - * detachFromContext detaches the SurfaceTexture from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a SurfaceTexture from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a SurfaceTexture has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the SurfaceTexture is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(); - - /** - * attachToContext attaches a SurfaceTexture that is currently in the - * 'detached' state to the current OpenGL ES context. A SurfaceTexture is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * SurfaceTexture that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex); - - sk_sp dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, - bool* queueEmpty, uirenderer::RenderState& renderState); - - /** - * attachToView attaches a SurfaceTexture that is currently in the - * 'detached' state to HWUI View system. - */ - void attachToView(); - - /** - * detachFromView detaches a SurfaceTexture from HWUI View system. - */ - void detachFromView(); - -protected: - /** - * abandonLocked overrides the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - virtual void abandonLocked(); - - /** - * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- - * specific info in addition to the ConsumerBase behavior. - */ - virtual void dumpLocked(String8& result, const char* prefix) const override; - - /** - * acquireBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber = 0) override; - - /** - * releaseBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; - - /** - * freeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - * - * This method must be called with mMutex locked. - */ - virtual void freeBufferLocked(int slotIndex); - - /** - * computeCurrentTransformMatrixLocked computes the transform matrix for the - * current texture. It uses mCurrentTransform and the current GraphicBuffer - * to compute this matrix and stores it in mCurrentTransformMatrix. - * mCurrentTextureImage must not be NULL. - */ - void computeCurrentTransformMatrixLocked(); - - /** - * The default consumer usage flags that SurfaceTexture always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the SurfaceTexture user. In particular, SurfaceTexture will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentCrop is the crop rectangle that applies to the current texture. - * It gets set each time updateTexImage is called. - */ - Rect mCurrentCrop; - - /** - * mCurrentTransform is the transform identifier for the current texture. It - * gets set each time updateTexImage is called. - */ - uint32_t mCurrentTransform; - - /** - * mCurrentScalingMode is the scaling mode for the current texture. It gets - * set each time updateTexImage is called. - */ - uint32_t mCurrentScalingMode; - - /** - * mCurrentFence is the fence received from BufferQueue in updateTexImage. - */ - sp mCurrentFence; - - /** - * The FenceTime wrapper around mCurrentFence. - */ - std::shared_ptr mCurrentFenceTime{FenceTime::NO_FENCE}; - - /** - * mCurrentTransformMatrix is the transform matrix for the current texture. - * It gets computed by computeTransformMatrix each time updateTexImage is - * called. - */ - float mCurrentTransformMatrix[16]; - - /** - * mCurrentTimestamp is the timestamp for the current texture. It - * gets set each time updateTexImage is called. - */ - int64_t mCurrentTimestamp; - - /** - * mCurrentDataSpace is the dataspace for the current texture. It - * gets set each time updateTexImage is called. - */ - android_dataspace mCurrentDataSpace; - - /** - * mCurrentFrameNumber is the frame counter for the current texture. - * It gets set each time updateTexImage is called. - */ - uint64_t mCurrentFrameNumber; - - uint32_t mDefaultWidth, mDefaultHeight; - - /** - * mFilteringEnabled indicates whether the transform matrix is computed for - * use with bilinear filtering. It defaults to true and is changed by - * setFilteringEnabled(). - */ - bool mFilteringEnabled; - - /** - * mTexName is the name of the OpenGL texture to which streamed images will - * be bound when updateTexImage is called. It is set at construction time - * and can be changed with a call to attachToContext. - */ - uint32_t mTexName; - - /** - * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync - * extension should be used to prevent buffers from being dequeued before - * it's safe for them to be written. It gets set at construction time and - * never changes. - */ - const bool mUseFenceSync; - - /** - * mTexTarget is the GL texture target with which the GL texture object is - * associated. It is set in the constructor and never changed. It is - * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android - * Browser. In that case it is set to GL_TEXTURE_2D to allow - * glCopyTexSubImage to read from the texture. This is a hack to work - * around a GL driver limitation on the number of FBO attachments, which the - * browser's tile cache exceeds. - */ - const uint32_t mTexTarget; - - /** - * mCurrentTexture is the buffer slot index of the buffer that is currently - * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, - * indicating that no buffer slot is currently bound to the texture. Note, - * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - * that no buffer is bound to the texture. A call to setBufferCount will - * reset mCurrentTexture to INVALID_BUFFER_SLOT. - */ - int mCurrentTexture; - - enum class OpMode { detached, attachedToView, attachedToGL }; - /** - * mOpMode indicates whether the SurfaceTexture is currently attached to - * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, - * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to - * whatever GL context is current at the time of the first updateTexImage call. - * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by - * attachToContext. - * attachToView/detachFromView are used to attach/detach from HWUI view system. - */ - OpMode mOpMode; - - /** - * mEGLConsumer has SurfaceTexture logic used when attached to GL context. - */ - EGLConsumer mEGLConsumer; - - /** - * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. - */ - ImageConsumer mImageConsumer; - - friend class ImageConsumer; - friend class EGLConsumer; -}; - -// ---------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp index d2d37dcb34f2..5b361548eeda 100644 --- a/libs/hwui/tests/common/LeakChecker.cpp +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -16,6 +16,7 @@ #include "LeakChecker.h" +#include "Caches.h" #include "TestUtils.h" #include @@ -70,6 +71,9 @@ void LeakChecker::checkForLeaks() { // thread-local caches so some leaks will not be properly tagged as leaks UnreachableMemoryInfo rtMemInfo; TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { + if (Caches::hasInstance()) { + Caches::getInstance().tasks.stop(); + } // Check for leaks if (!GetUnreachableMemory(rtMemInfo)) { cerr << "Failed to get unreachable memory!" << endl; diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 66b9b85bdbe7..69586345319e 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -67,14 +67,16 @@ sp TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { sp layerUpdater = createTextureLayerUpdater(renderThread); - layerUpdater->backingLayer()->getTransform() = transform; + layerUpdater->backingLayer()->getTransform().load(transform); layerUpdater->setSize(width, height); layerUpdater->setTransform(&transform); // updateLayer so it's ready to draw - SkMatrix identity; - identity.setIdentity(); - layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr); + layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN); + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + static_cast(layerUpdater->backingLayer()) + ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); + } return layerUpdater; } @@ -115,6 +117,7 @@ void TestUtils::TestTask::run() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { renderThread.vulkanManager().destroy(); } else { + renderThread.renderState().flush(Caches::FlushMode::Full); renderThread.destroyGlContext(); } } diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 0e6582c59a36..743f8093bfa8 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index 6c8775b1bdbb..f29830f0e34b 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -15,13 +15,12 @@ */ #include "DeferredLayerUpdater.h" +#include "GlLayer.h" #include "Properties.h" #include "tests/common/TestUtils.h" #include -#include -#include using namespace android; using namespace android::uirenderer; @@ -32,6 +31,10 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { layerUpdater->setBlend(true); // updates are deferred so the backing layer should still be in its default state + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); + EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget()); + } EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight()); EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter()); @@ -39,13 +42,19 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); // push the deferred updates to the layer - SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5); - SkBitmap bitmap; - bitmap.allocN32Pixels(16, 16); - sk_sp layerImage = SkImage::MakeFromBitmap(bitmap); - layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage); + Matrix4 scaledMatrix; + scaledMatrix.loadScale(0.5, 0.5, 0.0); + layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN); + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); + glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); + } // the backing layer should now have all the properties applied. + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); + EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget()); + } EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight()); EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter()); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index aecceb3609f5..9e6d9a8c27de 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -17,13 +17,12 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "Caches.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "Properties.h" #include "tests/common/LeakChecker.h" -#include "thread/TaskProcessor.h" -#include "thread/Task.h" #include "thread/TaskManager.h" #include diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index ebf2343c5518..f8e8a0a18284 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -16,7 +16,6 @@ #ifndef PAINT_UTILS_H #define PAINT_UTILS_H -#include #include #include diff --git a/native/android/Android.bp b/native/android/Android.bp index 43847cc4ab06..4fb5e748aaac 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -64,7 +64,6 @@ cc_library_shared { "libsensor", "libandroid_runtime", "libnetd_client", - "libhwui", ], static_libs: [ diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp index ced2792775d4..b26688190ccd 100644 --- a/native/android/surface_texture.cpp +++ b/native/android/surface_texture.cpp @@ -21,16 +21,15 @@ #include +#include #include #include -#include "surfacetexture/SurfaceTexture.h" - using namespace android; struct ASurfaceTexture { - sp consumer; + sp consumer; sp producer; }; -- cgit v1.2.3-59-g8ed1b From 85f9096b5272c9a39e592e2e97cbbe6cb0e767ab Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Fri, 31 Aug 2018 18:35:06 +0000 Subject: Revert "Revert "TextureView Vulkan support and optimized OpenGL draw"" This reverts commit 867c43de0544217d26c3ee18f4d6603bb2ea97ce. Reason for revert: Fixed issue with unsupported hardware buffer formats by landing https://skia-review.googlesource.com/c/skia/+/150470 "Support more hardware buffer formats" Bug: 113673613 Change-Id: I1f7c528f138e290160e75833c8d989d9535002ee --- core/jni/android/graphics/ColorFilter.cpp | 2 - core/jni/android/graphics/Matrix.cpp | 1 - core/jni/android/graphics/Shader.cpp | 1 - core/jni/android/graphics/SurfaceTexture.cpp | 47 +- core/jni/android_view_TextureLayer.cpp | 3 +- .../android_graphics_SurfaceTexture.h | 6 +- libs/hwui/Android.bp | 10 +- libs/hwui/Caches.cpp | 103 ---- libs/hwui/Caches.h | 135 ----- libs/hwui/DeferredLayerUpdater.cpp | 123 +--- libs/hwui/DeferredLayerUpdater.h | 34 +- libs/hwui/GlLayer.cpp | 68 --- libs/hwui/GlLayer.h | 75 --- libs/hwui/GpuMemoryTracker.cpp | 17 - libs/hwui/Layer.cpp | 8 +- libs/hwui/Layer.h | 57 +- libs/hwui/PixelBuffer.cpp | 156 ----- libs/hwui/PixelBuffer.h | 198 ------ libs/hwui/RenderProperties.h | 1 - libs/hwui/ResourceCache.cpp | 23 +- libs/hwui/Texture.cpp | 413 ------------- libs/hwui/Texture.h | 228 ------- libs/hwui/VkLayer.cpp | 40 -- libs/hwui/VkLayer.h | 70 --- libs/hwui/hwui/Bitmap.cpp | 2 +- libs/hwui/pipeline/skia/LayerDrawable.cpp | 32 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 12 +- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 9 +- libs/hwui/renderstate/PixelBufferState.cpp | 42 -- libs/hwui/renderstate/PixelBufferState.h | 38 -- libs/hwui/renderstate/RenderState.cpp | 83 +-- libs/hwui/renderstate/RenderState.h | 20 +- libs/hwui/renderstate/TextureState.cpp | 147 ----- libs/hwui/renderstate/TextureState.h | 98 --- libs/hwui/renderthread/CacheManager.cpp | 4 +- libs/hwui/renderthread/CanvasContext.cpp | 8 - libs/hwui/renderthread/EglManager.cpp | 104 ++++ libs/hwui/renderthread/EglManager.h | 10 + libs/hwui/renderthread/RenderThread.cpp | 4 +- libs/hwui/renderthread/VulkanManager.cpp | 20 +- libs/hwui/renderthread/VulkanManager.h | 8 + libs/hwui/surfacetexture/EGLConsumer.cpp | 675 +++++++++++++++++++++ libs/hwui/surfacetexture/EGLConsumer.h | 311 ++++++++++ libs/hwui/surfacetexture/ImageConsumer.cpp | 152 +++++ libs/hwui/surfacetexture/ImageConsumer.h | 97 +++ libs/hwui/surfacetexture/SurfaceTexture.cpp | 496 +++++++++++++++ libs/hwui/surfacetexture/SurfaceTexture.h | 452 ++++++++++++++ libs/hwui/tests/common/LeakChecker.cpp | 4 - libs/hwui/tests/common/TestUtils.cpp | 11 +- libs/hwui/tests/common/TestUtils.h | 1 - libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp | 23 +- libs/hwui/tests/unit/main.cpp | 3 +- libs/hwui/utils/PaintUtils.h | 1 + native/android/Android.bp | 1 + native/android/surface_texture.cpp | 5 +- 55 files changed, 2490 insertions(+), 2202 deletions(-) delete mode 100644 libs/hwui/Caches.cpp delete mode 100644 libs/hwui/Caches.h delete mode 100644 libs/hwui/GlLayer.cpp delete mode 100644 libs/hwui/GlLayer.h delete mode 100644 libs/hwui/PixelBuffer.cpp delete mode 100644 libs/hwui/PixelBuffer.h delete mode 100644 libs/hwui/Texture.cpp delete mode 100644 libs/hwui/Texture.h delete mode 100644 libs/hwui/VkLayer.cpp delete mode 100644 libs/hwui/VkLayer.h delete mode 100644 libs/hwui/renderstate/PixelBufferState.cpp delete mode 100644 libs/hwui/renderstate/PixelBufferState.h delete mode 100644 libs/hwui/renderstate/TextureState.cpp delete mode 100644 libs/hwui/renderstate/TextureState.h create mode 100644 libs/hwui/surfacetexture/EGLConsumer.cpp create mode 100644 libs/hwui/surfacetexture/EGLConsumer.h create mode 100644 libs/hwui/surfacetexture/ImageConsumer.cpp create mode 100644 libs/hwui/surfacetexture/ImageConsumer.h create mode 100644 libs/hwui/surfacetexture/SurfaceTexture.cpp create mode 100644 libs/hwui/surfacetexture/SurfaceTexture.h (limited to 'libs/hwui/ResourceCache.cpp') diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index 3fcedd0264ae..6ebf35c8e1dc 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -22,8 +22,6 @@ #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" -#include - namespace android { using namespace uirenderer; diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index f8bb77a9650c..755fcfb27141 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -20,7 +20,6 @@ #include "SkMatrix.h" #include "core_jni_helpers.h" -#include #include namespace android { diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index cff772002b14..68f5bef18de1 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -6,7 +6,6 @@ #include "SkBlendMode.h" #include "core_jni_helpers.h" -#include #include using namespace android::uirenderer; diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index d098a355085e..3e464c61665f 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -36,6 +36,7 @@ #include "jni.h" #include #include +#include "surfacetexture/SurfaceTexture.h" // ---------------------------------------------------------------------------- @@ -80,10 +81,10 @@ static bool isProtectedContext() { // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp& surfaceTexture) + const sp& surfaceTexture) { - GLConsumer* const p = - (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); + SurfaceTexture* const p = + (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); if (surfaceTexture.get()) { surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture); } @@ -108,10 +109,10 @@ static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz, } static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, - jobject thiz, sp listener) + jobject thiz, sp listener) { - GLConsumer::FrameAvailableListener* const p = - (GLConsumer::FrameAvailableListener*) + SurfaceTexture::FrameAvailableListener* const p = + (SurfaceTexture::FrameAvailableListener*) env->GetLongField(thiz, fields.frameAvailableListener); if (listener.get()) { listener->incStrong((void*)SurfaceTexture_setSurfaceTexture); @@ -122,8 +123,8 @@ static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get()); } -sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { - return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); +sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { + return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); } sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) { @@ -131,7 +132,7 @@ sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) } sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); sp producer(SurfaceTexture_getProducer(env, thiz)); sp surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL); return surfaceTextureClient; @@ -144,7 +145,7 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) { // ---------------------------------------------------------------------------- -class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener +class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener { public: JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz); @@ -266,12 +267,12 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, consumer->setMaxBufferCount(1); } - sp surfaceTexture; + sp surfaceTexture; if (isDetached) { - surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, + surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } else { - surfaceTexture = new GLConsumer(consumer, texName, + surfaceTexture = new SurfaceTexture(consumer, texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } @@ -306,7 +307,7 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setFrameAvailableListener(0); SurfaceTexture_setFrameAvailableListener(env, thiz, 0); SurfaceTexture_setSurfaceTexture(env, thiz, 0); @@ -315,13 +316,13 @@ static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) static void SurfaceTexture_setDefaultBufferSize( JNIEnv* env, jobject thiz, jint width, jint height) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->updateTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to update texture contents (see " @@ -333,7 +334,7 @@ static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->releaseTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to release texture contents (see " @@ -345,20 +346,20 @@ static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); float* mtx = env->GetFloatArrayElements(jmtx, NULL); surfaceTexture->getTransformMatrix(mtx); env->ReleaseFloatArrayElements(jmtx, mtx, 0); @@ -366,19 +367,19 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->isAbandoned(); } diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp index d3a447f1f7dc..1ccb6a8f610c 100644 --- a/core/jni/android_view_TextureLayer.cpp +++ b/core/jni/android_view_TextureLayer.cpp @@ -67,8 +67,7 @@ static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr, jobject surface) { DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); - layer->setSurfaceTexture(surfaceTexture); + layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); } static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h index c534d4bb9e0a..0ad25876a008 100644 --- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h +++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h @@ -23,14 +23,14 @@ namespace android { -class GLConsumer; class IGraphicBufferProducer; +class SurfaceTexture; extern sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz); extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz); -/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */ -extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */ +extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); /* gets the producer end of the SurfaceTexture */ extern sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index d5829838cb3f..b7ffb5d9dc2e 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -175,9 +175,7 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", - "renderstate/PixelBufferState.cpp", "renderstate/RenderState.cpp", - "renderstate/TextureState.cpp", "renderthread/CacheManager.cpp", "renderthread/CanvasContext.cpp", "renderthread/DrawFrameTask.cpp", @@ -189,6 +187,9 @@ cc_defaults { "renderthread/TimeLord.cpp", "renderthread/Frame.cpp", "service/GraphicsStatsService.cpp", + "surfacetexture/EGLConsumer.cpp", + "surfacetexture/ImageConsumer.cpp", + "surfacetexture/SurfaceTexture.cpp", "thread/TaskManager.cpp", "utils/Blur.cpp", "utils/Color.cpp", @@ -200,7 +201,6 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", - "Caches.cpp", "CanvasState.cpp", "CanvasTransform.cpp", "ClipArea.cpp", @@ -209,7 +209,6 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", - "GlLayer.cpp", "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "Interpolator.cpp", @@ -219,7 +218,6 @@ cc_defaults { "Matrix.cpp", "EglReadback.cpp", "PathParser.cpp", - "PixelBuffer.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Properties.cpp", @@ -231,9 +229,7 @@ cc_defaults { "ResourceCache.cpp", "SkiaCanvas.cpp", "Snapshot.cpp", - "Texture.cpp", "VectorDrawable.cpp", - "VkLayer.cpp", "protos/graphicsstats.proto", ], diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp deleted file mode 100644 index 254144448859..000000000000 --- a/libs/hwui/Caches.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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 "GlLayer.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -Caches* Caches::sInstance = nullptr; - -/////////////////////////////////////////////////////////////////////////////// -// Macros -/////////////////////////////////////////////////////////////////////////////// - -#if DEBUG_CACHE_FLUSH -#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) -#else -#define FLUSH_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -Caches::Caches(RenderState& renderState) : mInitialized(false) { - INIT_LOGD("Creating OpenGL renderer caches"); - init(); - initStaticProperties(); -} - -bool Caches::init() { - if (mInitialized) return false; - - ATRACE_NAME("Caches::init"); - - mRegionMesh = nullptr; - - mInitialized = true; - - mPixelBufferState = new PixelBufferState(); - mTextureState = new TextureState(); - mTextureState->constructTexture(*this); - - return true; -} - -void Caches::initStaticProperties() { - // OpenGL ES 3.0+ specific features - gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() && - property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); -} - -void Caches::terminate() { - if (!mInitialized) return; - mRegionMesh.reset(nullptr); - - clearGarbage(); - - delete mPixelBufferState; - mPixelBufferState = nullptr; - delete mTextureState; - mTextureState = nullptr; - mInitialized = false; -} - -/////////////////////////////////////////////////////////////////////////////// -// Memory management -/////////////////////////////////////////////////////////////////////////////// - -void Caches::clearGarbage() {} - -void Caches::flush(FlushMode mode) { - clearGarbage(); - glFinish(); - // Errors during cleanup should be considered non-fatal, dump them and - // and move on. TODO: All errors or just errors like bad surface? - GLUtils::dumpGLErrors(); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h deleted file mode 100644 index 642f9dc50eb1..000000000000 --- a/libs/hwui/Caches.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#pragma once - -#include "DeviceInfo.h" -#include "Extensions.h" -#include "ResourceCache.h" -#include "renderstate/PixelBufferState.h" -#include "renderstate/TextureState.h" -#include "thread/TaskManager.h" -#include "thread/TaskProcessor.h" - -#include -#include - -#include - -#include - -#include - -#include - -#include - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Caches -/////////////////////////////////////////////////////////////////////////////// - -class RenderNode; -class RenderState; - -class ANDROID_API Caches { -public: - static Caches& createInstance(RenderState& renderState) { - LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted"); - sInstance = new Caches(renderState); - return *sInstance; - } - - static Caches& getInstance() { - LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created"); - return *sInstance; - } - - static bool hasInstance() { return sInstance != nullptr; } - -private: - explicit Caches(RenderState& renderState); - static Caches* sInstance; - -public: - enum class FlushMode { Layers = 0, Moderate, Full }; - - /** - * Initialize caches. - */ - bool init(); - - bool isInitialized() { return mInitialized; } - - /** - * Flush the cache. - * - * @param mode Indicates how much of the cache should be flushed - */ - void flush(FlushMode mode); - - /** - * Destroys all resources associated with this cache. This should - * be called after a flush(FlushMode::Full). - */ - void terminate(); - - /** - * Call this on each frame to ensure that garbage is deleted from - * GPU memory. - */ - void clearGarbage(); - - /** - * 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().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; - } - -public: - TaskManager tasks; - - bool gpuPixelBuffersEnabled; - - const Extensions& extensions() const { return DeviceInfo::get()->extensions(); } - PixelBufferState& pixelBufferState() { return *mPixelBufferState; } - TextureState& textureState() { return *mTextureState; } - -private: - void initStaticProperties(); - - static void eventMarkNull(GLsizei length, const GLchar* marker) {} - static void startMarkNull(GLsizei length, const GLchar* marker) {} - static void endMarkNull() {} - - // Used to render layers - std::unique_ptr mRegionMesh; - - bool mInitialized; - - // TODO: move below to RenderState - PixelBufferState* mPixelBufferState = nullptr; - TextureState* mTextureState = nullptr; - -}; // class Caches - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 569de76f294e..00916559a9c2 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,27 +15,20 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderTask.h" #include "utils/PaintUtils.h" namespace android { namespace uirenderer { -DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi) +DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) - , mLayer(nullptr) - , mLayerApi(layerApi) - , mCreateLayerFn(createLayerFn) { + , mLayer(nullptr) { renderState.registerDeferredLayerUpdater(this); } @@ -50,13 +43,9 @@ void DeferredLayerUpdater::destroyLayer() { return; } - if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) { - status_t err = mSurfaceTexture->detachFromContext(); + if (mSurfaceTexture.get() && mGLContextAttached) { + mSurfaceTexture->detachFromView(); mGLContextAttached = false; - if (err != 0) { - // TODO: Elevate to fatal exception - ALOGE("Failed to detach SurfaceTexture from context %d", err); - } } mLayer->postDecStrong(); @@ -75,99 +64,53 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { void DeferredLayerUpdater::apply() { if (!mLayer) { - mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend); + mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); } mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { - if (mLayer->getApi() == Layer::Api::Vulkan) { - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateVkTexImage(); - } - } else { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "apply surfaceTexture with non GL backend %x, GL %x, VK %x", - mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); - if (!mGLContextAttached) { - mGLContextAttached = true; - mUpdateTexImage = true; - mSurfaceTexture->attachToContext(static_cast(mLayer)->getTextureId()); - } - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateTexImage(); + if (!mGLContextAttached) { + mGLContextAttached = true; + mUpdateTexImage = true; + mSurfaceTexture->attachToView(); + } + if (mUpdateTexImage) { + mUpdateTexImage = false; + sk_sp layerImage; + SkMatrix textureTransform; + android_dataspace dataSpace; + bool queueEmpty = true; + // If the SurfaceTexture queue is in synchronous mode, need to discard all + // but latest frame. Since we can't tell which mode it is in, + // do this unconditionally. + do { + layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty, + mRenderState); + } while (layerImage.get() && (!queueEmpty)); + if (layerImage.get()) { + // force filtration if buffer size != layer size + bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); + updateLayer(forceFilter, textureTransform, dataSpace, layerImage); } - GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); - static_cast(mLayer)->setRenderTarget(renderTarget); } + if (mTransform) { - mLayer->getTransform().load(*mTransform); + mLayer->getTransform() = *mTransform; setTransform(nullptr); } } } -void DeferredLayerUpdater::doUpdateTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - if (mSurfaceTexture->updateTexImage() == NO_ERROR) { - float transform[16]; - - int64_t frameNumber = mSurfaceTexture->getFrameNumber(); - // If the GLConsumer queue is in synchronous mode, need to discard all - // but latest frame, using the frame number to tell when we no longer - // have newer frames to target. Since we can't tell which mode it is in, - // do this unconditionally. - int dropCounter = 0; - while (mSurfaceTexture->updateTexImage() == NO_ERROR) { - int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); - if (newFrameNumber == frameNumber) break; - frameNumber = newFrameNumber; - dropCounter++; - } - - bool forceFilter = false; - sp buffer = mSurfaceTexture->getCurrentBuffer(); - if (buffer != nullptr) { - // force filtration if buffer size != layer size - forceFilter = mWidth != static_cast(buffer->getWidth()) || - mHeight != static_cast(buffer->getHeight()); - } - -#if DEBUG_RENDERER - if (dropCounter > 0) { - RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); - } -#endif - mSurfaceTexture->getTransformMatrix(transform); - - updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace()); - } -} - -void DeferredLayerUpdater::doUpdateVkTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, - "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - - static const mat4 identityMatrix; - updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN); - - VkLayer* vkLayer = static_cast(mLayer); - vkLayer->updateTexture(); -} - -void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform, - android_dataspace dataspace) { +void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp& layerImage) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); - mLayer->getTexTransform().load(textureTransform); + mLayer->getTexTransform() = textureTransform; mLayer->setDataSpace(dataspace); + mLayer->setImage(layerImage); } void DeferredLayerUpdater::detachSurfaceTexture() { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index fe3ee7a2b4c6..4c323b861002 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -17,18 +17,19 @@ #pragma once #include +#include #include #include -#include +#include #include #include #include #include +#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" -#include "renderthread/RenderThread.h" namespace android { namespace uirenderer { @@ -41,12 +42,7 @@ class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - typedef std::function colorFilter, int alpha, - SkBlendMode mode, bool blend)> - CreateLayerFn; - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi); + ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState); ANDROID_API ~DeferredLayerUpdater(); @@ -70,13 +66,13 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp& texture) { - if (texture.get() != mSurfaceTexture.get()) { - mSurfaceTexture = texture; + ANDROID_API void setSurfaceTexture(const sp& consumer) { + if (consumer.get() != mSurfaceTexture.get()) { + mSurfaceTexture = consumer; - GLenum target = texture->getCurrentTextureTarget(); + GLenum target = consumer->getCurrentTextureTarget(); LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported GLConsumer with target %x", target); + "set unsupported SurfaceTexture with target %x", target); } } @@ -97,12 +93,11 @@ public: void detachSurfaceTexture(); - void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace); + void updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp& layerImage); void destroyLayer(); - Layer::Api getBackingLayerApi() { return mLayerApi; } - private: RenderState& mRenderState; @@ -113,17 +108,12 @@ private: sk_sp mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp mSurfaceTexture; + sp mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; Layer* mLayer; - Layer::Api mLayerApi; - CreateLayerFn mCreateLayerFn; - - void doUpdateTexImage(); - void doUpdateVkTexImage(); }; } /* namespace uirenderer */ diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp deleted file mode 100644 index 432bb8526465..000000000000 --- a/libs/hwui/GlLayer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 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 "GlLayer.h" - -#include "Caches.h" -#include "RenderNode.h" -#include "renderstate/RenderState.h" - -namespace android { -namespace uirenderer { - -GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode) - , caches(Caches::getInstance()) - , texture(caches) { - texture.mWidth = layerWidth; - texture.mHeight = layerHeight; - texture.blend = blend; -} - -GlLayer::~GlLayer() { - // There's a rare possibility that Caches could have been destroyed already - // since this method is queued up as a task. - // Since this is a reset method, treat this as non-fatal. - if (caches.isInitialized() && texture.mId) { - texture.deleteTexture(); - } -} - -void GlLayer::onGlContextLost() { - texture.deleteTexture(); -} - -void GlLayer::setRenderTarget(GLenum renderTarget) { - if (renderTarget != getRenderTarget()) { - // new render target: bind with new target, and update filter/wrap - texture.mTarget = renderTarget; - if (texture.mId) { - caches.textureState().bindTexture(texture.target(), texture.mId); - } - texture.setFilter(GL_NEAREST, false, true); - texture.setWrap(GL_CLAMP_TO_EDGE, false, true); - } -} - -void GlLayer::generateTexture() { - if (!texture.mId) { - glGenTextures(1, &texture.mId); - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h deleted file mode 100644 index 9f70fdae6790..000000000000 --- a/libs/hwui/GlLayer.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include "Layer.h" - -#include "Texture.h" - -namespace android { -namespace uirenderer { - -// Forward declarations -class Caches; - -/** - * A layer has dimensions and is backed by an OpenGL texture or FBO. - */ -class GlLayer : public Layer { -public: - GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend); - virtual ~GlLayer(); - - uint32_t getWidth() const override { return texture.mWidth; } - - uint32_t getHeight() const override { return texture.mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - texture.updateLayout(width, height, texture.internalFormat(), texture.format(), - texture.target()); - } - - void setBlend(bool blend) override { texture.blend = blend; } - - bool isBlend() const override { return texture.blend; } - - inline GLuint getTextureId() const { return texture.id(); } - - inline GLenum getRenderTarget() const { return texture.target(); } - - void setRenderTarget(GLenum renderTarget); - - void generateTexture(); - - /** - * Lost the GL context but the layer is still around, mark it invalid internally - * so the dtor knows not to do any GL work - */ - void onGlContextLost(); - -private: - Caches& caches; - - /** - * The texture backing this layer. - */ - Texture texture; -}; // struct GlLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index 612bfde1a3fa..a9a7af8f22f3 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "Texture.h" #include "utils/StringUtils.h" #include @@ -117,22 +116,6 @@ void GpuMemoryTracker::onFrameCompleted() { ATRACE_INT(buf, stats.count); } } - - std::vector freeList; - for (const auto& obj : gObjectSet) { - if (obj->objectType() == GpuObjectType::Texture) { - const Texture* texture = static_cast(obj); - if (texture->cleanup) { - ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(), - texture->width(), texture->height()); - freeList.push_back(texture); - } - } - } - for (auto& texture : freeList) { - const_cast(texture)->deleteTexture(); - delete texture; - } } } // namespace uirenderer diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index fb8f0337c95e..f59a2e6ee5c1 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -17,17 +17,17 @@ #include "Layer.h" #include "renderstate/RenderState.h" +#include "utils/Color.h" #include namespace android { namespace uirenderer { -Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter, int alpha, - SkBlendMode mode) +Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alpha, + SkBlendMode mode) : GpuMemoryTracker(GpuObjectType::Layer) , mRenderState(renderState) - , mApi(api) , mColorFilter(colorFilter) , alpha(alpha) , mode(mode) { @@ -36,6 +36,8 @@ Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter incStrong(nullptr); buildColorSpaceWithFilter(); renderState.registerLayer(this); + texTransform.setIdentity(); + transform.setIdentity(); } Layer::~Layer() { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 31878ac23642..c4e4c1c96ba6 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -23,8 +23,9 @@ #include #include #include - -#include "Matrix.h" +#include +#include +#include namespace android { namespace uirenderer { @@ -40,24 +41,19 @@ class RenderState; */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - enum class Api { - OpenGL = 0, - Vulkan = 1, - }; - - Api getApi() const { return mApi; } + Layer(RenderState& renderState, sk_sp, int alpha, SkBlendMode mode); ~Layer(); - virtual uint32_t getWidth() const = 0; + virtual uint32_t getWidth() const { return mWidth; } - virtual uint32_t getHeight() const = 0; + virtual uint32_t getHeight() const { return mHeight; } - virtual void setSize(uint32_t width, uint32_t height) = 0; + virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } - virtual void setBlend(bool blend) = 0; + virtual void setBlend(bool blend) { mBlend = blend; } - virtual bool isBlend() const = 0; + virtual bool isBlend() const { return mBlend; } inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } @@ -84,9 +80,9 @@ public: inline sk_sp getColorSpaceWithFilter() const { return mColorSpaceWithFilter; } - inline mat4& getTexTransform() { return texTransform; } + inline SkMatrix& getTexTransform() { return texTransform; } - inline mat4& getTransform() { return transform; } + inline SkMatrix& getTransform() { return transform; } /** * Posts a decStrong call to the appropriate thread. @@ -94,16 +90,17 @@ public: */ void postDecStrong(); + inline void setImage(const sk_sp& image) { this->layerImage = image; } + + inline sk_sp getImage() const { return this->layerImage; } + protected: - Layer(RenderState& renderState, Api api, sk_sp, int alpha, SkBlendMode mode); RenderState& mRenderState; private: void buildColorSpaceWithFilter(); - Api mApi; - /** * Color filter used to draw this layer. Optional. */ @@ -137,12 +134,32 @@ private: /** * Optional texture coordinates transform. */ - mat4 texTransform; + SkMatrix texTransform; /** * Optional transform. */ - mat4 transform; + SkMatrix transform; + + /** + * An image backing the layer. + */ + sk_sp layerImage; + + /** + * layer width. + */ + uint32_t mWidth = 0; + + /** + * layer height. + */ + uint32_t mHeight = 0; + + /** + * enable blending + */ + bool mBlend = false; }; // struct Layer diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp deleted file mode 100644 index 910a9889db1f..000000000000 --- a/libs/hwui/PixelBuffer.cpp +++ /dev/null @@ -1,156 +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 "PixelBuffer.h" - -#include "Debug.h" -#include "Extensions.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// CPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class CpuPixelBuffer : public PixelBuffer { -public: - CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - std::unique_ptr mBuffer; -}; - -CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mBuffer(new uint8_t[width * height * formatSize(format)]) {} - -uint8_t* CpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mAccessMode = mode; - } - return mBuffer.get(); -} - -void CpuPixelBuffer::unmap() { - mAccessMode = kAccessMode_None; -} - -void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - &mBuffer[offset]); -} - -/////////////////////////////////////////////////////////////////////////////// -// GPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class GpuPixelBuffer : public PixelBuffer { -public: - GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - ~GpuPixelBuffer(); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - GLuint mBuffer; - uint8_t* mMappedPointer; - Caches& mCaches; -}; - -GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mMappedPointer(nullptr) - , mCaches(Caches::getInstance()) { - glGenBuffers(1, &mBuffer); - - mCaches.pixelBufferState().bind(mBuffer); - glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW); - mCaches.pixelBufferState().unbind(); -} - -GpuPixelBuffer::~GpuPixelBuffer() { - glDeleteBuffers(1, &mBuffer); -} - -uint8_t* GpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mCaches.pixelBufferState().bind(mBuffer); - mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); - if (CC_UNLIKELY(!mMappedPointer)) { - GLUtils::dumpGLErrors(); - LOG_ALWAYS_FATAL("Failed to map PBO"); - } - mAccessMode = mode; - mCaches.pixelBufferState().unbind(); - } - - return mMappedPointer; -} - -void GpuPixelBuffer::unmap() { - if (mAccessMode != kAccessMode_None) { - if (mMappedPointer) { - mCaches.pixelBufferState().bind(mBuffer); - GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - if (status == GL_FALSE) { - ALOGE("Corrupted GPU pixel buffer"); - } - } - mAccessMode = kAccessMode_None; - mMappedPointer = nullptr; - } -} - -void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - // If the buffer is not mapped, unmap() will not bind it - mCaches.pixelBufferState().bind(mBuffer); - unmap(); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - reinterpret_cast(offset)); - mCaches.pixelBufferState().unbind(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Factory -/////////////////////////////////////////////////////////////////////////////// - -PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { - if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { - return new GpuPixelBuffer(format, width, height); - } - return new CpuPixelBuffer(format, width, height); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h deleted file mode 100644 index e7e341b90ad3..000000000000 --- a/libs/hwui/PixelBuffer.h +++ /dev/null @@ -1,198 +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_PIXEL_BUFFER_H -#define ANDROID_HWUI_PIXEL_BUFFER_H - -#include - -#include - -namespace android { -namespace uirenderer { - -/** - * Represents a pixel buffer. A pixel buffer will be backed either by a - * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other - * versions. If the buffer is backed by a PBO it will of type - * GL_PIXEL_UNPACK_BUFFER. - * - * To read from or write into a PixelBuffer you must first map the - * buffer using the map(AccessMode) method. This method returns a - * pointer to the beginning of the buffer. - * - * Before the buffer can be used by the GPU, for instance to upload - * a texture, you must first unmap the buffer. To do so, call the - * unmap() method. - * - * Mapping and unmapping a PixelBuffer can have the side effect of - * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is - * therefore recommended to call Caches::unbindPixelbuffer() after - * using a PixelBuffer to upload to a texture. - */ -class PixelBuffer { -public: - enum BufferType { kBufferType_Auto, kBufferType_CPU }; - - enum AccessMode { - kAccessMode_None = 0, - kAccessMode_Read = GL_MAP_READ_BIT, - kAccessMode_Write = GL_MAP_WRITE_BIT, - kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT - }; - - /** - * Creates a new PixelBuffer object with the specified format and - * dimensions. The buffer is immediately allocated. - * - * The buffer type specifies how the buffer should be allocated. - * By default this method will automatically choose whether to allocate - * a CPU or GPU buffer. - */ - static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height, - BufferType type = kBufferType_Auto); - - virtual ~PixelBuffer() {} - - /** - * Returns the format of this render buffer. - */ - GLenum getFormat() const { return mFormat; } - - /** - * Maps this before with the specified access mode. This method - * returns a pointer to the region of memory where the buffer was - * mapped. - * - * If the buffer is already mapped when this method is invoked, - * this method will return the previously mapped pointer. The - * access mode can only be changed by calling unmap() first. - * - * The specified access mode cannot be kAccessMode_None. - */ - virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0; - - /** - * Returns the current access mode for this buffer. If the buffer - * is not mapped, this method returns kAccessMode_None. - */ - AccessMode getAccessMode() const { return mAccessMode; } - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - */ - virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0; - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - * - * This is a convenience function provided to save callers the - * trouble of computing the offset parameter. - */ - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - upload(x, y, width, height, getOffset(x, y)); - } - - /** - * Returns the width of the render buffer in pixels. - */ - uint32_t getWidth() const { return mWidth; } - - /** - * Returns the height of the render buffer in pixels. - */ - uint32_t getHeight() const { return mHeight; } - - /** - * Returns the size of this pixel buffer in bytes. - */ - uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); } - - /** - * Returns the offset of a pixel in this pixel buffer, in bytes. - */ - uint32_t getOffset(uint32_t x, uint32_t y) const { - return (y * mWidth + x) * formatSize(mFormat); - } - - /** - * Returns the number of bytes per pixel in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatSize(GLenum format) { - switch (format) { - case GL_ALPHA: - return 1; - case GL_RGBA: - return 4; - } - return 0; - } - - /** - * Returns the alpha channel offset in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatAlphaOffset(GLenum format) { - switch (format) { - case GL_ALPHA: - return 0; - case GL_RGBA: - return 3; - } - - ALOGE("unsupported format: %d", format); - return 0; - } - -protected: - /** - * Creates a new render buffer in the specified format and dimensions. - * The format must be GL_ALPHA or GL_RGBA. - */ - PixelBuffer(GLenum format, uint32_t width, uint32_t height) - : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {} - - /** - * Unmaps this buffer, if needed. After the buffer is unmapped, - * the pointer previously returned by map() becomes invalid and - * should not be used. - */ - virtual void unmap() = 0; - - GLenum mFormat; - - uint32_t mWidth; - uint32_t mHeight; - - AccessMode mAccessMode; - -}; // class PixelBuffer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_PIXEL_BUFFER_H diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 0766e3b7ed28..7966845ff814 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,6 @@ #pragma once -#include "Caches.h" #include "DeviceInfo.h" #include "Outline.h" #include "Rect.h" diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 464a58d0c0f8..65bee476f14d 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -15,7 +15,6 @@ */ #include "ResourceCache.h" -#include "Caches.h" namespace android { @@ -112,13 +111,9 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; if (ref == nullptr) { // If we're not tracking this resource, just delete it - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + delete[](int8_t*) resource; return; } ref->destroyed = true; @@ -135,14 +130,10 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource if (ref->destroyed) { switch (ref->resourceType) { case kNinePatch: { - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + int8_t* patch = (int8_t*)resource; + delete[] patch; } break; } } diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp deleted file mode 100644 index 1e90eebe3bb8..000000000000 --- a/libs/hwui/Texture.cpp +++ /dev/null @@ -1,413 +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 "Texture.h" -#include "Caches.h" -#include "utils/GLUtils.h" -#include "utils/MathUtils.h" -#include "utils/TraceUtils.h" - -#include - -#include - -#include - -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 8; - default: - LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); - } -} - -void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { - if (force || wrapS != mWrapS || wrapT != mWrapT) { - mWrapS = wrapS; - mWrapT = wrapT; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); - glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); - } -} - -void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { - if (force || min != mMinFilter || mag != mMagFilter) { - mMinFilter = min; - mMagFilter = mag; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; - - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); - glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); - } -} - -void Texture::deleteTexture() { - mCaches.textureState().deleteTexture(mId); - mId = 0; - mTarget = GL_NONE; - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } -} - -bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - if (mWidth == width && mHeight == height && mFormat == format && - mInternalFormat == internalFormat && mTarget == target) { - return false; - } - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); - return true; -} - -void Texture::resetCachedParams() { - mWrapS = GL_REPEAT; - mWrapT = GL_REPEAT; - mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - mMagFilter = GL_LINEAR; -} - -void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, - GLenum type, const void* pixels) { - GL_CHECKPOINT(MODERATE); - - // We don't have color space information, we assume the data is gamma encoded - mIsLinear = false; - - bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D); - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - resetCachedParams(); - } - mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); - if (needsAlloc) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } - GL_CHECKPOINT(MODERATE); -} - -void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } - mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - buffer->getNativeBuffer(), 0); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); -} - -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(); - if ((stride == width) || useStride) { - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } - - if (resize) { - 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); - } - - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - } else { - // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width - - GLvoid* temp = (GLvoid*)malloc(width * height * bpp); - if (!temp) return; - - uint8_t* pDst = (uint8_t*)temp; - uint8_t* pSrc = (uint8_t*)data; - for (GLsizei i = 0; i < height; i++) { - memcpy(pDst, pSrc, width * bpp); - pDst += width * bpp; - pSrc += stride * bpp; - } - - if (resize) { - 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); - } - - free(temp); - } -} - -void Texture::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: - 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 is upconverted to RGBA_8888 - case kARGB_4444_SkColorType: - case kN32_SkColorType: - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(needSRGB); - *outType = GL_UNSIGNED_BYTE; - break; - case kGray_8_SkColorType: - *outFormat = GL_LUMINANCE; - *outInternalFormat = GL_LUMINANCE; - *outType = GL_UNSIGNED_BYTE; - break; - case kRGBA_F16_SkColorType: - if (caches.extensions().getMajorGlVersion() >= 3) { - // This format is always linear - *outFormat = GL_RGBA; - *outInternalFormat = GL_RGBA16F; - *outType = GL_HALF_FLOAT; - } else { - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(true); - *outType = GL_UNSIGNED_BYTE; - } - break; - default: - LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); - break; - } -} - -SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp sRGB) { - SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), - bitmap.info().alphaType(), - hasLinearBlending ? sRGB : nullptr)); - rgbaBitmap.eraseColor(0); - - if (bitmap.colorType() == kRGBA_F16_SkColorType) { - // Drawing RGBA_F16 onto ARGB_8888 is not supported - bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), - rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); - } else { - SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); - } - - return rgbaBitmap; -} - -bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) { - return info.colorType() == kARGB_4444_SkColorType || - (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && - info.colorSpace()->isSRGB()) || - (info.colorType() == kRGBA_F16_SkColorType && - Caches::getInstance().extensions().getMajorGlVersion() < 3); -} - -void Texture::upload(Bitmap& bitmap) { - ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); - - // We could also enable mipmapping if both bitmap dimensions are powers - // of 2 but we'd have to deal with size changes. Let's keep this simple - const bool canMipMap = mCaches.extensions().hasNPot(); - - // If the texture had mipmap enabled but not anymore, - // force a glTexImage2D to discard the mipmap levels - bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); - bool setDefaultParams = false; - - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - setDefaultParams = true; - } - - bool hasLinearBlending = mCaches.extensions().hasLinearBlending(); - bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace()); - - GLint internalFormat, format, type; - colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending, - &internalFormat, &format, &type); - - // Some devices don't support GL_RGBA16F, so we need to compare the color type - // and internal GL format to decide what to do with 16 bit bitmaps - bool rgba16fNeedsConversion = - bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F; - - // RGBA16F is always linear extended sRGB - if (internalFormat == GL_RGBA16F) { - mIsLinear = true; - } - - mConnector.reset(); - - // Alpha masks don't have color profiles - // If an RGBA16F bitmap needs conversion, we know the target will be sRGB - if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) { - SkColorSpace* colorSpace = bitmap.info().colorSpace(); - // If the bitmap is sRGB we don't need conversion - if (colorSpace != nullptr && !colorSpace->isSRGB()) { - SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); - if (!colorSpace->toXYZD50(&xyzMatrix)) { - ALOGW("Incompatible color space!"); - } else { - SkColorSpaceTransferFn fn; - if (!colorSpace->isNumericalTransferFn(&fn)) { - ALOGW("Incompatible color space, no numerical transfer function!"); - } else { - float data[16]; - xyzMatrix.asColMajorf(data); - - ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC, - fn.fD, fn.fE, fn.fF}; - ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p); - mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB())); - - // A non-sRGB color space might have a transfer function close enough to sRGB - // that we can save shader instructions by using an sRGB sampler - // This is only possible if we have hardware support for sRGB textures - if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() && - !bitmap.isHardware()) { - internalFormat = GL_SRGB8_ALPHA8; - } - } - } - } - } - - GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; - needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target); - - blend = !bitmap.isOpaque(); - mCaches.textureState().bindTexture(mTarget, mId); - - // TODO: Handle sRGB gray bitmaps - if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) { - SkBitmap skBitmap; - bitmap.getSkBitmap(&skBitmap); - sk_sp sRGB = SkColorSpace::MakeSRGB(); - SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); - uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), - rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), - rgbaBitmap.getPixels()); - } else if (bitmap.isHardware()) { - uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); - } else { - uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), - bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), - bitmap.pixels()); - } - - if (canMipMap) { - mipMap = bitmap.hasHardwareMipMap(); - if (mipMap) { - glGenerateMipmap(GL_TEXTURE_2D); - } - } - - if (setDefaultParams) { - setFilter(GL_NEAREST); - setWrap(GL_CLAMP_TO_EDGE); - } -} - -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - mId = id; - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - mConnector.reset(); - // We're wrapping an existing texture, so don't double count this memory - notifySizeChanged(0); -} - -TransferFunctionType Texture::getTransferFunctionType() const { - if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) { - const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters(); - if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) { - if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) && - MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { - if (MathUtils::areEqual(p.g, 1.0f)) { - return TransferFunctionType::None; - } - return TransferFunctionType::Gamma; - } - return TransferFunctionType::Limited; - } - return TransferFunctionType::Full; - } - return TransferFunctionType::None; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h deleted file mode 100644 index 5b7e4e261f30..000000000000 --- a/libs/hwui/Texture.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2010 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_TEXTURE_H -#define ANDROID_HWUI_TEXTURE_H - -#include "GpuMemoryTracker.h" -#include "hwui/Bitmap.h" -#include "utils/Color.h" - -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace android { - -class GraphicBuffer; - -namespace uirenderer { - -class Caches; -class UvMapper; -class Layer; - -/** - * Represents an OpenGL texture. - */ -class Texture : public GpuMemoryTracker { -public: - static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp sRGB); - static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending); - static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, - bool needSRGB, GLint* outInternalFormat, - GLint* outFormat, GLint* outType); - - explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {} - - virtual ~Texture() {} - - inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { - setWrapST(wrap, wrap, bindTexture, force); - } - - virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false); - - inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { - setFilterMinMag(filter, filter, bindTexture, force); - } - - virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false); - - /** - * Convenience method to call glDeleteTextures() on this texture's id. - */ - void deleteTexture(); - - /** - * Sets the width, height, and format of the texture along with allocating - * the texture ID. Does nothing if the width, height, and format are already - * the requested values. - * - * The image data is undefined after calling this. - */ - void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { - upload(internalFormat, width, height, format, - internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); - } - - /** - * Updates this Texture with the contents of the provided Bitmap, - * also setting the appropriate width, height, and format. It is not necessary - * to call resize() prior to this. - * - * Note this does not set the generation from the Bitmap. - */ - void upload(Bitmap& source); - - /** - * Basically glTexImage2D/glTexSubImage2D. - */ - 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 internalFormat, GLint format, - GLenum target); - - GLuint id() const { return mId; } - - uint32_t width() const { return mWidth; } - - uint32_t height() const { return mHeight; } - - GLint format() const { return mFormat; } - - GLint internalFormat() const { return mInternalFormat; } - - GLenum target() const { return mTarget; } - - /** - * Returns nullptr if this texture does not require color space conversion - * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion - * is required. - */ - constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); } - - constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; } - - TransferFunctionType getTransferFunctionType() const; - - /** - * Returns true if this texture uses a linear encoding format. - */ - constexpr bool isLinear() const { return mIsLinear; } - - /** - * Generation of the backing bitmap, - */ - uint32_t generation = 0; - /** - * Indicates whether the texture requires blending. - */ - bool blend = false; - /** - * Indicates whether this texture should be cleaned up after use. - */ - bool cleanup = false; - /** - * Optional, size of the original bitmap. - */ - uint32_t bitmapSize = 0; - /** - * Indicates whether this texture will use trilinear filtering. - */ - bool mipMap = false; - - /** - * Optional, pointer to a texture coordinates mapper. - */ - const UvMapper* uvMapper = nullptr; - - /** - * Whether or not the Texture is marked in use and thus not evictable for - * the current frame. This is reset at the start of a new frame. - */ - void* isInUse = nullptr; - -private: - // TODO: Temporarily grant private access to GlLayer, remove once - // GlLayer can be de-tangled from being a dual-purpose render target - // and external texture wrapper - friend class GlLayer; - - // Returns true if the texture layout (size, format, etc.) changed, false if it was the same - bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target); - void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); - void resetCachedParams(); - - GLuint mId = 0; - uint32_t mWidth = 0; - uint32_t mHeight = 0; - GLint mFormat = 0; - GLint mInternalFormat = 0; - GLenum mTarget = GL_NONE; - EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR; - - /* See GLES spec section 3.8.14 - * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is - * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR. - * s, t, and r wrap modes are all set to REPEAT." - */ - GLenum mWrapS = GL_REPEAT; - GLenum mWrapT = GL_REPEAT; - GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - GLenum mMagFilter = GL_LINEAR; - - // Indicates whether the content of the texture is in linear space - bool mIsLinear = false; - - Caches& mCaches; - - std::unique_ptr mConnector; -}; // struct Texture - -class AutoTexture { -public: - explicit AutoTexture(Texture* texture) : texture(texture) {} - ~AutoTexture() { - if (texture && texture->cleanup) { - texture->deleteTexture(); - delete texture; - } - } - - Texture* const texture; -}; // class AutoTexture - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp deleted file mode 100644 index 30fba7ae7d9b..000000000000 --- a/libs/hwui/VkLayer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 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 "VkLayer.h" - -#include "renderstate/RenderState.h" - -#include -#include - -namespace android { -namespace uirenderer { - -void VkLayer::updateTexture() { - sk_sp surface; - SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType); - surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info); - surface->getCanvas()->clear(SK_ColorBLUE); - mImage = surface->makeImageSnapshot(); -} - -void VkLayer::onVkContextDestroyed() { - mImage = nullptr; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h deleted file mode 100644 index e9664d04b7a5..000000000000 --- a/libs/hwui/VkLayer.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include "Layer.h" - -#include - -namespace android { -namespace uirenderer { -/** - * A layer has dimensions and is backed by a VkImage. - */ -class VkLayer : public Layer { -public: - VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode) - , mWidth(layerWidth) - , mHeight(layerHeight) - , mBlend(blend) {} - - virtual ~VkLayer() {} - - uint32_t getWidth() const override { return mWidth; } - - uint32_t getHeight() const override { return mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - mWidth = width; - mHeight = height; - } - - void setBlend(bool blend) override { mBlend = blend; } - - bool isBlend() const override { return mBlend; } - - sk_sp getImage() { return mImage; } - - void updateTexture(); - - // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to - // destroy any VkImages that were made with that context. - void onVkContextDestroyed(); - -private: - int mWidth; - int mHeight; - bool mBlend; - - sk_sp mImage; - -}; // struct VkLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index a7d37f8aa45c..3939696692d2 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -15,11 +15,11 @@ */ #include "Bitmap.h" -#include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" +#include #include diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index c41f6a6f0ee6..fb66b50f0159 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -15,8 +15,6 @@ */ #include "LayerDrawable.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "GrBackendSurface.h" #include "SkColorFilter.h" @@ -41,35 +39,14 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer return false; } // transform the matrix based on the layer - SkMatrix layerTransform; - layer->getTransform().copyTo(layerTransform); - sk_sp layerImage; + SkMatrix layerTransform = layer->getTransform(); + sk_sp layerImage = layer->getImage(); const int layerWidth = layer->getWidth(); const int layerHeight = layer->getHeight(); - if (layer->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layer); - GrGLTextureInfo externalTexture; - externalTexture.fTarget = glLayer->getRenderTarget(); - externalTexture.fID = glLayer->getTextureId(); - // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't - // expose that info we use it as our default. Further, given that we only use this texture - // as a source this will not impact how Skia uses the texture. The only potential affect - // this is anticipated to have is that for some format types if we are not bound as an OES - // texture we may get invalid results for SKP capture if we read back the texture. - externalTexture.fFormat = GL_RGBA8; - GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture); - layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, - kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); - } else { - SkASSERT(layer->getApi() == Layer::Api::Vulkan); - VkLayer* vkLayer = static_cast(layer); - canvas->clear(SK_ColorGREEN); - layerImage = vkLayer->getImage(); - } if (layerImage) { SkMatrix textureMatrixInv; - layer->getTexTransform().copyTo(textureMatrixInv); + textureMatrixInv = layer->getTexTransform(); // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed // use bottom left origin and remove flipV and invert transformations. SkMatrix flipV; @@ -95,6 +72,9 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(layer->getColorSpaceWithFilter()); + if (layer->getForceFilter()) { + paint.setFilterQuality(kLow_SkFilterQuality); + } const bool nonIdentityMatrix = !matrix.isIdentity(); if (nonIdentityMatrix) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 78f5a71dee3b..2ae37233098e 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,7 +17,6 @@ #include "SkiaOpenGLPipeline.h" #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "LayerDrawable.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" @@ -187,18 +186,9 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, - bool blend) { - GlLayer* layer = - new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); - layer->generateTexture(); - return layer; -} - DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaOpenGLPipeline::onStop() { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index b2519fe59891..5f2eee4523fc 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -20,7 +20,6 @@ #include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" @@ -114,16 +113,10 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, - bool blend) { - return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); -} - DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { mVkManager.initialize(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaVulkanPipeline::onStop() {} diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp deleted file mode 100644 index 3a6efb833c47..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 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 "renderstate/PixelBufferState.h" - -namespace android { -namespace uirenderer { - -PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {} - -bool PixelBufferState::bind(GLuint buffer) { - if (mCurrentPixelBuffer != buffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); - mCurrentPixelBuffer = buffer; - return true; - } - return false; -} - -bool PixelBufferState::unbind() { - if (mCurrentPixelBuffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - mCurrentPixelBuffer = 0; - return true; - } - return false; -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h deleted file mode 100644 index f7ae6c575f6a..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2015 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 RENDERSTATE_PIXELBUFFERSTATE_H -#define RENDERSTATE_PIXELBUFFERSTATE_H - -#include - -namespace android { -namespace uirenderer { - -class PixelBufferState { - friend class Caches; // TODO: move to RenderState -public: - bool bind(GLuint buffer); - bool unbind(); - -private: - PixelBufferState(); - GLuint mCurrentPixelBuffer; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 3be84f588a20..b524bcb096da 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,8 +16,6 @@ #include "renderstate/RenderState.h" #include #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "Snapshot.h" #include "renderthread/CanvasContext.h" @@ -39,44 +37,11 @@ RenderState::RenderState(renderthread::RenderThread& thread) RenderState::~RenderState() { } -void RenderState::onGLContextCreated() { +void RenderState::onContextCreated() { GpuMemoryTracker::onGpuContextCreated(); - - // This is delayed because the first access of Caches makes GL calls - if (!mCaches) { - mCaches = &Caches::createInstance(*this); - } - mCaches->init(); } -static void layerLostGlContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, - "layerLostGlContext on non GL layer"); - static_cast(layer)->onGlContextLost(); -} - -void RenderState::onGLContextDestroyed() { - // TODO: reset all cached state in state objects - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - - mCaches->terminate(); - - destroyLayersInUpdater(); - GpuMemoryTracker::onGpuContextDestroyed(); -} - -void RenderState::onVkContextCreated() { - GpuMemoryTracker::onGpuContextCreated(); -} - -static void layerDestroyedVkContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan, - "layerLostVkContext on non Vulkan layer"); - static_cast(layer)->onVkContextDestroyed(); -} - -void RenderState::onVkContextDestroyed() { - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); +void RenderState::onContextDestroyed() { destroyLayersInUpdater(); GpuMemoryTracker::onGpuContextDestroyed(); } @@ -85,10 +50,6 @@ GrContext* RenderState::getGrContext() const { return mRenderThread.getGrContext(); } -void RenderState::flush(Caches::FlushMode mode) { - if (mCaches) mCaches->flush(mode); -} - void RenderState::onBitmapDestroyed(uint32_t pixelRefId) { // DEAD CODE } @@ -126,42 +87,6 @@ void RenderState::deleteFramebuffer(GLuint fbo) { glDeleteFramebuffers(1, &fbo); } -void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { - if (mode == DrawGlInfo::kModeProcessNoContext) { - // If there's no context we don't need to interrupt as there's - // no gl state to save/restore - (*functor)(mode, info); - } else { - interruptForFunctorInvoke(); - (*functor)(mode, info); - resumeFromFunctorInvoke(); - } -} - -void RenderState::interruptForFunctorInvoke() { - mCaches->textureState().resetActiveTexture(); - debugOverdraw(false, false); - // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glDisable(GL_FRAMEBUFFER_SRGB_EXT); - } -} - -void RenderState::resumeFromFunctorInvoke() { - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glEnable(GL_FRAMEBUFFER_SRGB_EXT); - } - - glViewport(0, 0, mViewportWidth, mViewportHeight); - glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - debugOverdraw(false, false); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - mCaches->textureState().activateTexture(0); - mCaches->textureState().resetBoundTextures(); -} - void RenderState::debugOverdraw(bool enable, bool clear) { // DEAD CODE } @@ -190,5 +115,9 @@ void RenderState::dump() { // DEAD CODE } +renderthread::RenderThread& RenderState::getRenderThread() { + return mRenderThread; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 97785a46dcd7..f39aa4b96547 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,8 +16,6 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H -#include "Caches.h" -#include "renderstate/PixelBufferState.h" #include "utils/Macros.h" #include @@ -34,7 +32,6 @@ class GrContext; namespace android { namespace uirenderer { -class Caches; class Layer; class DeferredLayerUpdater; @@ -44,22 +41,16 @@ class CanvasContext; class RenderThread; } -// TODO: Replace Cache's GL state tracking with this. For now it's more a thin // wrapper of Caches for users to migrate to. class RenderState { PREVENT_COPY_AND_ASSIGN(RenderState); friend class renderthread::RenderThread; - friend class Caches; friend class renderthread::CacheManager; public: - void onGLContextCreated(); - void onGLContextDestroyed(); + void onContextCreated(); + void onContextDestroyed(); - void onVkContextCreated(); - void onVkContextDestroyed(); - - void flush(Caches::FlushMode flushMode); void onBitmapDestroyed(uint32_t pixelRefId); void setViewport(GLsizei width, GLsizei height); @@ -70,8 +61,6 @@ public: GLuint createFramebuffer(); void deleteFramebuffer(GLuint fbo); - void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); - void debugOverdraw(bool enable, bool clear); void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } @@ -101,16 +90,15 @@ public: void dump(); + renderthread::RenderThread& getRenderThread(); + private: - void interruptForFunctorInvoke(); - void resumeFromFunctorInvoke(); void destroyLayersInUpdater(); explicit RenderState(renderthread::RenderThread& thread); ~RenderState(); renderthread::RenderThread& mRenderThread; - Caches* mCaches = nullptr; std::set mActiveLayers; std::set mActiveLayerUpdaters; diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp deleted file mode 100644 index 470b4f5de97f..000000000000 --- a/libs/hwui/renderstate/TextureState.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2015 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 "renderstate/TextureState.h" - -#include "Caches.h" -#include "utils/TraceUtils.h" - -#include -#include -#include -#include - -namespace android { -namespace uirenderer { - -// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is -static const int SHADOW_LUT_SIZE = 128; - -// Must define as many texture units as specified by kTextureUnitsCount -const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3}; - -TextureState::TextureState() : mTextureUnit(0) { - glActiveTexture(kTextureUnits[0]); - resetBoundTextures(); - - GLint maxTextureUnits; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, - "At least %d texture units are required!", kTextureUnitsCount); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); -} - -TextureState::~TextureState() { - if (mShadowLutTexture != nullptr) { - mShadowLutTexture->deleteTexture(); - } -} - -/** - * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to - * darkness at that spot. Input values of 0->1 should be mapped within the same - * range, but can affect the curve for a different visual falloff. - * - * This is used to populate the shadow LUT texture for quick lookup in the - * shadow shader. - */ -static float computeShadowOpacity(float ratio) { - // exponential falloff function provided by UX - float val = 1 - ratio; - return exp(-val * val * 4.0) - 0.018; -} - -void TextureState::constructTexture(Caches& caches) { - if (mShadowLutTexture == nullptr) { - mShadowLutTexture.reset(new Texture(caches)); - - unsigned char bytes[SHADOW_LUT_SIZE]; - for (int i = 0; i < SHADOW_LUT_SIZE; i++) { - float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f); - bytes[i] = computeShadowOpacity(inputRatio) * 255; - } - mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes); - mShadowLutTexture->setFilter(GL_LINEAR); - mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE); - } -} - -void TextureState::activateTexture(GLuint textureUnit) { - LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, - "Tried to use texture unit index %d, only %d exist", textureUnit, - kTextureUnitsCount); - if (mTextureUnit != textureUnit) { - glActiveTexture(kTextureUnits[textureUnit]); - mTextureUnit = textureUnit; - } -} - -void TextureState::resetActiveTexture() { - mTextureUnit = -1; -} - -void TextureState::bindTexture(GLuint texture) { - if (mBoundTextures[mTextureUnit] != texture) { - glBindTexture(GL_TEXTURE_2D, texture); - mBoundTextures[mTextureUnit] = texture; - } -} - -void TextureState::bindTexture(GLenum target, GLuint texture) { - if (target == GL_TEXTURE_2D) { - bindTexture(texture); - } else { - // GLConsumer directly calls glBindTexture() with - // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target - // since the cached state could be stale - glBindTexture(target, texture); - } -} - -void TextureState::deleteTexture(GLuint texture) { - // When glDeleteTextures() is called on a currently bound texture, - // OpenGL ES specifies that the texture is then considered unbound - // Consider the following series of calls: - // - // glGenTextures -> creates texture name 2 - // glBindTexture(2) - // glDeleteTextures(2) -> 2 is now unbound - // glGenTextures -> can return 2 again - // - // If we don't call glBindTexture(2) after the second glGenTextures - // call, any texture operation will be performed on the default - // texture (name=0) - - unbindTexture(texture); - - glDeleteTextures(1, &texture); -} - -void TextureState::resetBoundTextures() { - for (int i = 0; i < kTextureUnitsCount; i++) { - mBoundTextures[i] = 0; - } -} - -void TextureState::unbindTexture(GLuint texture) { - for (int i = 0; i < kTextureUnitsCount; i++) { - if (mBoundTextures[i] == texture) { - mBoundTextures[i] = 0; - } - } -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h deleted file mode 100644 index f1996d431fa2..000000000000 --- a/libs/hwui/renderstate/TextureState.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2015 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 RENDERSTATE_TEXTURESTATE_H -#define RENDERSTATE_TEXTURESTATE_H - -#include "Texture.h" -#include "Vertex.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -class Texture; - -class TextureState { - friend class Caches; // TODO: move to RenderState -public: - void constructTexture(Caches& caches); - - /** - * Activate the specified texture unit. The texture unit must - * be specified using an integer number (0 for GL_TEXTURE0 etc.) - */ - void activateTexture(GLuint textureUnit); - - /** - * Invalidate the cached value of the active texture unit. - */ - void resetActiveTexture(); - - /** - * Binds the specified texture as a GL_TEXTURE_2D texture. - * All texture bindings must be performed with this method or - * bindTexture(GLenum, GLuint). - */ - void bindTexture(GLuint texture); - - /** - * Binds the specified texture with the specified render target. - * All texture bindings must be performed with this method or - * bindTexture(GLuint). - */ - void bindTexture(GLenum target, GLuint texture); - - /** - * Deletes the specified texture and clears it from the cache - * of bound textures. - * All textures must be deleted using this method. - */ - void deleteTexture(GLuint texture); - - /** - * Signals that the cache of bound textures should be cleared. - * Other users of the context may have altered which textures are bound. - */ - void resetBoundTextures(); - - /** - * Clear the cache of bound textures. - */ - void unbindTexture(GLuint texture); - - Texture* getShadowLutTexture() { return mShadowLutTexture.get(); } - -private: - // total number of texture units available for use - static const int kTextureUnitsCount = 4; - - TextureState(); - ~TextureState(); - GLuint mTextureUnit; - - // Caches texture bindings for the GL_TEXTURE_2D target - GLuint mBoundTextures[kTextureUnitsCount]; - - std::unique_ptr mShadowLutTexture; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index bec80b1e6011..c45eedad775c 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -21,6 +21,7 @@ #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" +#include "Properties.h" #include "renderstate/RenderState.h" #include @@ -214,11 +215,12 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat(" Layer Info:\n"); } + const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL + ? "GlLayer" : "VkLayer"; size_t layerMemoryTotal = 0; for (std::set::iterator it = renderState->mActiveLayers.begin(); it != renderState->mActiveLayers.end(); it++) { const Layer* layer = *it; - const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5d7252304bf2..8b07d1dadeb6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,7 +18,6 @@ #include #include "AnimationContext.h" -#include "Caches.h" #include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" @@ -495,13 +494,6 @@ void CanvasContext::draw() { } GpuMemoryTracker::onFrameCompleted(); -#ifdef BUGREPORT_FONT_CACHE_USAGE - auto renderType = Properties::getRenderPipelineType(); - if (RenderPipelineType::OpenGL == renderType) { - Caches& caches = Caches::getInstance(); - caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); - } -#endif } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index cd21822df5b1..5f8d7ad3373a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "utils/StringUtils.h" @@ -464,6 +465,109 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } +status_t EglManager::fenceWait(sp& fence) { + if (!hasEglContext()) { + ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Block GPU on the fence. + // Create an EGLSyncKHR from the current fence. + int fenceFd = fence->dup(); + if (fenceFd == -1) { + ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, + EGL_NONE + }; + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(mEglDisplay, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(mEglDisplay, sync); + if (eglErr != EGL_SUCCESS) { + ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + // Block CPU on the fence. + status_t err = fence->waitForever("EglManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); + return err; + } + } + return OK; +} + +status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, + sp& nativeFence) { + if (!hasEglContext()) { + ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync); + eglDestroySyncKHR(mEglDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGE("EglManager::createReleaseFence: error dup'ing native fence " + "fd: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + nativeFence = new Fence(fenceFd); + *eglFence = EGL_NO_SYNC_KHR; + } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { + if (*eglFence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000); + if (result == EGL_FALSE) { + ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(mEglDisplay, *eglFence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr); + if (*eglFence == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + } + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 8e8bb8b68a1c..507673adf26e 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -17,8 +17,10 @@ #define EGLMANAGER_H #include +#include #include #include +#include #include #include @@ -66,6 +68,14 @@ public: EGLDisplay eglDisplay() const { return mEglDisplay; } + // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension + // support is missing, block the CPU on the fence. + status_t fenceWait(sp& fence); + + // Creates a fence that is signaled, when all the pending GL commands are flushed. + // Depending on installed extensions, the result is either Android native fence or EGL fence. + status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp& nativeFence); + private: void initExtensions(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index c1284ec02655..65f95ad3f0d4 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -178,7 +178,7 @@ void RenderThread::requireGlContext() { return; } mEglManager->initialize(); - renderState().onGLContextCreated(); + renderState().onContextCreated(); #ifdef HWUI_GLES_WRAP_ENABLED debug::GlesDriver* driver = debug::GlesDriver::get(); @@ -200,7 +200,7 @@ void RenderThread::requireGlContext() { void RenderThread::destroyGlContext() { if (mEglManager->hasEglContext()) { setGrContext(nullptr); - renderState().onGLContextDestroyed(); + renderState().onContextDestroyed(); mEglManager->destroy(); } } diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 1517f579a084..0c49dc03425e 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -40,7 +40,7 @@ namespace renderthread { VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { - mRenderThread.renderState().onVkContextDestroyed(); + mRenderThread.renderState().onContextDestroyed(); mRenderThread.setGrContext(nullptr); if (VK_NULL_HANDLE != mCommandPool) { @@ -404,7 +404,7 @@ void VulkanManager::initialize() { mSwapBehavior = SwapBehavior::BufferAge; } - mRenderThread.renderState().onVkContextCreated(); + mRenderThread.renderState().onContextCreated(); } // Returns the next BackbufferInfo to use for the next draw. The function will make sure all @@ -981,6 +981,22 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } +status_t VulkanManager::fenceWait(sp& fence) { + //TODO: Insert a wait on fence command into the Vulkan command buffer. + // Block CPU on the fence. + status_t err = fence->waitForever("VulkanManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); + return err; + } + return OK; +} + +status_t VulkanManager::createReleaseFence(sp& nativeFence) { + //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 5524c39d7a0c..ebc11a50685e 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -23,6 +23,8 @@ #include #include +#include +#include #include class GrVkExtensions; @@ -110,6 +112,12 @@ public: // Presents the current VkImage. void swapBuffers(VulkanSurface* surface); + // Inserts a wait on fence command into the Vulkan command buffer. + status_t fenceWait(sp& fence); + + // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. + status_t createReleaseFence(sp& nativeFence); + private: friend class RenderThread; diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp new file mode 100644 index 000000000000..c8220c6cb0d4 --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2018 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include "EGLConsumer.h" +#include "SurfaceTexture.h" + +#include +#include +#include + +#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" +#define EGL_PROTECTED_CONTENT_EXT 0x32C0 + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +static const struct { + uint32_t width, height; + char const* bits; +} kDebugData = {15, 12, + "_______________" + "_______________" + "_____XX_XX_____" + "__X_X_____X_X__" + "__X_XXXXXXX_X__" + "__XXXXXXXXXXX__" + "___XX_XXX_XX___" + "____XXXXXXX____" + "_____X___X_____" + "____X_____X____" + "_______________" + "_______________"}; + +Mutex EGLConsumer::sStaticInitLock; +sp EGLConsumer::sReleasedTexImageBuffer; + +static bool hasEglProtectedContentImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); + bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); + bool atEnd = (cropExtLen + 1) < extsLen && + !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); + bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} + +static bool hasEglProtectedContent() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglProtectedContentImpl(); + return hasIt; +} + +EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} + +status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + return err; + } + + BufferItem item; + + // Acquire the next buffer. + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + err = st.acquireBufferLocked(&item, 0); + if (err != NO_ERROR) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // We always bind the texture even if we don't update its contents. + EGC_LOGV("updateTexImage: no buffers were available"); + glBindTexture(st.mTexTarget, st.mTexName); + err = NO_ERROR; + } else { + EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); + } + return err; + } + + // Release the previous buffer. + err = updateAndReleaseLocked(item, nullptr, st); + if (err != NO_ERROR) { + // We always bind the texture. + glBindTexture(st.mTexTarget, st.mTexName); + return err; + } + + // Bind the new buffer to the GL texture, and wait until it's ready. + return bindTextureImageLocked(st); +} + +status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = NO_ERROR; + + // if we're detached, no need to validate EGL's state -- we won't use it. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + err = checkAndUpdateEglStateLocked(st, true); + if (err != NO_ERROR) { + return err; + } + } + + // Update the EGLConsumer state. + int buf = st.mCurrentTexture; + if (buf != BufferQueue::INVALID_BUFFER_SLOT) { + EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); + + // if we're detached, we just use the fence that was created in detachFromContext() + // so... basically, nothing more to do here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // Do whatever sync ops we need to do before releasing the slot. + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); + return err; + } + } + + err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { + EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); + return err; + } + + if (mReleasedTexImage == nullptr) { + mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + } + + st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureImage = mReleasedTexImage; + st.mCurrentCrop.makeInvalid(); + st.mCurrentTransform = 0; + st.mCurrentTimestamp = 0; + st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; + st.mCurrentFence = Fence::NO_FENCE; + st.mCurrentFenceTime = FenceTime::NO_FENCE; + + // detached, don't touch the texture (and we may not even have an + // EGLDisplay here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // This binds a dummy buffer (mReleasedTexImage). + status_t result = bindTextureImageLocked(st); + if (result != NO_ERROR) { + return result; + } + } + } + + return NO_ERROR; +} + +sp EGLConsumer::getDebugTexImageBuffer() { + Mutex::Autolock _l(sStaticInitLock); + if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { + // The first time, create the debug texture in case the application + // continues to use it. + sp buffer = new GraphicBuffer( + kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); + uint32_t* bits; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast(&bits)); + uint32_t stride = buffer->getStride(); + uint32_t height = buffer->getHeight(); + memset(bits, 0, stride * height * 4); + for (uint32_t y = 0; y < kDebugData.height; y++) { + for (uint32_t x = 0; x < kDebugData.width; x++) { + bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 + : 0xFFFFFFFF; + } + bits += stride; + } + buffer->unlock(); + sReleasedTexImageBuffer = buffer; + } + return sReleasedTexImageBuffer; +} + +void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior EglImage created is using a stale buffer. This + // replaces any old EglImage with a new one (using the new buffer). + int slot = item->mSlot; + if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } +} + +void EGLConsumer::onReleaseBufferLocked(int buf) { + mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st) { + status_t err = NO_ERROR; + + int slot = item.mSlot; + + if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { + EGC_LOGE( + "updateAndRelease: EGLConsumer is not attached to an OpenGL " + "ES context"); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return INVALID_OPERATION; + } + + // Confirm state. + err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return err; + } + + // Ensure we have a valid EglImageKHR for the slot, creating an EglImage + // if nessessary, for the gralloc buffer currently in the slot in + // ConsumerBase. + // We may have to do this even when item.mGraphicBuffer == NULL (which + // means the buffer was previously acquired). + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, + slot); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return UNKNOWN_ERROR; + } + + // Do whatever sync ops we need to do before releasing the old slot. + if (slot != st.mCurrentTexture) { + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + // Release the buffer we just acquired. It's not safe to + // release the old buffer, so instead we just drop the new frame. + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + return err; + } + } + + EGC_LOGV( + "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, + mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, + slot, st.mSlots[slot].mGraphicBuffer->handle); + + // Hang onto the pointer so that it isn't freed in the call to + // releaseBufferLocked() if we're in shared buffer mode and both buffers are + // the same. + sp nextTextureImage = mEglSlots[slot].mEglImage; + + // release old buffer + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (pendingRelease == nullptr) { + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, + mEglSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), + status); + err = status; + // keep going, with error raised [?] + } + } else { + pendingRelease->currentTexture = st.mCurrentTexture; + pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); + pendingRelease->display = mEglDisplay; + pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; + pendingRelease->isPending = true; + } + } + + // Update the EGLConsumer state. + st.mCurrentTexture = slot; + mCurrentTextureImage = nextTextureImage; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + + st.computeCurrentTransformMatrixLocked(); + + return err; +} + +status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { + if (mEglDisplay == EGL_NO_DISPLAY) { + ALOGE("bindTextureImage: invalid display"); + return INVALID_OPERATION; + } + + GLenum error; + while ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); + } + + glBindTexture(st.mTexTarget, st.mTexName); + if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { + EGC_LOGE("bindTextureImage: no currently-bound texture"); + return NO_INIT; + } + + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + + // In the rare case that the display is terminated and then initialized + // again, we can't detect that the display changed (it didn't), but the + // image is invalid. In this case, repeat the exact same steps while + // forcing the creation of a new image. + if ((error = glGetError()) != GL_NO_ERROR) { + glBindTexture(st.mTexTarget, st.mTexName); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); + if (result != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + if ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); + return UNKNOWN_ERROR; + } + } + + // Wait for the new buffer to be ready. + return doGLFenceWaitLocked(st); +} + +status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (!contextCheck) { + // if this is the first time we're called, mEglDisplay/mEglContext have + // never been set, so don't error out (below). + if (mEglDisplay == EGL_NO_DISPLAY) { + mEglDisplay = dpy; + } + if (mEglContext == EGL_NO_CONTEXT) { + mEglContext = ctx; + } + } + + if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); + return INVALID_OPERATION; + } + + mEglDisplay = dpy; + mEglContext = ctx; + return NO_ERROR; +} + +status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { + EGC_LOGE("detachFromContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { + EGC_LOGE("detachFromContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { + status_t err = syncForReleaseLocked(dpy, st); + if (err != OK) { + return err; + } + + glDeleteTextures(1, &st.mTexName); + } + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + + return OK; +} + +status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { + // Initialize mCurrentTextureImage if there is a current buffer from past attached state. + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + if (!mEglSlots[slot].mEglImage.get()) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } + mCurrentTextureImage = mEglSlots[slot].mEglImage; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (dpy == EGL_NO_DISPLAY) { + EGC_LOGE("attachToContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (ctx == EGL_NO_CONTEXT) { + EGC_LOGE("attachToContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + // We need to bind the texture regardless of whether there's a current + // buffer. + glBindTexture(st.mTexTarget, GLuint(tex)); + + mEglDisplay = dpy; + mEglContext = ctx; + st.mTexName = tex; + st.mOpMode = SurfaceTexture::OpMode::attachedToGL; + + if (mCurrentTextureImage != nullptr) { + // This may wait for a buffer a second time. This is likely required if + // this is a different context, since otherwise the wait could be skipped + // by bouncing through another context. For the same context the extra + // wait is redundant. + status_t err = bindTextureImageLocked(st); + if (err != NO_ERROR) { + return err; + } + } + + return OK; +} + +status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { + EGC_LOGV("syncForReleaseLocked"); + + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); + eglDestroySyncKHR(dpy, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + EGC_LOGE( + "syncForReleaseLocked: error dup'ing native fence " + "fd: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + sp fence(new Fence(fenceFd)); + status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, + mCurrentTextureImage->graphicBuffer(), fence); + if (err != OK) { + EGC_LOGE( + "syncForReleaseLocked: error adding release fence: " + "%s (%d)", + strerror(-err), err); + return err; + } + } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { + EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; + if (fence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (result == EGL_FALSE) { + EGC_LOGE( + "syncForReleaseLocked: error waiting for previous " + "fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + EGC_LOGE( + "syncForReleaseLocked: timeout waiting for previous " + "fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(dpy, fence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); + if (fence == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + mEglSlots[st.mCurrentTexture].mEglFence = fence; + } + } + + return OK; +} + +status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + EGC_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (st.mCurrentFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = st.mCurrentFence->dup(); + if (fenceFd == -1) { + EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); + if (err != NO_ERROR) { + EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +void EGLConsumer::onFreeBufferLocked(int slotIndex) { + mEglSlots[slotIndex].mEglImage.clear(); +} + +void EGLConsumer::onAbandonLocked() { + mCurrentTextureImage.clear(); +} + +EGLConsumer::EglImage::EglImage(sp graphicBuffer) + : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} + +EGLConsumer::EglImage::~EglImage() { + if (mEglImage != EGL_NO_IMAGE_KHR) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("~EglImage: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + } +} + +status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { + // If there's an image and it's no longer valid, destroy it. + bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; + bool displayInvalid = mEglDisplay != eglDisplay; + if (haveImage && (displayInvalid || forceCreation)) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("createIfNeeded: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + mEglImage = EGL_NO_IMAGE_KHR; + mEglDisplay = EGL_NO_DISPLAY; + } + + // If there's no image, create one. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = eglDisplay; + mEglImage = createImage(mEglDisplay, mGraphicBuffer); + } + + // Fail if we can't create a valid image. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = EGL_NO_DISPLAY; + const sp& buffer = mGraphicBuffer; + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), + buffer->getPixelFormat()); + return UNKNOWN_ERROR; + } + + return OK; +} + +void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { + glEGLImageTargetTexture2DOES(texTarget, static_cast(mEglImage)); +} + +EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, + const sp& graphicBuffer) { + EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); + const bool createProtectedImage = + (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, + EGL_TRUE, + createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + createProtectedImage ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + eglInitialize(dpy, nullptr, nullptr); + EGLImageKHR image = + eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + if (image == EGL_NO_IMAGE_KHR) { + EGLint error = eglGetError(); + ALOGE("error creating EGLImage: %#x", error); + eglTerminate(dpy); + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h new file mode 100644 index 000000000000..eccb08298f6f --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +namespace android { + +class SurfaceTexture; + +/* + * EGLConsumer implements the parts of SurfaceTexture that deal with + * textures attached to an GL context. + */ +class EGLConsumer { +public: + EGLConsumer(); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(SurfaceTexture& st); + + /* + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(SurfaceTexture& st); + + /** + * detachFromContext detaches the EGLConsumer from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a EGLConsumer from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a EGLConsumer has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the EGLConsumer is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(SurfaceTexture& st); + + /** + * attachToContext attaches a EGLConsumer that is currently in the + * 'detached' state to the current OpenGL ES context. A EGLConsumer is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * EGLConsumer that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex, SurfaceTexture& st); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + + /** + * onAbandonLocked amends the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + void onAbandonLocked(); + +protected: + struct PendingRelease { + PendingRelease() + : isPending(false) + , currentTexture(-1) + , graphicBuffer() + , display(nullptr) + , fence(nullptr) {} + + bool isPending; + int currentTexture; + sp graphicBuffer; + EGLDisplay display; + EGLSyncKHR fence; + }; + + /** + * This releases the buffer in the slot referenced by mCurrentTexture, + * then updates state to refer to the BufferItem, which must be a + * newly-acquired buffer. If pendingRelease is not null, the parameters + * which would have been passed to releaseBufferLocked upon the successful + * completion of the method will instead be returned to the caller, so that + * it may call releaseBufferLocked itself later. + */ + status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st); + + /** + * Binds mTexName and the current buffer to mTexTarget. Uses + * mCurrentTexture if it's set, mCurrentTextureImage if not. If the + * bind succeeds, this calls doGLFenceWait. + */ + status_t bindTextureImageLocked(SurfaceTexture& st); + + /** + * Gets the current EGLDisplay and EGLContext values, and compares them + * to mEglDisplay and mEglContext. If the fields have been previously + * set, the values must match; if not, the fields are set to the current + * values. + * The contextCheck argument is used to ensure that a GL context is + * properly set; when set to false, the check is not performed. + */ + status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); + + /** + * EglImage is a utility class for tracking and creating EGLImageKHRs. There + * is primarily just one image per slot, but there is also special cases: + * - For releaseTexImage, we use a debug image (mReleasedTexImage) + * - After freeBuffer, we must still keep the current image/buffer + * Reference counting EGLImages lets us handle all these cases easily while + * also only creating new EGLImages from buffers when required. + */ + class EglImage : public LightRefBase { + public: + EglImage(sp graphicBuffer); + + /** + * createIfNeeded creates an EGLImage if required (we haven't created + * one yet, or the EGLDisplay or crop-rect has changed). + */ + status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); + + /** + * This calls glEGLImageTargetTexture2DOES to bind the image to the + * texture in the specified texture target. + */ + void bindToTextureTarget(uint32_t texTarget); + + const sp& graphicBuffer() { return mGraphicBuffer; } + const native_handle* graphicBufferHandle() { + return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; + } + + private: + // Only allow instantiation using ref counting. + friend class LightRefBase; + virtual ~EglImage(); + + // createImage creates a new EGLImage from a GraphicBuffer. + EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer); + + // Disallow copying + EglImage(const EglImage& rhs); + void operator=(const EglImage& rhs); + + // mGraphicBuffer is the buffer that was used to create this image. + sp mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEGLDisplay is the EGLDisplay that was used to create mEglImage. + EGLDisplay mEglDisplay; + + // mCropRect is the crop rectangle passed to EGL when mEglImage + // was created. + Rect mCropRect; + }; + + /** + * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command + * stream to ensure that it is safe for future OpenGL ES commands to + * access the current texture buffer. + */ + status_t doGLFenceWaitLocked(SurfaceTexture& st) const; + + /** + * syncForReleaseLocked performs the synchronization needed to release the + * current slot from an OpenGL ES context. If needed it will set the + * current slot's fence to guard against a producer accessing the buffer + * before the outstanding accesses have completed. + */ + status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); + + /** + * returns a graphic buffer used when the texture image has been released + */ + static sp getDebugTexImageBuffer(); + + /** + * The default consumer usage flags that EGLConsumer always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the EGLConsumer user. In particular, EGLConsumer will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentTextureImage is the EglImage/buffer of the current texture. It's + * possible that this buffer is not associated with any buffer slot, so we + * must track it separately in order to support the getCurrentBuffer method. + */ + sp mCurrentTextureImage; + + /** + * EGLSlot contains the information and object references that + * EGLConsumer maintains about a BufferQueue buffer slot. + */ + struct EglSlot { + EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + /** + * mEglImage is the EGLImage created from mGraphicBuffer. + */ + sp mEglImage; + + /** + * mFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. It is initialized + * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + * on a compile-time option) set to a new sync object in updateTexImage. + */ + EGLSyncKHR mEglFence; + }; + + /** + * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently + * associated. It is intialized to EGL_NO_DISPLAY and gets set to the + * current display when updateTexImage is called for the first time and when + * attachToContext is called. + */ + EGLDisplay mEglDisplay; + + /** + * mEglContext is the OpenGL ES context with which this EGLConsumer is + * currently associated. It is initialized to EGL_NO_CONTEXT and gets set + * to the current GL context when updateTexImage is called for the first + * time and when attachToContext is called. + */ + EGLContext mEglContext; + + /** + * mEGLSlots stores the buffers that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; + + /** + * protects static initialization + */ + static Mutex sStaticInitLock; + + /** + * mReleasedTexImageBuffer is a dummy buffer used when in single buffer + * mode and releaseTexImage() has been called + */ + static sp sReleasedTexImageBuffer; + sp mReleasedTexImage; +}; + +}; // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp new file mode 100644 index 000000000000..c86cd962ebed --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 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 "ImageConsumer.h" +#include +#include "Properties.h" +#include "SurfaceTexture.h" +#include "renderstate/RenderState.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "renderthread/VulkanManager.h" + +// Macro for including the SurfaceTexture name in log messages +#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +namespace android { + +void ImageConsumer::onFreeBufferLocked(int slotIndex) { + mImageSlots[slotIndex].mImage.reset(); +} + +void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. + if (item->mGraphicBuffer != nullptr) { + mImageSlots[item->mSlot].mImage.reset(); + } +} + +void ImageConsumer::onReleaseBufferLocked(int buf) { + mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer) { + if (!mImage.get()) { + mImage = graphicBuffer.get() + ? SkImage::MakeFromAHardwareBuffer( + reinterpret_cast(graphicBuffer.get()), + kPremul_SkAlphaType, SkColorSpace::MakeSRGB()) + : nullptr; + } +} + +sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, + uirenderer::RenderState& renderState) { + BufferItem item; + status_t err; + err = st.acquireBufferLocked(&item, 0); + if (err != OK) { + if (err != BufferQueue::NO_BUFFER_AVAILABLE) { + IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } else { + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + *queueEmpty = true; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; + } + } + return nullptr; + } + + int slot = item.mSlot; + if (item.mFence->isValid()) { + // Wait on the producer fence for the buffer to be ready. + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); + } else { + err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence); + } + if (err != OK) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Release old buffer. + if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { + // If needed, set the released slot's fence to guard against a producer accessing the + // buffer before the outstanding accesses have completed. + sp releaseFence; + EGLDisplay display = EGL_NO_DISPLAY; + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + auto& eglManager = renderState.getRenderThread().eglManager(); + display = eglManager.eglDisplay(); + err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence, + releaseFence); + } else { + err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence); + } + if (OK != err) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + + if (releaseFence.get()) { + status_t err = st.addReleaseFenceLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); + if (err != OK) { + IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Finally release the old buffer. + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, + mImageSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); + err = status; + // Keep going, with error raised. + } + } + + // Update the state. + st.mCurrentTexture = slot; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + st.computeCurrentTransformMatrixLocked(); + + *queueEmpty = false; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; +} + +} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h new file mode 100644 index 000000000000..31ee8db52874 --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + +namespace android { + +namespace uirenderer { +class RenderState; +} + +class SurfaceTexture; + +/* + * ImageConsumer implements the parts of SurfaceTexture that deal with + * images consumed by HWUI view system. + */ +class ImageConsumer { +public: + sk_sp dequeueImage(bool* queueEmpty, SurfaceTexture& cb, + uirenderer::RenderState& renderState); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the SkImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + +private: + /** + * ImageSlot contains the information and object references that + * ImageConsumer maintains about a BufferQueue buffer slot. + */ + struct ImageSlot { + ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + // mImage is the SkImage created from mGraphicBuffer. + sk_sp mImage; + + /** + * mEglFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. + */ + EGLSyncKHR mEglFence; + + void createIfNeeded(sp graphicBuffer); + }; + + /** + * ImageConsumer stores the SkImages that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; +}; + +}; /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp new file mode 100644 index 000000000000..4bff715822e8 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2018 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 +#include +#include +#include + +#include + +#include "Matrix.h" +#include "SurfaceTexture.h" + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) + +static const mat4 mtxIdentity; + +SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t tex, + uint32_t texTarget, bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(tex) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::attachedToGL) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t texTarget, + bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(0) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::detached) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); + return NO_INIT; + } + mDefaultWidth = w; + mDefaultHeight = h; + return mConsumer->setDefaultBufferSize(w, h); +} + +status_t SurfaceTexture::updateTexImage() { + ATRACE_CALL(); + SFT_LOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.updateTexImage(*this); +} + +status_t SurfaceTexture::releaseTexImage() { + // releaseTexImage can be invoked even when not attached to a GL context. + ATRACE_CALL(); + SFT_LOGV("releaseTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.releaseTexImage(*this); +} + +status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber) { + status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); + if (err != NO_ERROR) { + return err; + } + + switch (mOpMode) { + case OpMode::attachedToView: + mImageConsumer.onAcquireBufferLocked(item); + break; + case OpMode::attachedToGL: + mEGLConsumer.onAcquireBufferLocked(item, *this); + break; + case OpMode::detached: + break; + } + + return NO_ERROR; +} + +status_t SurfaceTexture::releaseBufferLocked(int buf, sp graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); + // We could be releasing an EGL buffer, even if not currently attached to a GL context. + mImageConsumer.onReleaseBufferLocked(buf); + mEGLConsumer.onReleaseBufferLocked(buf); + return err; +} + +status_t SurfaceTexture::detachFromContext() { + ATRACE_CALL(); + SFT_LOGV("detachFromContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::attachedToGL) { + SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); + return INVALID_OPERATION; + } + + status_t err = mEGLConsumer.detachFromContext(*this); + if (err == OK) { + mOpMode = OpMode::detached; + } + + return err; +} + +status_t SurfaceTexture::attachToContext(uint32_t tex) { + ATRACE_CALL(); + SFT_LOGV("attachToContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("attachToContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::detached) { + SFT_LOGE( + "attachToContext: SurfaceTexture is already attached to a " + "context"); + return INVALID_OPERATION; + } + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible ImageConsumer cache + mImageConsumer.onFreeBufferLocked(mCurrentTexture); + } + + return mEGLConsumer.attachToContext(tex, *this); +} + +void SurfaceTexture::attachToView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + if (mAbandoned) { + SFT_LOGE("attachToView: abandoned SurfaceTexture"); + return; + } + if (mOpMode == OpMode::detached) { + mOpMode = OpMode::attachedToView; + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible EGLConsumer texture cache + mEGLConsumer.onFreeBufferLocked(mCurrentTexture); + mEGLConsumer.onAbandonLocked(); + } + } else { + SFT_LOGE("attachToView: already attached"); + } +} + +void SurfaceTexture::detachFromView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromView: abandoned SurfaceTexture"); + return; + } + + if (mOpMode == OpMode::attachedToView) { + mOpMode = OpMode::detached; + } else { + SFT_LOGE("detachFromView: not attached to View"); + } +} + +uint32_t SurfaceTexture::getCurrentTextureTarget() const { + return mTexTarget; +} + +void SurfaceTexture::getTransformMatrix(float mtx[16]) { + Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void SurfaceTexture::setFilteringEnabled(bool enabled) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); + return; + } + bool needsRecompute = mFilteringEnabled != enabled; + mFilteringEnabled = enabled; + + if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { + SFT_LOGD("setFilteringEnabled called with no current item"); + } + + if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + computeCurrentTransformMatrixLocked(); + } +} + +void SurfaceTexture::computeCurrentTransformMatrixLocked() { + SFT_LOGV("computeCurrentTransformMatrixLocked"); + sp buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) + ? nullptr + : mSlots[mCurrentTexture].mGraphicBuffer; + if (buf == nullptr) { + SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); + } + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, + mFilteringEnabled); +} + +void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp& buf, + const Rect& cropRect, uint32_t transform, + bool filtering) { + // Transform matrices + static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); + static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + + mat4 xform; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + xform *= mtxFlipH; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + xform *= mtxFlipV; + } + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + xform *= mtxRot90; + } + + if (!cropRect.isEmpty() && buf.get()) { + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + float shrinkAmount = 0.0f; + if (filtering) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; + + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; + } + + mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); + xform = crop * xform; + } + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so SurfaceTexture must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + xform = mtxFlipV * xform; + + memcpy(outTransform, xform.asArray(), sizeof(xform)); +} + +Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { + Rect outCrop = crop; + + uint32_t newWidth = static_cast(crop.width()); + uint32_t newHeight = static_cast(crop.height()); + + if (newWidth * bufferHeight > newHeight * bufferWidth) { + newWidth = newHeight * bufferWidth / bufferHeight; + ALOGV("too wide: newWidth = %d", newWidth); + } else if (newWidth * bufferHeight < newHeight * bufferWidth) { + newHeight = newWidth * bufferHeight / bufferWidth; + ALOGV("too tall: newHeight = %d", newHeight); + } + + uint32_t currentWidth = static_cast(crop.width()); + uint32_t currentHeight = static_cast(crop.height()); + + // The crop is too wide + if (newWidth < currentWidth) { + uint32_t dw = currentWidth - newWidth; + auto halfdw = dw / 2; + outCrop.left += halfdw; + // Not halfdw because it would subtract 1 too few when dw is odd + outCrop.right -= (dw - halfdw); + // The crop is too tall + } else if (newHeight < currentHeight) { + uint32_t dh = currentHeight - newHeight; + auto halfdh = dh / 2; + outCrop.top += halfdh; + // Not halfdh because it would subtract 1 too few when dh is odd + outCrop.bottom -= (dh - halfdh); + } + + ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, + outCrop.bottom); + + return outCrop; +} + +nsecs_t SurfaceTexture::getTimestamp() { + SFT_LOGV("getTimestamp"); + Mutex::Autolock lock(mMutex); + return mCurrentTimestamp; +} + +android_dataspace SurfaceTexture::getCurrentDataSpace() { + SFT_LOGV("getCurrentDataSpace"); + Mutex::Autolock lock(mMutex); + return mCurrentDataSpace; +} + +uint64_t SurfaceTexture::getFrameNumber() { + SFT_LOGV("getFrameNumber"); + Mutex::Autolock lock(mMutex); + return mCurrentFrameNumber; +} + +Rect SurfaceTexture::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) + ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) + : mCurrentCrop; +} + +uint32_t SurfaceTexture::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + +uint32_t SurfaceTexture::getCurrentScalingMode() const { + Mutex::Autolock lock(mMutex); + return mCurrentScalingMode; +} + +sp SurfaceTexture::getCurrentFence() const { + Mutex::Autolock lock(mMutex); + return mCurrentFence; +} + +std::shared_ptr SurfaceTexture::getCurrentFenceTime() const { + Mutex::Autolock lock(mMutex); + return mCurrentFenceTime; +} + +void SurfaceTexture::freeBufferLocked(int slotIndex) { + SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + if (slotIndex == mCurrentTexture) { + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + } + // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. + // Buffers can be freed after SurfaceTexture has detached from GL context or View. + mImageConsumer.onFreeBufferLocked(slotIndex); + mEGLConsumer.onFreeBufferLocked(slotIndex); + ConsumerBase::freeBufferLocked(slotIndex); +} + +void SurfaceTexture::abandonLocked() { + SFT_LOGV("abandonLocked"); + mEGLConsumer.onAbandonLocked(); + ConsumerBase::abandonLocked(); +} + +status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { + return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); +} + +void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat( + "%smTexName=%d mCurrentTexture=%d\n" + "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", + prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, + mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); + + ConsumerBase::dumpLocked(result, prefix); +} + +sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, + uirenderer::RenderState& renderState) { + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); + return nullptr; + } + + if (mOpMode != OpMode::attachedToView) { + SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); + return nullptr; + } + + auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); + if (image.get()) { + uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); + dataSpace = mCurrentDataSpace; + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h new file mode 100644 index 000000000000..db392a9f8476 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.h @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include +#include + +#include +#include + +#include "EGLConsumer.h" +#include "ImageConsumer.h" + +namespace android { + +namespace uirenderer { +class RenderState; +} + +/* + * SurfaceTexture consumes buffers of graphics data from a BufferQueue, + * and makes them available to HWUI render thread as a SkImage and to + * an application GL render thread as an OpenGL texture. + * + * When attached to an application GL render thread, a typical usage + * pattern is to set up the SurfaceTexture with the + * desired options, and call updateTexImage() when a new frame is desired. + * If a new frame is available, the texture will be updated. If not, + * the previous contents are retained. + * + * When attached to a HWUI render thread, the TextureView implementation + * calls dequeueImage, which either pulls a new SkImage or returns the + * last cached SkImage if BufferQueue is empty. + * When attached to HWUI render thread, SurfaceTexture is compatible to + * both Vulkan and GL drawing pipelines. + */ +class ANDROID_API SurfaceTexture : public ConsumerBase { +public: + enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES + typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; + + /** + * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with + * the tex parameter is used, tex indicates the name of the OpenGL ES + * texture to which images are to be streamed. texTarget specifies the + * OpenGL ES texture target to which the texture will be bound in + * updateTexImage. useFenceSync specifies whether fences should be used to + * synchronize access to buffers if that behavior is enabled at + * compile-time. + * + * A SurfaceTexture may be detached from one OpenGL ES context and then + * attached to a different context using the detachFromContext and + * attachToContext methods, respectively. The intention of these methods is + * purely to allow a SurfaceTexture to be transferred from one consumer + * context to another. If such a transfer is not needed there is no + * requirement that either of these methods be called. + * + * If the constructor with the tex parameter is used, the SurfaceTexture is + * created in a state where it is considered attached to an OpenGL ES + * context for the purposes of the attachToContext and detachFromContext + * methods. However, despite being considered "attached" to a context, the + * specific OpenGL ES context doesn't get latched until the first call to + * updateTexImage. After that point, all calls to updateTexImage must be + * made with the same OpenGL ES context current. + * + * If the constructor without the tex parameter is used, the SurfaceTexture is + * created in a detached state, and attachToContext must be called before + * calls to updateTexImage. + */ + SurfaceTexture(const sp& bq, uint32_t tex, uint32_t texureTarget, + bool useFenceSync, bool isControlledByApp); + + SurfaceTexture(const sp& bq, uint32_t texureTarget, bool useFenceSync, + bool isControlledByApp); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(); + + /** + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(); + + /** + * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix + * associated with the texture image set by the most recent call to + * updateTexImage. + * + * This transform matrix maps 2D homogeneous texture coordinates of the form + * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture + * coordinate that should be used to sample that location from the texture. + * Sampling the texture outside of the range of this transform is undefined. + * + * This transform is necessary to compensate for transforms that the stream + * content producer may implicitly apply to the content. By forcing users of + * a SurfaceTexture to apply this transform we avoid performing an extra + * copy of the data that would be needed to hide the transform from the + * user. + * + * The matrix is stored in column-major order so that it may be passed + * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv + * functions. + */ + void getTransformMatrix(float mtx[16]); + + /** + * Computes the transform matrix documented by getTransformMatrix + * from the BufferItem sub parts. + */ + static void computeTransformMatrix(float outTransform[16], const sp& buf, + const Rect& cropRect, uint32_t transform, bool filtering); + + /** + * Scale the crop down horizontally or vertically such that it has the + * same aspect ratio as the buffer does. + */ + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + + /** + * getTimestamp retrieves the timestamp associated with the texture image + * set by the most recent call to updateTexImage. + * + * The timestamp is in nanoseconds, and is monotonically increasing. Its + * other semantics (zero point, etc) are source-dependent and should be + * documented by the source. + */ + int64_t getTimestamp(); + + /** + * getDataSpace retrieves the DataSpace associated with the texture image + * set by the most recent call to updateTexImage. + */ + android_dataspace getCurrentDataSpace(); + + /** + * getFrameNumber retrieves the frame number associated with the texture + * image set by the most recent call to updateTexImage. + * + * The frame number is an incrementing counter set to 0 at the creation of + * the BufferQueue associated with this consumer. + */ + uint64_t getFrameNumber(); + + /** + * setDefaultBufferSize is used to set the size of buffers returned by + * requestBuffers when a with and height of zero is requested. + * A call to setDefaultBufferSize() may trigger requestBuffers() to + * be called from the client. + * The width and height parameters must be no greater than the minimum of + * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + * An error due to invalid dimensions might not be reported until + * updateTexImage() is called. + */ + status_t setDefaultBufferSize(uint32_t width, uint32_t height); + + /** + * setFilteringEnabled sets whether the transform matrix should be computed + * for use with bilinear filtering. + */ + void setFilteringEnabled(bool enabled); + + /** + * getCurrentTextureTarget returns the texture target of the current + * texture as returned by updateTexImage(). + */ + uint32_t getCurrentTextureTarget() const; + + /** + * getCurrentCrop returns the cropping rectangle of the current buffer. + */ + Rect getCurrentCrop() const; + + /** + * getCurrentTransform returns the transform of the current buffer. + */ + uint32_t getCurrentTransform() const; + + /** + * getCurrentScalingMode returns the scaling mode of the current buffer. + */ + uint32_t getCurrentScalingMode() const; + + /** + * getCurrentFence returns the fence indicating when the current buffer is + * ready to be read from. + */ + sp getCurrentFence() const; + + /** + * getCurrentFence returns the FenceTime indicating when the current + * buffer is ready to be read from. + */ + std::shared_ptr getCurrentFenceTime() const; + + /** + * setConsumerUsageBits overrides the ConsumerBase method to OR + * DEFAULT_USAGE_FLAGS to usage. + */ + status_t setConsumerUsageBits(uint64_t usage); + + /** + * detachFromContext detaches the SurfaceTexture from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a SurfaceTexture from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a SurfaceTexture has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the SurfaceTexture is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(); + + /** + * attachToContext attaches a SurfaceTexture that is currently in the + * 'detached' state to the current OpenGL ES context. A SurfaceTexture is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * SurfaceTexture that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex); + + sk_sp dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, uirenderer::RenderState& renderState); + + /** + * attachToView attaches a SurfaceTexture that is currently in the + * 'detached' state to HWUI View system. + */ + void attachToView(); + + /** + * detachFromView detaches a SurfaceTexture from HWUI View system. + */ + void detachFromView(); + +protected: + /** + * abandonLocked overrides the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + virtual void abandonLocked(); + + /** + * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- + * specific info in addition to the ConsumerBase behavior. + */ + virtual void dumpLocked(String8& result, const char* prefix) const override; + + /** + * acquireBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) override; + + /** + * releaseBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) override; + + /** + * freeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + * + * This method must be called with mMutex locked. + */ + virtual void freeBufferLocked(int slotIndex); + + /** + * computeCurrentTransformMatrixLocked computes the transform matrix for the + * current texture. It uses mCurrentTransform and the current GraphicBuffer + * to compute this matrix and stores it in mCurrentTransformMatrix. + * mCurrentTextureImage must not be NULL. + */ + void computeCurrentTransformMatrixLocked(); + + /** + * The default consumer usage flags that SurfaceTexture always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the SurfaceTexture user. In particular, SurfaceTexture will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentCrop is the crop rectangle that applies to the current texture. + * It gets set each time updateTexImage is called. + */ + Rect mCurrentCrop; + + /** + * mCurrentTransform is the transform identifier for the current texture. It + * gets set each time updateTexImage is called. + */ + uint32_t mCurrentTransform; + + /** + * mCurrentScalingMode is the scaling mode for the current texture. It gets + * set each time updateTexImage is called. + */ + uint32_t mCurrentScalingMode; + + /** + * mCurrentFence is the fence received from BufferQueue in updateTexImage. + */ + sp mCurrentFence; + + /** + * The FenceTime wrapper around mCurrentFence. + */ + std::shared_ptr mCurrentFenceTime{FenceTime::NO_FENCE}; + + /** + * mCurrentTransformMatrix is the transform matrix for the current texture. + * It gets computed by computeTransformMatrix each time updateTexImage is + * called. + */ + float mCurrentTransformMatrix[16]; + + /** + * mCurrentTimestamp is the timestamp for the current texture. It + * gets set each time updateTexImage is called. + */ + int64_t mCurrentTimestamp; + + /** + * mCurrentDataSpace is the dataspace for the current texture. It + * gets set each time updateTexImage is called. + */ + android_dataspace mCurrentDataSpace; + + /** + * mCurrentFrameNumber is the frame counter for the current texture. + * It gets set each time updateTexImage is called. + */ + uint64_t mCurrentFrameNumber; + + uint32_t mDefaultWidth, mDefaultHeight; + + /** + * mFilteringEnabled indicates whether the transform matrix is computed for + * use with bilinear filtering. It defaults to true and is changed by + * setFilteringEnabled(). + */ + bool mFilteringEnabled; + + /** + * mTexName is the name of the OpenGL texture to which streamed images will + * be bound when updateTexImage is called. It is set at construction time + * and can be changed with a call to attachToContext. + */ + uint32_t mTexName; + + /** + * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync + * extension should be used to prevent buffers from being dequeued before + * it's safe for them to be written. It gets set at construction time and + * never changes. + */ + const bool mUseFenceSync; + + /** + * mTexTarget is the GL texture target with which the GL texture object is + * associated. It is set in the constructor and never changed. It is + * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android + * Browser. In that case it is set to GL_TEXTURE_2D to allow + * glCopyTexSubImage to read from the texture. This is a hack to work + * around a GL driver limitation on the number of FBO attachments, which the + * browser's tile cache exceeds. + */ + const uint32_t mTexTarget; + + /** + * mCurrentTexture is the buffer slot index of the buffer that is currently + * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + * indicating that no buffer slot is currently bound to the texture. Note, + * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + * that no buffer is bound to the texture. A call to setBufferCount will + * reset mCurrentTexture to INVALID_BUFFER_SLOT. + */ + int mCurrentTexture; + + enum class OpMode { detached, attachedToView, attachedToGL }; + /** + * mOpMode indicates whether the SurfaceTexture is currently attached to + * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, + * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to + * whatever GL context is current at the time of the first updateTexImage call. + * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by + * attachToContext. + * attachToView/detachFromView are used to attach/detach from HWUI view system. + */ + OpMode mOpMode; + + /** + * mEGLConsumer has SurfaceTexture logic used when attached to GL context. + */ + EGLConsumer mEGLConsumer; + + /** + * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. + */ + ImageConsumer mImageConsumer; + + friend class ImageConsumer; + friend class EGLConsumer; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp index 5b361548eeda..d2d37dcb34f2 100644 --- a/libs/hwui/tests/common/LeakChecker.cpp +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -16,7 +16,6 @@ #include "LeakChecker.h" -#include "Caches.h" #include "TestUtils.h" #include @@ -71,9 +70,6 @@ void LeakChecker::checkForLeaks() { // thread-local caches so some leaks will not be properly tagged as leaks UnreachableMemoryInfo rtMemInfo; TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { - if (Caches::hasInstance()) { - Caches::getInstance().tasks.stop(); - } // Check for leaks if (!GetUnreachableMemory(rtMemInfo)) { cerr << "Failed to get unreachable memory!" << endl; diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 69586345319e..66b9b85bdbe7 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -67,16 +67,14 @@ sp TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { sp layerUpdater = createTextureLayerUpdater(renderThread); - layerUpdater->backingLayer()->getTransform().load(transform); + layerUpdater->backingLayer()->getTransform() = transform; layerUpdater->setSize(width, height); layerUpdater->setTransform(&transform); // updateLayer so it's ready to draw - layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - static_cast(layerUpdater->backingLayer()) - ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix identity; + identity.setIdentity(); + layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr); return layerUpdater; } @@ -117,7 +115,6 @@ void TestUtils::TestTask::run() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { renderThread.vulkanManager().destroy(); } else { - renderThread.renderState().flush(Caches::FlushMode::Full); renderThread.destroyGlContext(); } } diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 743f8093bfa8..0e6582c59a36 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -18,7 +18,6 @@ #include #include -#include #include #include #include diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index f29830f0e34b..6c8775b1bdbb 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -15,12 +15,13 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "Properties.h" #include "tests/common/TestUtils.h" #include +#include +#include using namespace android; using namespace android::uirenderer; @@ -31,10 +32,6 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { layerUpdater->setBlend(true); // updates are deferred so the backing layer should still be in its default state - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget()); - } EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight()); EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter()); @@ -42,19 +39,13 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); // push the deferred updates to the layer - Matrix4 scaledMatrix; - scaledMatrix.loadScale(0.5, 0.5, 0.0); - layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5); + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + sk_sp layerImage = SkImage::MakeFromBitmap(bitmap); + layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage); // the backing layer should now have all the properties applied. - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget()); - } EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight()); EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter()); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 9e6d9a8c27de..aecceb3609f5 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -17,12 +17,13 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "Caches.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "Properties.h" #include "tests/common/LeakChecker.h" +#include "thread/TaskProcessor.h" +#include "thread/Task.h" #include "thread/TaskManager.h" #include diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index f8e8a0a18284..ebf2343c5518 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -16,6 +16,7 @@ #ifndef PAINT_UTILS_H #define PAINT_UTILS_H +#include #include #include diff --git a/native/android/Android.bp b/native/android/Android.bp index 4fb5e748aaac..43847cc4ab06 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -64,6 +64,7 @@ cc_library_shared { "libsensor", "libandroid_runtime", "libnetd_client", + "libhwui", ], static_libs: [ diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp index b26688190ccd..ced2792775d4 100644 --- a/native/android/surface_texture.cpp +++ b/native/android/surface_texture.cpp @@ -21,15 +21,16 @@ #include -#include #include #include +#include "surfacetexture/SurfaceTexture.h" + using namespace android; struct ASurfaceTexture { - sp consumer; + sp consumer; sp producer; }; -- cgit v1.2.3-59-g8ed1b From a683eb3945320f592f58705c3c31b3c044f4bd72 Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 4 Sep 2018 15:42:18 +0000 Subject: Revert "Revert "Revert "TextureView Vulkan support and optimized OpenGL draw""" This reverts commit 85f9096b5272c9a39e592e2e97cbbe6cb0e767ab. Reason for revert: lensblur broken. Change-Id: I83ac163159fc537bc15936a0f8597a7512ca9d6e --- core/jni/android/graphics/ColorFilter.cpp | 2 + core/jni/android/graphics/Matrix.cpp | 1 + core/jni/android/graphics/Shader.cpp | 1 + core/jni/android/graphics/SurfaceTexture.cpp | 47 +- core/jni/android_view_TextureLayer.cpp | 3 +- .../android_graphics_SurfaceTexture.h | 6 +- libs/hwui/Android.bp | 10 +- libs/hwui/Caches.cpp | 103 ++++ libs/hwui/Caches.h | 135 +++++ libs/hwui/DeferredLayerUpdater.cpp | 123 +++- libs/hwui/DeferredLayerUpdater.h | 34 +- libs/hwui/GlLayer.cpp | 68 +++ libs/hwui/GlLayer.h | 75 +++ libs/hwui/GpuMemoryTracker.cpp | 17 + libs/hwui/Layer.cpp | 8 +- libs/hwui/Layer.h | 57 +- libs/hwui/PixelBuffer.cpp | 156 +++++ libs/hwui/PixelBuffer.h | 198 ++++++ libs/hwui/RenderProperties.h | 1 + libs/hwui/ResourceCache.cpp | 23 +- libs/hwui/Texture.cpp | 413 +++++++++++++ libs/hwui/Texture.h | 228 +++++++ libs/hwui/VkLayer.cpp | 40 ++ libs/hwui/VkLayer.h | 70 +++ libs/hwui/hwui/Bitmap.cpp | 2 +- libs/hwui/pipeline/skia/LayerDrawable.cpp | 32 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 12 +- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 9 +- libs/hwui/renderstate/PixelBufferState.cpp | 42 ++ libs/hwui/renderstate/PixelBufferState.h | 38 ++ libs/hwui/renderstate/RenderState.cpp | 83 ++- libs/hwui/renderstate/RenderState.h | 20 +- libs/hwui/renderstate/TextureState.cpp | 147 +++++ libs/hwui/renderstate/TextureState.h | 98 +++ libs/hwui/renderthread/CacheManager.cpp | 4 +- libs/hwui/renderthread/CanvasContext.cpp | 8 + libs/hwui/renderthread/EglManager.cpp | 104 ---- libs/hwui/renderthread/EglManager.h | 10 - libs/hwui/renderthread/RenderThread.cpp | 4 +- libs/hwui/renderthread/VulkanManager.cpp | 20 +- libs/hwui/renderthread/VulkanManager.h | 8 - libs/hwui/surfacetexture/EGLConsumer.cpp | 675 --------------------- libs/hwui/surfacetexture/EGLConsumer.h | 311 ---------- libs/hwui/surfacetexture/ImageConsumer.cpp | 152 ----- libs/hwui/surfacetexture/ImageConsumer.h | 97 --- libs/hwui/surfacetexture/SurfaceTexture.cpp | 496 --------------- libs/hwui/surfacetexture/SurfaceTexture.h | 452 -------------- libs/hwui/tests/common/LeakChecker.cpp | 4 + libs/hwui/tests/common/TestUtils.cpp | 11 +- libs/hwui/tests/common/TestUtils.h | 1 + libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp | 23 +- libs/hwui/tests/unit/main.cpp | 3 +- libs/hwui/utils/PaintUtils.h | 1 - native/android/Android.bp | 1 - native/android/surface_texture.cpp | 5 +- 55 files changed, 2202 insertions(+), 2490 deletions(-) create mode 100644 libs/hwui/Caches.cpp create mode 100644 libs/hwui/Caches.h create mode 100644 libs/hwui/GlLayer.cpp create mode 100644 libs/hwui/GlLayer.h create mode 100644 libs/hwui/PixelBuffer.cpp create mode 100644 libs/hwui/PixelBuffer.h create mode 100644 libs/hwui/Texture.cpp create mode 100644 libs/hwui/Texture.h create mode 100644 libs/hwui/VkLayer.cpp create mode 100644 libs/hwui/VkLayer.h create mode 100644 libs/hwui/renderstate/PixelBufferState.cpp create mode 100644 libs/hwui/renderstate/PixelBufferState.h create mode 100644 libs/hwui/renderstate/TextureState.cpp create mode 100644 libs/hwui/renderstate/TextureState.h delete mode 100644 libs/hwui/surfacetexture/EGLConsumer.cpp delete mode 100644 libs/hwui/surfacetexture/EGLConsumer.h delete mode 100644 libs/hwui/surfacetexture/ImageConsumer.cpp delete mode 100644 libs/hwui/surfacetexture/ImageConsumer.h delete mode 100644 libs/hwui/surfacetexture/SurfaceTexture.cpp delete mode 100644 libs/hwui/surfacetexture/SurfaceTexture.h (limited to 'libs/hwui/ResourceCache.cpp') diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index 6ebf35c8e1dc..3fcedd0264ae 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -22,6 +22,8 @@ #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" +#include + namespace android { using namespace uirenderer; diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index 755fcfb27141..f8bb77a9650c 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -20,6 +20,7 @@ #include "SkMatrix.h" #include "core_jni_helpers.h" +#include #include namespace android { diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 68f5bef18de1..cff772002b14 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -6,6 +6,7 @@ #include "SkBlendMode.h" #include "core_jni_helpers.h" +#include #include using namespace android::uirenderer; diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index 3e464c61665f..d098a355085e 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -36,7 +36,6 @@ #include "jni.h" #include #include -#include "surfacetexture/SurfaceTexture.h" // ---------------------------------------------------------------------------- @@ -81,10 +80,10 @@ static bool isProtectedContext() { // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp& surfaceTexture) + const sp& surfaceTexture) { - SurfaceTexture* const p = - (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); + GLConsumer* const p = + (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); if (surfaceTexture.get()) { surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture); } @@ -109,10 +108,10 @@ static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz, } static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, - jobject thiz, sp listener) + jobject thiz, sp listener) { - SurfaceTexture::FrameAvailableListener* const p = - (SurfaceTexture::FrameAvailableListener*) + GLConsumer::FrameAvailableListener* const p = + (GLConsumer::FrameAvailableListener*) env->GetLongField(thiz, fields.frameAvailableListener); if (listener.get()) { listener->incStrong((void*)SurfaceTexture_setSurfaceTexture); @@ -123,8 +122,8 @@ static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get()); } -sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { - return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); +sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { + return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); } sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) { @@ -132,7 +131,7 @@ sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) } sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); sp producer(SurfaceTexture_getProducer(env, thiz)); sp surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL); return surfaceTextureClient; @@ -145,7 +144,7 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) { // ---------------------------------------------------------------------------- -class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener +class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener { public: JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz); @@ -267,12 +266,12 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, consumer->setMaxBufferCount(1); } - sp surfaceTexture; + sp surfaceTexture; if (isDetached) { - surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES, + surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } else { - surfaceTexture = new SurfaceTexture(consumer, texName, + surfaceTexture = new GLConsumer(consumer, texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } @@ -307,7 +306,7 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setFrameAvailableListener(0); SurfaceTexture_setFrameAvailableListener(env, thiz, 0); SurfaceTexture_setSurfaceTexture(env, thiz, 0); @@ -316,13 +315,13 @@ static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) static void SurfaceTexture_setDefaultBufferSize( JNIEnv* env, jobject thiz, jint width, jint height) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->updateTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to update texture contents (see " @@ -334,7 +333,7 @@ static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->releaseTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to release texture contents (see " @@ -346,20 +345,20 @@ static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); float* mtx = env->GetFloatArrayElements(jmtx, NULL); surfaceTexture->getTransformMatrix(mtx); env->ReleaseFloatArrayElements(jmtx, mtx, 0); @@ -367,19 +366,19 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->isAbandoned(); } diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp index 1ccb6a8f610c..d3a447f1f7dc 100644 --- a/core/jni/android_view_TextureLayer.cpp +++ b/core/jni/android_view_TextureLayer.cpp @@ -67,7 +67,8 @@ static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr, jobject surface) { DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); - layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); + layer->setSurfaceTexture(surfaceTexture); } static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h index 0ad25876a008..c534d4bb9e0a 100644 --- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h +++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h @@ -23,14 +23,14 @@ namespace android { +class GLConsumer; class IGraphicBufferProducer; -class SurfaceTexture; extern sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz); extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz); -/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */ -extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */ +extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); /* gets the producer end of the SurfaceTexture */ extern sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index b7ffb5d9dc2e..d5829838cb3f 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -175,7 +175,9 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", + "renderstate/PixelBufferState.cpp", "renderstate/RenderState.cpp", + "renderstate/TextureState.cpp", "renderthread/CacheManager.cpp", "renderthread/CanvasContext.cpp", "renderthread/DrawFrameTask.cpp", @@ -187,9 +189,6 @@ cc_defaults { "renderthread/TimeLord.cpp", "renderthread/Frame.cpp", "service/GraphicsStatsService.cpp", - "surfacetexture/EGLConsumer.cpp", - "surfacetexture/ImageConsumer.cpp", - "surfacetexture/SurfaceTexture.cpp", "thread/TaskManager.cpp", "utils/Blur.cpp", "utils/Color.cpp", @@ -201,6 +200,7 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", + "Caches.cpp", "CanvasState.cpp", "CanvasTransform.cpp", "ClipArea.cpp", @@ -209,6 +209,7 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", + "GlLayer.cpp", "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "Interpolator.cpp", @@ -218,6 +219,7 @@ cc_defaults { "Matrix.cpp", "EglReadback.cpp", "PathParser.cpp", + "PixelBuffer.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Properties.cpp", @@ -229,7 +231,9 @@ cc_defaults { "ResourceCache.cpp", "SkiaCanvas.cpp", "Snapshot.cpp", + "Texture.cpp", "VectorDrawable.cpp", + "VkLayer.cpp", "protos/graphicsstats.proto", ], diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp new file mode 100644 index 000000000000..254144448859 --- /dev/null +++ b/libs/hwui/Caches.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 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 "GlLayer.h" +#include "Properties.h" +#include "renderstate/RenderState.h" +#include "utils/GLUtils.h" + +#include +#include +#include + +namespace android { +namespace uirenderer { + +Caches* Caches::sInstance = nullptr; + +/////////////////////////////////////////////////////////////////////////////// +// Macros +/////////////////////////////////////////////////////////////////////////////// + +#if DEBUG_CACHE_FLUSH +#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) +#else +#define FLUSH_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +Caches::Caches(RenderState& renderState) : mInitialized(false) { + INIT_LOGD("Creating OpenGL renderer caches"); + init(); + initStaticProperties(); +} + +bool Caches::init() { + if (mInitialized) return false; + + ATRACE_NAME("Caches::init"); + + mRegionMesh = nullptr; + + mInitialized = true; + + mPixelBufferState = new PixelBufferState(); + mTextureState = new TextureState(); + mTextureState->constructTexture(*this); + + return true; +} + +void Caches::initStaticProperties() { + // OpenGL ES 3.0+ specific features + gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() && + property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); +} + +void Caches::terminate() { + if (!mInitialized) return; + mRegionMesh.reset(nullptr); + + clearGarbage(); + + delete mPixelBufferState; + mPixelBufferState = nullptr; + delete mTextureState; + mTextureState = nullptr; + mInitialized = false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Memory management +/////////////////////////////////////////////////////////////////////////////// + +void Caches::clearGarbage() {} + +void Caches::flush(FlushMode mode) { + clearGarbage(); + glFinish(); + // Errors during cleanup should be considered non-fatal, dump them and + // and move on. TODO: All errors or just errors like bad surface? + GLUtils::dumpGLErrors(); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h new file mode 100644 index 000000000000..642f9dc50eb1 --- /dev/null +++ b/libs/hwui/Caches.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 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. + */ + +#pragma once + +#include "DeviceInfo.h" +#include "Extensions.h" +#include "ResourceCache.h" +#include "renderstate/PixelBufferState.h" +#include "renderstate/TextureState.h" +#include "thread/TaskManager.h" +#include "thread/TaskProcessor.h" + +#include +#include + +#include + +#include + +#include + +#include + +#include + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Caches +/////////////////////////////////////////////////////////////////////////////// + +class RenderNode; +class RenderState; + +class ANDROID_API Caches { +public: + static Caches& createInstance(RenderState& renderState) { + LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted"); + sInstance = new Caches(renderState); + return *sInstance; + } + + static Caches& getInstance() { + LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created"); + return *sInstance; + } + + static bool hasInstance() { return sInstance != nullptr; } + +private: + explicit Caches(RenderState& renderState); + static Caches* sInstance; + +public: + enum class FlushMode { Layers = 0, Moderate, Full }; + + /** + * Initialize caches. + */ + bool init(); + + bool isInitialized() { return mInitialized; } + + /** + * Flush the cache. + * + * @param mode Indicates how much of the cache should be flushed + */ + void flush(FlushMode mode); + + /** + * Destroys all resources associated with this cache. This should + * be called after a flush(FlushMode::Full). + */ + void terminate(); + + /** + * Call this on each frame to ensure that garbage is deleted from + * GPU memory. + */ + void clearGarbage(); + + /** + * 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().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; + } + +public: + TaskManager tasks; + + bool gpuPixelBuffersEnabled; + + const Extensions& extensions() const { return DeviceInfo::get()->extensions(); } + PixelBufferState& pixelBufferState() { return *mPixelBufferState; } + TextureState& textureState() { return *mTextureState; } + +private: + void initStaticProperties(); + + static void eventMarkNull(GLsizei length, const GLchar* marker) {} + static void startMarkNull(GLsizei length, const GLchar* marker) {} + static void endMarkNull() {} + + // Used to render layers + std::unique_ptr mRegionMesh; + + bool mInitialized; + + // TODO: move below to RenderState + PixelBufferState* mPixelBufferState = nullptr; + TextureState* mTextureState = nullptr; + +}; // class Caches + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 00916559a9c2..569de76f294e 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,20 +15,27 @@ */ #include "DeferredLayerUpdater.h" +#include "GlLayer.h" +#include "VkLayer.h" #include "renderstate/RenderState.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderTask.h" #include "utils/PaintUtils.h" namespace android { namespace uirenderer { -DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) +DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, + Layer::Api layerApi) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) - , mLayer(nullptr) { + , mLayer(nullptr) + , mLayerApi(layerApi) + , mCreateLayerFn(createLayerFn) { renderState.registerDeferredLayerUpdater(this); } @@ -43,9 +50,13 @@ void DeferredLayerUpdater::destroyLayer() { return; } - if (mSurfaceTexture.get() && mGLContextAttached) { - mSurfaceTexture->detachFromView(); + if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) { + status_t err = mSurfaceTexture->detachFromContext(); mGLContextAttached = false; + if (err != 0) { + // TODO: Elevate to fatal exception + ALOGE("Failed to detach SurfaceTexture from context %d", err); + } } mLayer->postDecStrong(); @@ -64,53 +75,99 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { void DeferredLayerUpdater::apply() { if (!mLayer) { - mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); + mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend); } mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { - if (!mGLContextAttached) { - mGLContextAttached = true; - mUpdateTexImage = true; - mSurfaceTexture->attachToView(); - } - if (mUpdateTexImage) { - mUpdateTexImage = false; - sk_sp layerImage; - SkMatrix textureTransform; - android_dataspace dataSpace; - bool queueEmpty = true; - // If the SurfaceTexture queue is in synchronous mode, need to discard all - // but latest frame. Since we can't tell which mode it is in, - // do this unconditionally. - do { - layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty, - mRenderState); - } while (layerImage.get() && (!queueEmpty)); - if (layerImage.get()) { - // force filtration if buffer size != layer size - bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); - updateLayer(forceFilter, textureTransform, dataSpace, layerImage); + if (mLayer->getApi() == Layer::Api::Vulkan) { + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateVkTexImage(); + } + } else { + LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, + "apply surfaceTexture with non GL backend %x, GL %x, VK %x", + mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); + if (!mGLContextAttached) { + mGLContextAttached = true; + mUpdateTexImage = true; + mSurfaceTexture->attachToContext(static_cast(mLayer)->getTextureId()); } + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateTexImage(); + } + GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); + static_cast(mLayer)->setRenderTarget(renderTarget); } - if (mTransform) { - mLayer->getTransform() = *mTransform; + mLayer->getTransform().load(*mTransform); setTransform(nullptr); } } } -void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, - android_dataspace dataspace, const sk_sp& layerImage) { +void DeferredLayerUpdater::doUpdateTexImage() { + LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, + "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), + Layer::Api::OpenGL, Layer::Api::Vulkan); + if (mSurfaceTexture->updateTexImage() == NO_ERROR) { + float transform[16]; + + int64_t frameNumber = mSurfaceTexture->getFrameNumber(); + // If the GLConsumer queue is in synchronous mode, need to discard all + // but latest frame, using the frame number to tell when we no longer + // have newer frames to target. Since we can't tell which mode it is in, + // do this unconditionally. + int dropCounter = 0; + while (mSurfaceTexture->updateTexImage() == NO_ERROR) { + int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); + if (newFrameNumber == frameNumber) break; + frameNumber = newFrameNumber; + dropCounter++; + } + + bool forceFilter = false; + sp buffer = mSurfaceTexture->getCurrentBuffer(); + if (buffer != nullptr) { + // force filtration if buffer size != layer size + forceFilter = mWidth != static_cast(buffer->getWidth()) || + mHeight != static_cast(buffer->getHeight()); + } + +#if DEBUG_RENDERER + if (dropCounter > 0) { + RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); + } +#endif + mSurfaceTexture->getTransformMatrix(transform); + + updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace()); + } +} + +void DeferredLayerUpdater::doUpdateVkTexImage() { + LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, + "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), + Layer::Api::OpenGL, Layer::Api::Vulkan); + + static const mat4 identityMatrix; + updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN); + + VkLayer* vkLayer = static_cast(mLayer); + vkLayer->updateTexture(); +} + +void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform, + android_dataspace dataspace) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); - mLayer->getTexTransform() = textureTransform; + mLayer->getTexTransform().load(textureTransform); mLayer->setDataSpace(dataspace); - mLayer->setImage(layerImage); } void DeferredLayerUpdater::detachSurfaceTexture() { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 4c323b861002..fe3ee7a2b4c6 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -17,19 +17,18 @@ #pragma once #include -#include #include #include -#include +#include #include #include #include #include -#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" +#include "renderthread/RenderThread.h" namespace android { namespace uirenderer { @@ -42,7 +41,12 @@ class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState); + typedef std::function colorFilter, int alpha, + SkBlendMode mode, bool blend)> + CreateLayerFn; + ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, + Layer::Api layerApi); ANDROID_API ~DeferredLayerUpdater(); @@ -66,13 +70,13 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp& consumer) { - if (consumer.get() != mSurfaceTexture.get()) { - mSurfaceTexture = consumer; + ANDROID_API void setSurfaceTexture(const sp& texture) { + if (texture.get() != mSurfaceTexture.get()) { + mSurfaceTexture = texture; - GLenum target = consumer->getCurrentTextureTarget(); + GLenum target = texture->getCurrentTextureTarget(); LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported SurfaceTexture with target %x", target); + "set unsupported GLConsumer with target %x", target); } } @@ -93,11 +97,12 @@ public: void detachSurfaceTexture(); - void updateLayer(bool forceFilter, const SkMatrix& textureTransform, - android_dataspace dataspace, const sk_sp& layerImage); + void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace); void destroyLayer(); + Layer::Api getBackingLayerApi() { return mLayerApi; } + private: RenderState& mRenderState; @@ -108,12 +113,17 @@ private: sk_sp mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp mSurfaceTexture; + sp mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; Layer* mLayer; + Layer::Api mLayerApi; + CreateLayerFn mCreateLayerFn; + + void doUpdateTexImage(); + void doUpdateVkTexImage(); }; } /* namespace uirenderer */ diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp new file mode 100644 index 000000000000..432bb8526465 --- /dev/null +++ b/libs/hwui/GlLayer.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 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 "GlLayer.h" + +#include "Caches.h" +#include "RenderNode.h" +#include "renderstate/RenderState.h" + +namespace android { +namespace uirenderer { + +GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) + : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode) + , caches(Caches::getInstance()) + , texture(caches) { + texture.mWidth = layerWidth; + texture.mHeight = layerHeight; + texture.blend = blend; +} + +GlLayer::~GlLayer() { + // There's a rare possibility that Caches could have been destroyed already + // since this method is queued up as a task. + // Since this is a reset method, treat this as non-fatal. + if (caches.isInitialized() && texture.mId) { + texture.deleteTexture(); + } +} + +void GlLayer::onGlContextLost() { + texture.deleteTexture(); +} + +void GlLayer::setRenderTarget(GLenum renderTarget) { + if (renderTarget != getRenderTarget()) { + // new render target: bind with new target, and update filter/wrap + texture.mTarget = renderTarget; + if (texture.mId) { + caches.textureState().bindTexture(texture.target(), texture.mId); + } + texture.setFilter(GL_NEAREST, false, true); + texture.setWrap(GL_CLAMP_TO_EDGE, false, true); + } +} + +void GlLayer::generateTexture() { + if (!texture.mId) { + glGenTextures(1, &texture.mId); + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h new file mode 100644 index 000000000000..9f70fdae6790 --- /dev/null +++ b/libs/hwui/GlLayer.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 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. + */ + +#pragma once + +#include "Layer.h" + +#include "Texture.h" + +namespace android { +namespace uirenderer { + +// Forward declarations +class Caches; + +/** + * A layer has dimensions and is backed by an OpenGL texture or FBO. + */ +class GlLayer : public Layer { +public: + GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend); + virtual ~GlLayer(); + + uint32_t getWidth() const override { return texture.mWidth; } + + uint32_t getHeight() const override { return texture.mHeight; } + + void setSize(uint32_t width, uint32_t height) override { + texture.updateLayout(width, height, texture.internalFormat(), texture.format(), + texture.target()); + } + + void setBlend(bool blend) override { texture.blend = blend; } + + bool isBlend() const override { return texture.blend; } + + inline GLuint getTextureId() const { return texture.id(); } + + inline GLenum getRenderTarget() const { return texture.target(); } + + void setRenderTarget(GLenum renderTarget); + + void generateTexture(); + + /** + * Lost the GL context but the layer is still around, mark it invalid internally + * so the dtor knows not to do any GL work + */ + void onGlContextLost(); + +private: + Caches& caches; + + /** + * The texture backing this layer. + */ + Texture texture; +}; // struct GlLayer + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index a9a7af8f22f3..612bfde1a3fa 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "Texture.h" #include "utils/StringUtils.h" #include @@ -116,6 +117,22 @@ void GpuMemoryTracker::onFrameCompleted() { ATRACE_INT(buf, stats.count); } } + + std::vector freeList; + for (const auto& obj : gObjectSet) { + if (obj->objectType() == GpuObjectType::Texture) { + const Texture* texture = static_cast(obj); + if (texture->cleanup) { + ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(), + texture->width(), texture->height()); + freeList.push_back(texture); + } + } + } + for (auto& texture : freeList) { + const_cast(texture)->deleteTexture(); + delete texture; + } } } // namespace uirenderer diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index f59a2e6ee5c1..fb8f0337c95e 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -17,17 +17,17 @@ #include "Layer.h" #include "renderstate/RenderState.h" -#include "utils/Color.h" #include namespace android { namespace uirenderer { -Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alpha, - SkBlendMode mode) +Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter, int alpha, + SkBlendMode mode) : GpuMemoryTracker(GpuObjectType::Layer) , mRenderState(renderState) + , mApi(api) , mColorFilter(colorFilter) , alpha(alpha) , mode(mode) { @@ -36,8 +36,6 @@ Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alp incStrong(nullptr); buildColorSpaceWithFilter(); renderState.registerLayer(this); - texTransform.setIdentity(); - transform.setIdentity(); } Layer::~Layer() { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index c4e4c1c96ba6..31878ac23642 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -23,9 +23,8 @@ #include #include #include -#include -#include -#include + +#include "Matrix.h" namespace android { namespace uirenderer { @@ -41,19 +40,24 @@ class RenderState; */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - Layer(RenderState& renderState, sk_sp, int alpha, SkBlendMode mode); + enum class Api { + OpenGL = 0, + Vulkan = 1, + }; + + Api getApi() const { return mApi; } ~Layer(); - virtual uint32_t getWidth() const { return mWidth; } + virtual uint32_t getWidth() const = 0; - virtual uint32_t getHeight() const { return mHeight; } + virtual uint32_t getHeight() const = 0; - virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } + virtual void setSize(uint32_t width, uint32_t height) = 0; - virtual void setBlend(bool blend) { mBlend = blend; } + virtual void setBlend(bool blend) = 0; - virtual bool isBlend() const { return mBlend; } + virtual bool isBlend() const = 0; inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } @@ -80,9 +84,9 @@ public: inline sk_sp getColorSpaceWithFilter() const { return mColorSpaceWithFilter; } - inline SkMatrix& getTexTransform() { return texTransform; } + inline mat4& getTexTransform() { return texTransform; } - inline SkMatrix& getTransform() { return transform; } + inline mat4& getTransform() { return transform; } /** * Posts a decStrong call to the appropriate thread. @@ -90,17 +94,16 @@ public: */ void postDecStrong(); - inline void setImage(const sk_sp& image) { this->layerImage = image; } - - inline sk_sp getImage() const { return this->layerImage; } - protected: + Layer(RenderState& renderState, Api api, sk_sp, int alpha, SkBlendMode mode); RenderState& mRenderState; private: void buildColorSpaceWithFilter(); + Api mApi; + /** * Color filter used to draw this layer. Optional. */ @@ -134,32 +137,12 @@ private: /** * Optional texture coordinates transform. */ - SkMatrix texTransform; + mat4 texTransform; /** * Optional transform. */ - SkMatrix transform; - - /** - * An image backing the layer. - */ - sk_sp layerImage; - - /** - * layer width. - */ - uint32_t mWidth = 0; - - /** - * layer height. - */ - uint32_t mHeight = 0; - - /** - * enable blending - */ - bool mBlend = false; + mat4 transform; }; // struct Layer diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp new file mode 100644 index 000000000000..910a9889db1f --- /dev/null +++ b/libs/hwui/PixelBuffer.cpp @@ -0,0 +1,156 @@ +/* + * 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 "PixelBuffer.h" + +#include "Debug.h" +#include "Extensions.h" +#include "Properties.h" +#include "renderstate/RenderState.h" +#include "utils/GLUtils.h" + +#include + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// CPU pixel buffer +/////////////////////////////////////////////////////////////////////////////// + +class CpuPixelBuffer : public PixelBuffer { +public: + CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); + + uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; + + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; + +protected: + void unmap() override; + +private: + std::unique_ptr mBuffer; +}; + +CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) + : PixelBuffer(format, width, height) + , mBuffer(new uint8_t[width * height * formatSize(format)]) {} + +uint8_t* CpuPixelBuffer::map(AccessMode mode) { + if (mAccessMode == kAccessMode_None) { + mAccessMode = mode; + } + return mBuffer.get(); +} + +void CpuPixelBuffer::unmap() { + mAccessMode = kAccessMode_None; +} + +void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, + &mBuffer[offset]); +} + +/////////////////////////////////////////////////////////////////////////////// +// GPU pixel buffer +/////////////////////////////////////////////////////////////////////////////// + +class GpuPixelBuffer : public PixelBuffer { +public: + GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); + ~GpuPixelBuffer(); + + uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; + + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; + +protected: + void unmap() override; + +private: + GLuint mBuffer; + uint8_t* mMappedPointer; + Caches& mCaches; +}; + +GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) + : PixelBuffer(format, width, height) + , mMappedPointer(nullptr) + , mCaches(Caches::getInstance()) { + glGenBuffers(1, &mBuffer); + + mCaches.pixelBufferState().bind(mBuffer); + glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW); + mCaches.pixelBufferState().unbind(); +} + +GpuPixelBuffer::~GpuPixelBuffer() { + glDeleteBuffers(1, &mBuffer); +} + +uint8_t* GpuPixelBuffer::map(AccessMode mode) { + if (mAccessMode == kAccessMode_None) { + mCaches.pixelBufferState().bind(mBuffer); + mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); + if (CC_UNLIKELY(!mMappedPointer)) { + GLUtils::dumpGLErrors(); + LOG_ALWAYS_FATAL("Failed to map PBO"); + } + mAccessMode = mode; + mCaches.pixelBufferState().unbind(); + } + + return mMappedPointer; +} + +void GpuPixelBuffer::unmap() { + if (mAccessMode != kAccessMode_None) { + if (mMappedPointer) { + mCaches.pixelBufferState().bind(mBuffer); + GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + if (status == GL_FALSE) { + ALOGE("Corrupted GPU pixel buffer"); + } + } + mAccessMode = kAccessMode_None; + mMappedPointer = nullptr; + } +} + +void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { + // If the buffer is not mapped, unmap() will not bind it + mCaches.pixelBufferState().bind(mBuffer); + unmap(); + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, + reinterpret_cast(offset)); + mCaches.pixelBufferState().unbind(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Factory +/////////////////////////////////////////////////////////////////////////////// + +PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { + if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { + return new GpuPixelBuffer(format, width, height); + } + return new CpuPixelBuffer(format, width, height); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h new file mode 100644 index 000000000000..e7e341b90ad3 --- /dev/null +++ b/libs/hwui/PixelBuffer.h @@ -0,0 +1,198 @@ +/* + * 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_PIXEL_BUFFER_H +#define ANDROID_HWUI_PIXEL_BUFFER_H + +#include + +#include + +namespace android { +namespace uirenderer { + +/** + * Represents a pixel buffer. A pixel buffer will be backed either by a + * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other + * versions. If the buffer is backed by a PBO it will of type + * GL_PIXEL_UNPACK_BUFFER. + * + * To read from or write into a PixelBuffer you must first map the + * buffer using the map(AccessMode) method. This method returns a + * pointer to the beginning of the buffer. + * + * Before the buffer can be used by the GPU, for instance to upload + * a texture, you must first unmap the buffer. To do so, call the + * unmap() method. + * + * Mapping and unmapping a PixelBuffer can have the side effect of + * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is + * therefore recommended to call Caches::unbindPixelbuffer() after + * using a PixelBuffer to upload to a texture. + */ +class PixelBuffer { +public: + enum BufferType { kBufferType_Auto, kBufferType_CPU }; + + enum AccessMode { + kAccessMode_None = 0, + kAccessMode_Read = GL_MAP_READ_BIT, + kAccessMode_Write = GL_MAP_WRITE_BIT, + kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT + }; + + /** + * Creates a new PixelBuffer object with the specified format and + * dimensions. The buffer is immediately allocated. + * + * The buffer type specifies how the buffer should be allocated. + * By default this method will automatically choose whether to allocate + * a CPU or GPU buffer. + */ + static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height, + BufferType type = kBufferType_Auto); + + virtual ~PixelBuffer() {} + + /** + * Returns the format of this render buffer. + */ + GLenum getFormat() const { return mFormat; } + + /** + * Maps this before with the specified access mode. This method + * returns a pointer to the region of memory where the buffer was + * mapped. + * + * If the buffer is already mapped when this method is invoked, + * this method will return the previously mapped pointer. The + * access mode can only be changed by calling unmap() first. + * + * The specified access mode cannot be kAccessMode_None. + */ + virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0; + + /** + * Returns the current access mode for this buffer. If the buffer + * is not mapped, this method returns kAccessMode_None. + */ + AccessMode getAccessMode() const { return mAccessMode; } + + /** + * Upload the specified rectangle of this pixel buffer as a + * GL_TEXTURE_2D texture. Calling this method will trigger + * an unmap() if necessary. + */ + virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0; + + /** + * Upload the specified rectangle of this pixel buffer as a + * GL_TEXTURE_2D texture. Calling this method will trigger + * an unmap() if necessary. + * + * This is a convenience function provided to save callers the + * trouble of computing the offset parameter. + */ + void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + upload(x, y, width, height, getOffset(x, y)); + } + + /** + * Returns the width of the render buffer in pixels. + */ + uint32_t getWidth() const { return mWidth; } + + /** + * Returns the height of the render buffer in pixels. + */ + uint32_t getHeight() const { return mHeight; } + + /** + * Returns the size of this pixel buffer in bytes. + */ + uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); } + + /** + * Returns the offset of a pixel in this pixel buffer, in bytes. + */ + uint32_t getOffset(uint32_t x, uint32_t y) const { + return (y * mWidth + x) * formatSize(mFormat); + } + + /** + * Returns the number of bytes per pixel in the specified format. + * + * Supported formats: + * GL_ALPHA + * GL_RGBA + */ + static uint32_t formatSize(GLenum format) { + switch (format) { + case GL_ALPHA: + return 1; + case GL_RGBA: + return 4; + } + return 0; + } + + /** + * Returns the alpha channel offset in the specified format. + * + * Supported formats: + * GL_ALPHA + * GL_RGBA + */ + static uint32_t formatAlphaOffset(GLenum format) { + switch (format) { + case GL_ALPHA: + return 0; + case GL_RGBA: + return 3; + } + + ALOGE("unsupported format: %d", format); + return 0; + } + +protected: + /** + * Creates a new render buffer in the specified format and dimensions. + * The format must be GL_ALPHA or GL_RGBA. + */ + PixelBuffer(GLenum format, uint32_t width, uint32_t height) + : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {} + + /** + * Unmaps this buffer, if needed. After the buffer is unmapped, + * the pointer previously returned by map() becomes invalid and + * should not be used. + */ + virtual void unmap() = 0; + + GLenum mFormat; + + uint32_t mWidth; + uint32_t mHeight; + + AccessMode mAccessMode; + +}; // class PixelBuffer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_PIXEL_BUFFER_H diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 7966845ff814..0766e3b7ed28 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,6 +16,7 @@ #pragma once +#include "Caches.h" #include "DeviceInfo.h" #include "Outline.h" #include "Rect.h" diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 65bee476f14d..464a58d0c0f8 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -15,6 +15,7 @@ */ #include "ResourceCache.h" +#include "Caches.h" namespace android { @@ -111,9 +112,13 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; if (ref == nullptr) { // If we're not tracking this resource, just delete it - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; + if (Caches::hasInstance()) { + // DEAD CODE + } else { + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + delete[](int8_t*) resource; + } return; } ref->destroyed = true; @@ -130,10 +135,14 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource if (ref->destroyed) { switch (ref->resourceType) { case kNinePatch: { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; + if (Caches::hasInstance()) { + // DEAD CODE + } else { + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + int8_t* patch = (int8_t*)resource; + delete[] patch; + } } break; } } diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp new file mode 100644 index 000000000000..1e90eebe3bb8 --- /dev/null +++ b/libs/hwui/Texture.cpp @@ -0,0 +1,413 @@ +/* + * 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 "Texture.h" +#include "Caches.h" +#include "utils/GLUtils.h" +#include "utils/MathUtils.h" +#include "utils/TraceUtils.h" + +#include + +#include + +#include + +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 8; + default: + LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); + } +} + +void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { + if (force || wrapS != mWrapS || wrapT != mWrapT) { + mWrapS = wrapS; + mWrapT = wrapT; + + if (bindTexture) { + mCaches.textureState().bindTexture(mTarget, mId); + } + + glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); + } +} + +void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { + if (force || min != mMinFilter || mag != mMagFilter) { + mMinFilter = min; + mMagFilter = mag; + + if (bindTexture) { + mCaches.textureState().bindTexture(mTarget, mId); + } + + if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; + + glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); + glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); + } +} + +void Texture::deleteTexture() { + mCaches.textureState().deleteTexture(mId); + mId = 0; + mTarget = GL_NONE; + if (mEglImageHandle != EGL_NO_IMAGE_KHR) { + EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); + eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); + mEglImageHandle = EGL_NO_IMAGE_KHR; + } +} + +bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target) { + if (mWidth == width && mHeight == height && mFormat == format && + mInternalFormat == internalFormat && mTarget == target) { + return false; + } + mWidth = width; + mHeight = height; + mFormat = format; + mInternalFormat = internalFormat; + mTarget = target; + notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); + return true; +} + +void Texture::resetCachedParams() { + mWrapS = GL_REPEAT; + mWrapT = GL_REPEAT; + mMinFilter = GL_NEAREST_MIPMAP_LINEAR; + mMagFilter = GL_LINEAR; +} + +void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, + GLenum type, const void* pixels) { + GL_CHECKPOINT(MODERATE); + + // We don't have color space information, we assume the data is gamma encoded + mIsLinear = false; + + bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D); + if (!mId) { + glGenTextures(1, &mId); + needsAlloc = true; + resetCachedParams(); + } + mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); + if (needsAlloc) { + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); + } else if (pixels) { + glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); + } + GL_CHECKPOINT(MODERATE); +} + +void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { + EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); + if (mEglImageHandle != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); + mEglImageHandle = EGL_NO_IMAGE_KHR; + } + mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + buffer->getNativeBuffer(), 0); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); +} + +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(); + if ((stride == width) || useStride) { + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + } + + if (resize) { + 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); + } + + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + } else { + // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer + // if the stride doesn't match the width + + GLvoid* temp = (GLvoid*)malloc(width * height * bpp); + if (!temp) return; + + uint8_t* pDst = (uint8_t*)temp; + uint8_t* pSrc = (uint8_t*)data; + for (GLsizei i = 0; i < height; i++) { + memcpy(pDst, pSrc, width * bpp); + pDst += width * bpp; + pSrc += stride * bpp; + } + + if (resize) { + 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); + } + + free(temp); + } +} + +void Texture::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: + 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 is upconverted to RGBA_8888 + case kARGB_4444_SkColorType: + case kN32_SkColorType: + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(needSRGB); + *outType = GL_UNSIGNED_BYTE; + break; + case kGray_8_SkColorType: + *outFormat = GL_LUMINANCE; + *outInternalFormat = GL_LUMINANCE; + *outType = GL_UNSIGNED_BYTE; + break; + case kRGBA_F16_SkColorType: + if (caches.extensions().getMajorGlVersion() >= 3) { + // This format is always linear + *outFormat = GL_RGBA; + *outInternalFormat = GL_RGBA16F; + *outType = GL_HALF_FLOAT; + } else { + *outFormat = GL_RGBA; + *outInternalFormat = caches.rgbaInternalFormat(true); + *outType = GL_UNSIGNED_BYTE; + } + break; + default: + LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); + break; + } +} + +SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, + sk_sp sRGB) { + SkBitmap rgbaBitmap; + rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), + bitmap.info().alphaType(), + hasLinearBlending ? sRGB : nullptr)); + rgbaBitmap.eraseColor(0); + + if (bitmap.colorType() == kRGBA_F16_SkColorType) { + // Drawing RGBA_F16 onto ARGB_8888 is not supported + bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), + rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); + } else { + SkCanvas canvas(rgbaBitmap); + canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); + } + + return rgbaBitmap; +} + +bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) { + return info.colorType() == kARGB_4444_SkColorType || + (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && + info.colorSpace()->isSRGB()) || + (info.colorType() == kRGBA_F16_SkColorType && + Caches::getInstance().extensions().getMajorGlVersion() < 3); +} + +void Texture::upload(Bitmap& bitmap) { + ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); + + // We could also enable mipmapping if both bitmap dimensions are powers + // of 2 but we'd have to deal with size changes. Let's keep this simple + const bool canMipMap = mCaches.extensions().hasNPot(); + + // If the texture had mipmap enabled but not anymore, + // force a glTexImage2D to discard the mipmap levels + bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); + bool setDefaultParams = false; + + if (!mId) { + glGenTextures(1, &mId); + needsAlloc = true; + setDefaultParams = true; + } + + bool hasLinearBlending = mCaches.extensions().hasLinearBlending(); + bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace()); + + GLint internalFormat, format, type; + colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending, + &internalFormat, &format, &type); + + // Some devices don't support GL_RGBA16F, so we need to compare the color type + // and internal GL format to decide what to do with 16 bit bitmaps + bool rgba16fNeedsConversion = + bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F; + + // RGBA16F is always linear extended sRGB + if (internalFormat == GL_RGBA16F) { + mIsLinear = true; + } + + mConnector.reset(); + + // Alpha masks don't have color profiles + // If an RGBA16F bitmap needs conversion, we know the target will be sRGB + if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) { + SkColorSpace* colorSpace = bitmap.info().colorSpace(); + // If the bitmap is sRGB we don't need conversion + if (colorSpace != nullptr && !colorSpace->isSRGB()) { + SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); + if (!colorSpace->toXYZD50(&xyzMatrix)) { + ALOGW("Incompatible color space!"); + } else { + SkColorSpaceTransferFn fn; + if (!colorSpace->isNumericalTransferFn(&fn)) { + ALOGW("Incompatible color space, no numerical transfer function!"); + } else { + float data[16]; + xyzMatrix.asColMajorf(data); + + ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC, + fn.fD, fn.fE, fn.fF}; + ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p); + mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB())); + + // A non-sRGB color space might have a transfer function close enough to sRGB + // that we can save shader instructions by using an sRGB sampler + // This is only possible if we have hardware support for sRGB textures + if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() && + !bitmap.isHardware()) { + internalFormat = GL_SRGB8_ALPHA8; + } + } + } + } + } + + GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target); + + blend = !bitmap.isOpaque(); + mCaches.textureState().bindTexture(mTarget, mId); + + // TODO: Handle sRGB gray bitmaps + if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) { + SkBitmap skBitmap; + bitmap.getSkBitmap(&skBitmap); + sk_sp sRGB = SkColorSpace::MakeSRGB(); + SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); + uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), + rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), + rgbaBitmap.getPixels()); + } else if (bitmap.isHardware()) { + uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); + } else { + uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), + bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), + bitmap.pixels()); + } + + if (canMipMap) { + mipMap = bitmap.hasHardwareMipMap(); + if (mipMap) { + glGenerateMipmap(GL_TEXTURE_2D); + } + } + + if (setDefaultParams) { + setFilter(GL_NEAREST); + setWrap(GL_CLAMP_TO_EDGE); + } +} + +void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target) { + mId = id; + mWidth = width; + mHeight = height; + mFormat = format; + mInternalFormat = internalFormat; + mTarget = target; + mConnector.reset(); + // We're wrapping an existing texture, so don't double count this memory + notifySizeChanged(0); +} + +TransferFunctionType Texture::getTransferFunctionType() const { + if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) { + const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters(); + if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) { + if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) && + MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { + if (MathUtils::areEqual(p.g, 1.0f)) { + return TransferFunctionType::None; + } + return TransferFunctionType::Gamma; + } + return TransferFunctionType::Limited; + } + return TransferFunctionType::Full; + } + return TransferFunctionType::None; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h new file mode 100644 index 000000000000..5b7e4e261f30 --- /dev/null +++ b/libs/hwui/Texture.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010 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_TEXTURE_H +#define ANDROID_HWUI_TEXTURE_H + +#include "GpuMemoryTracker.h" +#include "hwui/Bitmap.h" +#include "utils/Color.h" + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +namespace android { + +class GraphicBuffer; + +namespace uirenderer { + +class Caches; +class UvMapper; +class Layer; + +/** + * Represents an OpenGL texture. + */ +class Texture : public GpuMemoryTracker { +public: + static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, + sk_sp sRGB); + static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending); + static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, + bool needSRGB, GLint* outInternalFormat, + GLint* outFormat, GLint* outType); + + explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {} + + virtual ~Texture() {} + + inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { + setWrapST(wrap, wrap, bindTexture, force); + } + + virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, + bool force = false); + + inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { + setFilterMinMag(filter, filter, bindTexture, force); + } + + virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, + bool force = false); + + /** + * Convenience method to call glDeleteTextures() on this texture's id. + */ + void deleteTexture(); + + /** + * Sets the width, height, and format of the texture along with allocating + * the texture ID. Does nothing if the width, height, and format are already + * the requested values. + * + * The image data is undefined after calling this. + */ + void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { + upload(internalFormat, width, height, format, + internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); + } + + /** + * Updates this Texture with the contents of the provided Bitmap, + * also setting the appropriate width, height, and format. It is not necessary + * to call resize() prior to this. + * + * Note this does not set the generation from the Bitmap. + */ + void upload(Bitmap& source); + + /** + * Basically glTexImage2D/glTexSubImage2D. + */ + 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 internalFormat, GLint format, + GLenum target); + + GLuint id() const { return mId; } + + uint32_t width() const { return mWidth; } + + uint32_t height() const { return mHeight; } + + GLint format() const { return mFormat; } + + GLint internalFormat() const { return mInternalFormat; } + + GLenum target() const { return mTarget; } + + /** + * Returns nullptr if this texture does not require color space conversion + * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion + * is required. + */ + constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); } + + constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; } + + TransferFunctionType getTransferFunctionType() const; + + /** + * Returns true if this texture uses a linear encoding format. + */ + constexpr bool isLinear() const { return mIsLinear; } + + /** + * Generation of the backing bitmap, + */ + uint32_t generation = 0; + /** + * Indicates whether the texture requires blending. + */ + bool blend = false; + /** + * Indicates whether this texture should be cleaned up after use. + */ + bool cleanup = false; + /** + * Optional, size of the original bitmap. + */ + uint32_t bitmapSize = 0; + /** + * Indicates whether this texture will use trilinear filtering. + */ + bool mipMap = false; + + /** + * Optional, pointer to a texture coordinates mapper. + */ + const UvMapper* uvMapper = nullptr; + + /** + * Whether or not the Texture is marked in use and thus not evictable for + * the current frame. This is reset at the start of a new frame. + */ + void* isInUse = nullptr; + +private: + // TODO: Temporarily grant private access to GlLayer, remove once + // GlLayer can be de-tangled from being a dual-purpose render target + // and external texture wrapper + friend class GlLayer; + + // Returns true if the texture layout (size, format, etc.) changed, false if it was the same + bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, + GLenum target); + void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); + void resetCachedParams(); + + GLuint mId = 0; + uint32_t mWidth = 0; + uint32_t mHeight = 0; + GLint mFormat = 0; + GLint mInternalFormat = 0; + GLenum mTarget = GL_NONE; + EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR; + + /* See GLES spec section 3.8.14 + * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is + * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR. + * s, t, and r wrap modes are all set to REPEAT." + */ + GLenum mWrapS = GL_REPEAT; + GLenum mWrapT = GL_REPEAT; + GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR; + GLenum mMagFilter = GL_LINEAR; + + // Indicates whether the content of the texture is in linear space + bool mIsLinear = false; + + Caches& mCaches; + + std::unique_ptr mConnector; +}; // struct Texture + +class AutoTexture { +public: + explicit AutoTexture(Texture* texture) : texture(texture) {} + ~AutoTexture() { + if (texture && texture->cleanup) { + texture->deleteTexture(); + delete texture; + } + } + + Texture* const texture; +}; // class AutoTexture + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp new file mode 100644 index 000000000000..30fba7ae7d9b --- /dev/null +++ b/libs/hwui/VkLayer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 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 "VkLayer.h" + +#include "renderstate/RenderState.h" + +#include +#include + +namespace android { +namespace uirenderer { + +void VkLayer::updateTexture() { + sk_sp surface; + SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType); + surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info); + surface->getCanvas()->clear(SK_ColorBLUE); + mImage = surface->makeImageSnapshot(); +} + +void VkLayer::onVkContextDestroyed() { + mImage = nullptr; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h new file mode 100644 index 000000000000..e9664d04b7a5 --- /dev/null +++ b/libs/hwui/VkLayer.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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. + */ + +#pragma once + +#include "Layer.h" + +#include + +namespace android { +namespace uirenderer { +/** + * A layer has dimensions and is backed by a VkImage. + */ +class VkLayer : public Layer { +public: + VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) + : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode) + , mWidth(layerWidth) + , mHeight(layerHeight) + , mBlend(blend) {} + + virtual ~VkLayer() {} + + uint32_t getWidth() const override { return mWidth; } + + uint32_t getHeight() const override { return mHeight; } + + void setSize(uint32_t width, uint32_t height) override { + mWidth = width; + mHeight = height; + } + + void setBlend(bool blend) override { mBlend = blend; } + + bool isBlend() const override { return mBlend; } + + sk_sp getImage() { return mImage; } + + void updateTexture(); + + // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to + // destroy any VkImages that were made with that context. + void onVkContextDestroyed(); + +private: + int mWidth; + int mHeight; + bool mBlend; + + sk_sp mImage; + +}; // struct VkLayer + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 3939696692d2..a7d37f8aa45c 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -15,11 +15,11 @@ */ #include "Bitmap.h" +#include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" -#include #include diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index fb66b50f0159..c41f6a6f0ee6 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -15,6 +15,8 @@ */ #include "LayerDrawable.h" +#include "GlLayer.h" +#include "VkLayer.h" #include "GrBackendSurface.h" #include "SkColorFilter.h" @@ -39,14 +41,35 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer return false; } // transform the matrix based on the layer - SkMatrix layerTransform = layer->getTransform(); - sk_sp layerImage = layer->getImage(); + SkMatrix layerTransform; + layer->getTransform().copyTo(layerTransform); + sk_sp layerImage; const int layerWidth = layer->getWidth(); const int layerHeight = layer->getHeight(); + if (layer->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layer); + GrGLTextureInfo externalTexture; + externalTexture.fTarget = glLayer->getRenderTarget(); + externalTexture.fID = glLayer->getTextureId(); + // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't + // expose that info we use it as our default. Further, given that we only use this texture + // as a source this will not impact how Skia uses the texture. The only potential affect + // this is anticipated to have is that for some format types if we are not bound as an OES + // texture we may get invalid results for SKP capture if we read back the texture. + externalTexture.fFormat = GL_RGBA8; + GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture); + layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); + } else { + SkASSERT(layer->getApi() == Layer::Api::Vulkan); + VkLayer* vkLayer = static_cast(layer); + canvas->clear(SK_ColorGREEN); + layerImage = vkLayer->getImage(); + } if (layerImage) { SkMatrix textureMatrixInv; - textureMatrixInv = layer->getTexTransform(); + layer->getTexTransform().copyTo(textureMatrixInv); // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed // use bottom left origin and remove flipV and invert transformations. SkMatrix flipV; @@ -72,9 +95,6 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(layer->getColorSpaceWithFilter()); - if (layer->getForceFilter()) { - paint.setFilterQuality(kLow_SkFilterQuality); - } const bool nonIdentityMatrix = !matrix.isIdentity(); if (nonIdentityMatrix) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 2ae37233098e..78f5a71dee3b 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,6 +17,7 @@ #include "SkiaOpenGLPipeline.h" #include "DeferredLayerUpdater.h" +#include "GlLayer.h" #include "LayerDrawable.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" @@ -186,9 +187,18 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi return false; } +static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, + bool blend) { + GlLayer* layer = + new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); + layer->generateTexture(); + return layer; +} + DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); - return new DeferredLayerUpdater(mRenderThread.renderState()); + return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL); } void SkiaOpenGLPipeline::onStop() { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 5f2eee4523fc..b2519fe59891 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -20,6 +20,7 @@ #include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" +#include "VkLayer.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" @@ -113,10 +114,16 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi return false; } +static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, + sk_sp colorFilter, int alpha, SkBlendMode mode, + bool blend) { + return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); +} + DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { mVkManager.initialize(); - return new DeferredLayerUpdater(mRenderThread.renderState()); + return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan); } void SkiaVulkanPipeline::onStop() {} diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp new file mode 100644 index 000000000000..3a6efb833c47 --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 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 "renderstate/PixelBufferState.h" + +namespace android { +namespace uirenderer { + +PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {} + +bool PixelBufferState::bind(GLuint buffer) { + if (mCurrentPixelBuffer != buffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); + mCurrentPixelBuffer = buffer; + return true; + } + return false; +} + +bool PixelBufferState::unbind() { + if (mCurrentPixelBuffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + mCurrentPixelBuffer = 0; + return true; + } + return false; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h new file mode 100644 index 000000000000..f7ae6c575f6a --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 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 RENDERSTATE_PIXELBUFFERSTATE_H +#define RENDERSTATE_PIXELBUFFERSTATE_H + +#include + +namespace android { +namespace uirenderer { + +class PixelBufferState { + friend class Caches; // TODO: move to RenderState +public: + bool bind(GLuint buffer); + bool unbind(); + +private: + PixelBufferState(); + GLuint mCurrentPixelBuffer; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index b524bcb096da..3be84f588a20 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,6 +16,8 @@ #include "renderstate/RenderState.h" #include #include "DeferredLayerUpdater.h" +#include "GlLayer.h" +#include "VkLayer.h" #include "Snapshot.h" #include "renderthread/CanvasContext.h" @@ -37,11 +39,44 @@ RenderState::RenderState(renderthread::RenderThread& thread) RenderState::~RenderState() { } -void RenderState::onContextCreated() { +void RenderState::onGLContextCreated() { GpuMemoryTracker::onGpuContextCreated(); + + // This is delayed because the first access of Caches makes GL calls + if (!mCaches) { + mCaches = &Caches::createInstance(*this); + } + mCaches->init(); } -void RenderState::onContextDestroyed() { +static void layerLostGlContext(Layer* layer) { + LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, + "layerLostGlContext on non GL layer"); + static_cast(layer)->onGlContextLost(); +} + +void RenderState::onGLContextDestroyed() { + // TODO: reset all cached state in state objects + std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); + + mCaches->terminate(); + + destroyLayersInUpdater(); + GpuMemoryTracker::onGpuContextDestroyed(); +} + +void RenderState::onVkContextCreated() { + GpuMemoryTracker::onGpuContextCreated(); +} + +static void layerDestroyedVkContext(Layer* layer) { + LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan, + "layerLostVkContext on non Vulkan layer"); + static_cast(layer)->onVkContextDestroyed(); +} + +void RenderState::onVkContextDestroyed() { + std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); destroyLayersInUpdater(); GpuMemoryTracker::onGpuContextDestroyed(); } @@ -50,6 +85,10 @@ GrContext* RenderState::getGrContext() const { return mRenderThread.getGrContext(); } +void RenderState::flush(Caches::FlushMode mode) { + if (mCaches) mCaches->flush(mode); +} + void RenderState::onBitmapDestroyed(uint32_t pixelRefId) { // DEAD CODE } @@ -87,6 +126,42 @@ void RenderState::deleteFramebuffer(GLuint fbo) { glDeleteFramebuffers(1, &fbo); } +void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { + if (mode == DrawGlInfo::kModeProcessNoContext) { + // If there's no context we don't need to interrupt as there's + // no gl state to save/restore + (*functor)(mode, info); + } else { + interruptForFunctorInvoke(); + (*functor)(mode, info); + resumeFromFunctorInvoke(); + } +} + +void RenderState::interruptForFunctorInvoke() { + mCaches->textureState().resetActiveTexture(); + debugOverdraw(false, false); + // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) + if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + } +} + +void RenderState::resumeFromFunctorInvoke() { + if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { + glEnable(GL_FRAMEBUFFER_SRGB_EXT); + } + + glViewport(0, 0, mViewportWidth, mViewportHeight); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + debugOverdraw(false, false); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + mCaches->textureState().activateTexture(0); + mCaches->textureState().resetBoundTextures(); +} + void RenderState::debugOverdraw(bool enable, bool clear) { // DEAD CODE } @@ -115,9 +190,5 @@ void RenderState::dump() { // DEAD CODE } -renderthread::RenderThread& RenderState::getRenderThread() { - return mRenderThread; -} - } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index f39aa4b96547..97785a46dcd7 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,6 +16,8 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H +#include "Caches.h" +#include "renderstate/PixelBufferState.h" #include "utils/Macros.h" #include @@ -32,6 +34,7 @@ class GrContext; namespace android { namespace uirenderer { +class Caches; class Layer; class DeferredLayerUpdater; @@ -41,16 +44,22 @@ class CanvasContext; class RenderThread; } +// TODO: Replace Cache's GL state tracking with this. For now it's more a thin // wrapper of Caches for users to migrate to. class RenderState { PREVENT_COPY_AND_ASSIGN(RenderState); friend class renderthread::RenderThread; + friend class Caches; friend class renderthread::CacheManager; public: - void onContextCreated(); - void onContextDestroyed(); + void onGLContextCreated(); + void onGLContextDestroyed(); + void onVkContextCreated(); + void onVkContextDestroyed(); + + void flush(Caches::FlushMode flushMode); void onBitmapDestroyed(uint32_t pixelRefId); void setViewport(GLsizei width, GLsizei height); @@ -61,6 +70,8 @@ public: GLuint createFramebuffer(); void deleteFramebuffer(GLuint fbo); + void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); + void debugOverdraw(bool enable, bool clear); void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } @@ -90,15 +101,16 @@ public: void dump(); - renderthread::RenderThread& getRenderThread(); - private: + void interruptForFunctorInvoke(); + void resumeFromFunctorInvoke(); void destroyLayersInUpdater(); explicit RenderState(renderthread::RenderThread& thread); ~RenderState(); renderthread::RenderThread& mRenderThread; + Caches* mCaches = nullptr; std::set mActiveLayers; std::set mActiveLayerUpdaters; diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp new file mode 100644 index 000000000000..470b4f5de97f --- /dev/null +++ b/libs/hwui/renderstate/TextureState.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 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 "renderstate/TextureState.h" + +#include "Caches.h" +#include "utils/TraceUtils.h" + +#include +#include +#include +#include + +namespace android { +namespace uirenderer { + +// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is +static const int SHADOW_LUT_SIZE = 128; + +// Must define as many texture units as specified by kTextureUnitsCount +const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3}; + +TextureState::TextureState() : mTextureUnit(0) { + glActiveTexture(kTextureUnits[0]); + resetBoundTextures(); + + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, + "At least %d texture units are required!", kTextureUnitsCount); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +} + +TextureState::~TextureState() { + if (mShadowLutTexture != nullptr) { + mShadowLutTexture->deleteTexture(); + } +} + +/** + * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to + * darkness at that spot. Input values of 0->1 should be mapped within the same + * range, but can affect the curve for a different visual falloff. + * + * This is used to populate the shadow LUT texture for quick lookup in the + * shadow shader. + */ +static float computeShadowOpacity(float ratio) { + // exponential falloff function provided by UX + float val = 1 - ratio; + return exp(-val * val * 4.0) - 0.018; +} + +void TextureState::constructTexture(Caches& caches) { + if (mShadowLutTexture == nullptr) { + mShadowLutTexture.reset(new Texture(caches)); + + unsigned char bytes[SHADOW_LUT_SIZE]; + for (int i = 0; i < SHADOW_LUT_SIZE; i++) { + float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f); + bytes[i] = computeShadowOpacity(inputRatio) * 255; + } + mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes); + mShadowLutTexture->setFilter(GL_LINEAR); + mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE); + } +} + +void TextureState::activateTexture(GLuint textureUnit) { + LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, + "Tried to use texture unit index %d, only %d exist", textureUnit, + kTextureUnitsCount); + if (mTextureUnit != textureUnit) { + glActiveTexture(kTextureUnits[textureUnit]); + mTextureUnit = textureUnit; + } +} + +void TextureState::resetActiveTexture() { + mTextureUnit = -1; +} + +void TextureState::bindTexture(GLuint texture) { + if (mBoundTextures[mTextureUnit] != texture) { + glBindTexture(GL_TEXTURE_2D, texture); + mBoundTextures[mTextureUnit] = texture; + } +} + +void TextureState::bindTexture(GLenum target, GLuint texture) { + if (target == GL_TEXTURE_2D) { + bindTexture(texture); + } else { + // GLConsumer directly calls glBindTexture() with + // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target + // since the cached state could be stale + glBindTexture(target, texture); + } +} + +void TextureState::deleteTexture(GLuint texture) { + // When glDeleteTextures() is called on a currently bound texture, + // OpenGL ES specifies that the texture is then considered unbound + // Consider the following series of calls: + // + // glGenTextures -> creates texture name 2 + // glBindTexture(2) + // glDeleteTextures(2) -> 2 is now unbound + // glGenTextures -> can return 2 again + // + // If we don't call glBindTexture(2) after the second glGenTextures + // call, any texture operation will be performed on the default + // texture (name=0) + + unbindTexture(texture); + + glDeleteTextures(1, &texture); +} + +void TextureState::resetBoundTextures() { + for (int i = 0; i < kTextureUnitsCount; i++) { + mBoundTextures[i] = 0; + } +} + +void TextureState::unbindTexture(GLuint texture) { + for (int i = 0; i < kTextureUnitsCount; i++) { + if (mBoundTextures[i] == texture) { + mBoundTextures[i] = 0; + } + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h new file mode 100644 index 000000000000..f1996d431fa2 --- /dev/null +++ b/libs/hwui/renderstate/TextureState.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 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 RENDERSTATE_TEXTURESTATE_H +#define RENDERSTATE_TEXTURESTATE_H + +#include "Texture.h" +#include "Vertex.h" + +#include +#include +#include + +namespace android { +namespace uirenderer { + +class Texture; + +class TextureState { + friend class Caches; // TODO: move to RenderState +public: + void constructTexture(Caches& caches); + + /** + * Activate the specified texture unit. The texture unit must + * be specified using an integer number (0 for GL_TEXTURE0 etc.) + */ + void activateTexture(GLuint textureUnit); + + /** + * Invalidate the cached value of the active texture unit. + */ + void resetActiveTexture(); + + /** + * Binds the specified texture as a GL_TEXTURE_2D texture. + * All texture bindings must be performed with this method or + * bindTexture(GLenum, GLuint). + */ + void bindTexture(GLuint texture); + + /** + * Binds the specified texture with the specified render target. + * All texture bindings must be performed with this method or + * bindTexture(GLuint). + */ + void bindTexture(GLenum target, GLuint texture); + + /** + * Deletes the specified texture and clears it from the cache + * of bound textures. + * All textures must be deleted using this method. + */ + void deleteTexture(GLuint texture); + + /** + * Signals that the cache of bound textures should be cleared. + * Other users of the context may have altered which textures are bound. + */ + void resetBoundTextures(); + + /** + * Clear the cache of bound textures. + */ + void unbindTexture(GLuint texture); + + Texture* getShadowLutTexture() { return mShadowLutTexture.get(); } + +private: + // total number of texture units available for use + static const int kTextureUnitsCount = 4; + + TextureState(); + ~TextureState(); + GLuint mTextureUnit; + + // Caches texture bindings for the GL_TEXTURE_2D target + GLuint mBoundTextures[kTextureUnitsCount]; + + std::unique_ptr mShadowLutTexture; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index c45eedad775c..bec80b1e6011 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -21,7 +21,6 @@ #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" -#include "Properties.h" #include "renderstate/RenderState.h" #include @@ -215,12 +214,11 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat(" Layer Info:\n"); } - const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL - ? "GlLayer" : "VkLayer"; size_t layerMemoryTotal = 0; for (std::set::iterator it = renderState->mActiveLayers.begin(); it != renderState->mActiveLayers.end(); it++) { const Layer* layer = *it; + const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8b07d1dadeb6..5d7252304bf2 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,6 +18,7 @@ #include #include "AnimationContext.h" +#include "Caches.h" #include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" @@ -494,6 +495,13 @@ void CanvasContext::draw() { } GpuMemoryTracker::onFrameCompleted(); +#ifdef BUGREPORT_FONT_CACHE_USAGE + auto renderType = Properties::getRenderPipelineType(); + if (RenderPipelineType::OpenGL == renderType) { + Caches& caches = Caches::getInstance(); + caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); + } +#endif } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 5f8d7ad3373a..cd21822df5b1 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include "utils/StringUtils.h" @@ -465,109 +464,6 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } -status_t EglManager::fenceWait(sp& fence) { - if (!hasEglContext()) { - ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); - return INVALID_OPERATION; - } - - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { - // Block GPU on the fence. - // Create an EGLSyncKHR from the current fence. - int fenceFd = fence->dup(); - if (fenceFd == -1) { - ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - EGLint attribs[] = { - EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, - EGL_NONE - }; - EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, - EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - close(fenceFd); - ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - - // XXX: The spec draft is inconsistent as to whether this should - // return an EGLint or void. Ignore the return value for now, as - // it's not strictly needed. - eglWaitSyncKHR(mEglDisplay, sync, 0); - EGLint eglErr = eglGetError(); - eglDestroySyncKHR(mEglDisplay, sync); - if (eglErr != EGL_SUCCESS) { - ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr); - return UNKNOWN_ERROR; - } - } else { - // Block CPU on the fence. - status_t err = fence->waitForever("EglManager::fenceWait"); - if (err != NO_ERROR) { - ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); - return err; - } - } - return OK; -} - -status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, - sp& nativeFence) { - if (!hasEglContext()) { - ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); - return INVALID_OPERATION; - } - - if (SyncFeatures::getInstance().useNativeFenceSync()) { - EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, - EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync); - eglDestroySyncKHR(mEglDisplay, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - ALOGE("EglManager::createReleaseFence: error dup'ing native fence " - "fd: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - nativeFence = new Fence(fenceFd); - *eglFence = EGL_NO_SYNC_KHR; - } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { - if (*eglFence != EGL_NO_SYNC_KHR) { - // There is already a fence for the current slot. We need to - // wait on that before replacing it with another fence to - // ensure that all outstanding buffer accesses have completed - // before the producer accesses it. - EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000); - if (result == EGL_FALSE) { - ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); - return TIMED_OUT; - } - eglDestroySyncKHR(mEglDisplay, *eglFence); - } - - // Create a fence for the outstanding accesses in the current - // OpenGL ES context. - *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr); - if (*eglFence == EGL_NO_SYNC_KHR) { - ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - } - return OK; -} - } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 507673adf26e..8e8bb8b68a1c 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -17,10 +17,8 @@ #define EGLMANAGER_H #include -#include #include #include -#include #include #include @@ -68,14 +66,6 @@ public: EGLDisplay eglDisplay() const { return mEglDisplay; } - // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension - // support is missing, block the CPU on the fence. - status_t fenceWait(sp& fence); - - // Creates a fence that is signaled, when all the pending GL commands are flushed. - // Depending on installed extensions, the result is either Android native fence or EGL fence. - status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp& nativeFence); - private: void initExtensions(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 65f95ad3f0d4..c1284ec02655 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -178,7 +178,7 @@ void RenderThread::requireGlContext() { return; } mEglManager->initialize(); - renderState().onContextCreated(); + renderState().onGLContextCreated(); #ifdef HWUI_GLES_WRAP_ENABLED debug::GlesDriver* driver = debug::GlesDriver::get(); @@ -200,7 +200,7 @@ void RenderThread::requireGlContext() { void RenderThread::destroyGlContext() { if (mEglManager->hasEglContext()) { setGrContext(nullptr); - renderState().onContextDestroyed(); + renderState().onGLContextDestroyed(); mEglManager->destroy(); } } diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 0c49dc03425e..1517f579a084 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -40,7 +40,7 @@ namespace renderthread { VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { - mRenderThread.renderState().onContextDestroyed(); + mRenderThread.renderState().onVkContextDestroyed(); mRenderThread.setGrContext(nullptr); if (VK_NULL_HANDLE != mCommandPool) { @@ -404,7 +404,7 @@ void VulkanManager::initialize() { mSwapBehavior = SwapBehavior::BufferAge; } - mRenderThread.renderState().onContextCreated(); + mRenderThread.renderState().onVkContextCreated(); } // Returns the next BackbufferInfo to use for the next draw. The function will make sure all @@ -981,22 +981,6 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } -status_t VulkanManager::fenceWait(sp& fence) { - //TODO: Insert a wait on fence command into the Vulkan command buffer. - // Block CPU on the fence. - status_t err = fence->waitForever("VulkanManager::fenceWait"); - if (err != NO_ERROR) { - ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); - return err; - } - return OK; -} - -status_t VulkanManager::createReleaseFence(sp& nativeFence) { - //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. - return OK; -} - } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index ebc11a50685e..5524c39d7a0c 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -23,8 +23,6 @@ #include #include -#include -#include #include class GrVkExtensions; @@ -112,12 +110,6 @@ public: // Presents the current VkImage. void swapBuffers(VulkanSurface* surface); - // Inserts a wait on fence command into the Vulkan command buffer. - status_t fenceWait(sp& fence); - - // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. - status_t createReleaseFence(sp& nativeFence); - private: friend class RenderThread; diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp deleted file mode 100644 index c8220c6cb0d4..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.cpp +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (C) 2018 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include "EGLConsumer.h" -#include "SurfaceTexture.h" - -#include -#include -#include - -#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" -#define EGL_PROTECTED_CONTENT_EXT 0x32C0 - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -static const struct { - uint32_t width, height; - char const* bits; -} kDebugData = {15, 12, - "_______________" - "_______________" - "_____XX_XX_____" - "__X_X_____X_X__" - "__X_XXXXXXX_X__" - "__XXXXXXXXXXX__" - "___XX_XXX_XX___" - "____XXXXXXX____" - "_____X___X_____" - "____X_____X____" - "_______________" - "_______________"}; - -Mutex EGLConsumer::sStaticInitLock; -sp EGLConsumer::sReleasedTexImageBuffer; - -static bool hasEglProtectedContentImpl() { - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); - bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); - bool atEnd = (cropExtLen + 1) < extsLen && - !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); - bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); - return equal || atStart || atEnd || inMiddle; -} - -static bool hasEglProtectedContent() { - // Only compute whether the extension is present once the first time this - // function is called. - static bool hasIt = hasEglProtectedContentImpl(); - return hasIt; -} - -EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} - -status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - return err; - } - - BufferItem item; - - // Acquire the next buffer. - // In asynchronous mode the list is guaranteed to be one buffer - // deep, while in synchronous mode we use the oldest buffer. - err = st.acquireBufferLocked(&item, 0); - if (err != NO_ERROR) { - if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - // We always bind the texture even if we don't update its contents. - EGC_LOGV("updateTexImage: no buffers were available"); - glBindTexture(st.mTexTarget, st.mTexName); - err = NO_ERROR; - } else { - EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); - } - return err; - } - - // Release the previous buffer. - err = updateAndReleaseLocked(item, nullptr, st); - if (err != NO_ERROR) { - // We always bind the texture. - glBindTexture(st.mTexTarget, st.mTexName); - return err; - } - - // Bind the new buffer to the GL texture, and wait until it's ready. - return bindTextureImageLocked(st); -} - -status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { - // Make sure the EGL state is the same as in previous calls. - status_t err = NO_ERROR; - - // if we're detached, no need to validate EGL's state -- we won't use it. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - err = checkAndUpdateEglStateLocked(st, true); - if (err != NO_ERROR) { - return err; - } - } - - // Update the EGLConsumer state. - int buf = st.mCurrentTexture; - if (buf != BufferQueue::INVALID_BUFFER_SLOT) { - EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); - - // if we're detached, we just use the fence that was created in detachFromContext() - // so... basically, nothing more to do here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // Do whatever sync ops we need to do before releasing the slot. - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); - return err; - } - } - - err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - if (err < NO_ERROR) { - EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); - return err; - } - - if (mReleasedTexImage == nullptr) { - mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); - } - - st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - mCurrentTextureImage = mReleasedTexImage; - st.mCurrentCrop.makeInvalid(); - st.mCurrentTransform = 0; - st.mCurrentTimestamp = 0; - st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; - st.mCurrentFence = Fence::NO_FENCE; - st.mCurrentFenceTime = FenceTime::NO_FENCE; - - // detached, don't touch the texture (and we may not even have an - // EGLDisplay here. - if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { - // This binds a dummy buffer (mReleasedTexImage). - status_t result = bindTextureImageLocked(st); - if (result != NO_ERROR) { - return result; - } - } - } - - return NO_ERROR; -} - -sp EGLConsumer::getDebugTexImageBuffer() { - Mutex::Autolock _l(sStaticInitLock); - if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { - // The first time, create the debug texture in case the application - // continues to use it. - sp buffer = new GraphicBuffer( - kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, - GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); - uint32_t* bits; - buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast(&bits)); - uint32_t stride = buffer->getStride(); - uint32_t height = buffer->getHeight(); - memset(bits, 0, stride * height * 4); - for (uint32_t y = 0; y < kDebugData.height; y++) { - for (uint32_t x = 0; x < kDebugData.width; x++) { - bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 - : 0xFFFFFFFF; - } - bits += stride; - } - buffer->unlock(); - sReleasedTexImageBuffer = buffer; - } - return sReleasedTexImageBuffer; -} - -void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior EglImage created is using a stale buffer. This - // replaces any old EglImage with a new one (using the new buffer). - int slot = item->mSlot; - if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } -} - -void EGLConsumer::onReleaseBufferLocked(int buf) { - mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; -} - -status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st) { - status_t err = NO_ERROR; - - int slot = item.mSlot; - - if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { - EGC_LOGE( - "updateAndRelease: EGLConsumer is not attached to an OpenGL " - "ES context"); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return INVALID_OPERATION; - } - - // Confirm state. - err = checkAndUpdateEglStateLocked(st); - if (err != NO_ERROR) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return err; - } - - // Ensure we have a valid EglImageKHR for the slot, creating an EglImage - // if nessessary, for the gralloc buffer currently in the slot in - // ConsumerBase. - // We may have to do this even when item.mGraphicBuffer == NULL (which - // means the buffer was previously acquired). - err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, - slot); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); - return UNKNOWN_ERROR; - } - - // Do whatever sync ops we need to do before releasing the old slot. - if (slot != st.mCurrentTexture) { - err = syncForReleaseLocked(mEglDisplay, st); - if (err != NO_ERROR) { - // Release the buffer we just acquired. It's not safe to - // release the old buffer, so instead we just drop the new frame. - // As we are still under lock since acquireBuffer, it is safe to - // release by slot. - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); - return err; - } - } - - EGC_LOGV( - "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, - mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, - slot, st.mSlots[slot].mGraphicBuffer->handle); - - // Hang onto the pointer so that it isn't freed in the call to - // releaseBufferLocked() if we're in shared buffer mode and both buffers are - // the same. - sp nextTextureImage = mEglSlots[slot].mEglImage; - - // release old buffer - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (pendingRelease == nullptr) { - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, - mEglSlots[st.mCurrentTexture].mEglFence); - if (status < NO_ERROR) { - EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), - status); - err = status; - // keep going, with error raised [?] - } - } else { - pendingRelease->currentTexture = st.mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); - pendingRelease->display = mEglDisplay; - pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; - pendingRelease->isPending = true; - } - } - - // Update the EGLConsumer state. - st.mCurrentTexture = slot; - mCurrentTextureImage = nextTextureImage; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - - st.computeCurrentTransformMatrixLocked(); - - return err; -} - -status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { - if (mEglDisplay == EGL_NO_DISPLAY) { - ALOGE("bindTextureImage: invalid display"); - return INVALID_OPERATION; - } - - GLenum error; - while ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); - } - - glBindTexture(st.mTexTarget, st.mTexName); - if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { - EGC_LOGE("bindTextureImage: no currently-bound texture"); - return NO_INIT; - } - - status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); - if (err != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - - // In the rare case that the display is terminated and then initialized - // again, we can't detect that the display changed (it didn't), but the - // image is invalid. In this case, repeat the exact same steps while - // forcing the creation of a new image. - if ((error = glGetError()) != GL_NO_ERROR) { - glBindTexture(st.mTexTarget, st.mTexName); - status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); - if (result != NO_ERROR) { - EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, - st.mCurrentTexture); - return UNKNOWN_ERROR; - } - mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); - if ((error = glGetError()) != GL_NO_ERROR) { - EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); - return UNKNOWN_ERROR; - } - } - - // Wait for the new buffer to be ready. - return doGLFenceWaitLocked(st); -} - -status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (!contextCheck) { - // if this is the first time we're called, mEglDisplay/mEglContext have - // never been set, so don't error out (below). - if (mEglDisplay == EGL_NO_DISPLAY) { - mEglDisplay = dpy; - } - if (mEglContext == EGL_NO_CONTEXT) { - mEglContext = ctx; - } - } - - if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { - EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); - return INVALID_OPERATION; - } - - mEglDisplay = dpy; - mEglContext = ctx; - return NO_ERROR; -} - -status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { - EGC_LOGE("detachFromContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { - EGC_LOGE("detachFromContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { - status_t err = syncForReleaseLocked(dpy, st); - if (err != OK) { - return err; - } - - glDeleteTextures(1, &st.mTexName); - } - - mEglDisplay = EGL_NO_DISPLAY; - mEglContext = EGL_NO_CONTEXT; - - return OK; -} - -status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { - // Initialize mCurrentTextureImage if there is a current buffer from past attached state. - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - if (!mEglSlots[slot].mEglImage.get()) { - mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); - } - mCurrentTextureImage = mEglSlots[slot].mEglImage; - } - - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (dpy == EGL_NO_DISPLAY) { - EGC_LOGE("attachToContext: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (ctx == EGL_NO_CONTEXT) { - EGC_LOGE("attachToContext: invalid current EGLContext"); - return INVALID_OPERATION; - } - - // We need to bind the texture regardless of whether there's a current - // buffer. - glBindTexture(st.mTexTarget, GLuint(tex)); - - mEglDisplay = dpy; - mEglContext = ctx; - st.mTexName = tex; - st.mOpMode = SurfaceTexture::OpMode::attachedToGL; - - if (mCurrentTextureImage != nullptr) { - // This may wait for a buffer a second time. This is likely required if - // this is a different context, since otherwise the wait could be skipped - // by bouncing through another context. For the same context the extra - // wait is redundant. - status_t err = bindTextureImageLocked(st); - if (err != NO_ERROR) { - return err; - } - } - - return OK; -} - -status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { - EGC_LOGV("syncForReleaseLocked"); - - if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (SyncFeatures::getInstance().useNativeFenceSync()) { - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); - eglDestroySyncKHR(dpy, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - EGC_LOGE( - "syncForReleaseLocked: error dup'ing native fence " - "fd: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } - sp fence(new Fence(fenceFd)); - status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, - mCurrentTextureImage->graphicBuffer(), fence); - if (err != OK) { - EGC_LOGE( - "syncForReleaseLocked: error adding release fence: " - "%s (%d)", - strerror(-err), err); - return err; - } - } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { - EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; - if (fence != EGL_NO_SYNC_KHR) { - // There is already a fence for the current slot. We need to - // wait on that before replacing it with another fence to - // ensure that all outstanding buffer accesses have completed - // before the producer accesses it. - EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); - if (result == EGL_FALSE) { - EGC_LOGE( - "syncForReleaseLocked: error waiting for previous " - "fence: %#x", - eglGetError()); - return UNKNOWN_ERROR; - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - EGC_LOGE( - "syncForReleaseLocked: timeout waiting for previous " - "fence"); - return TIMED_OUT; - } - eglDestroySyncKHR(dpy, fence); - } - - // Create a fence for the outstanding accesses in the current - // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); - if (fence == EGL_NO_SYNC_KHR) { - EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - glFlush(); - mEglSlots[st.mCurrentTexture].mEglFence = fence; - } - } - - return OK; -} - -status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { - EGLDisplay dpy = eglGetCurrentDisplay(); - EGLContext ctx = eglGetCurrentContext(); - - if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { - EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); - return INVALID_OPERATION; - } - - if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { - EGC_LOGE("doGLFenceWait: invalid current EGLContext"); - return INVALID_OPERATION; - } - - if (st.mCurrentFence->isValid()) { - if (SyncFeatures::getInstance().useWaitSync() && - SyncFeatures::getInstance().useNativeFenceSync()) { - // Create an EGLSyncKHR from the current fence. - int fenceFd = st.mCurrentFence->dup(); - if (fenceFd == -1) { - EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - close(fenceFd); - EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); - return UNKNOWN_ERROR; - } - - // XXX: The spec draft is inconsistent as to whether this should - // return an EGLint or void. Ignore the return value for now, as - // it's not strictly needed. - eglWaitSyncKHR(dpy, sync, 0); - EGLint eglErr = eglGetError(); - eglDestroySyncKHR(dpy, sync); - if (eglErr != EGL_SUCCESS) { - EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); - return UNKNOWN_ERROR; - } - } else { - status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); - if (err != NO_ERROR) { - EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); - return err; - } - } - } - - return NO_ERROR; -} - -void EGLConsumer::onFreeBufferLocked(int slotIndex) { - mEglSlots[slotIndex].mEglImage.clear(); -} - -void EGLConsumer::onAbandonLocked() { - mCurrentTextureImage.clear(); -} - -EGLConsumer::EglImage::EglImage(sp graphicBuffer) - : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} - -EGLConsumer::EglImage::~EglImage() { - if (mEglImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("~EglImage: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - } -} - -status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { - // If there's an image and it's no longer valid, destroy it. - bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; - bool displayInvalid = mEglDisplay != eglDisplay; - if (haveImage && (displayInvalid || forceCreation)) { - if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { - ALOGE("createIfNeeded: eglDestroyImageKHR failed"); - } - eglTerminate(mEglDisplay); - mEglImage = EGL_NO_IMAGE_KHR; - mEglDisplay = EGL_NO_DISPLAY; - } - - // If there's no image, create one. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = eglDisplay; - mEglImage = createImage(mEglDisplay, mGraphicBuffer); - } - - // Fail if we can't create a valid image. - if (mEglImage == EGL_NO_IMAGE_KHR) { - mEglDisplay = EGL_NO_DISPLAY; - const sp& buffer = mGraphicBuffer; - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); - return UNKNOWN_ERROR; - } - - return OK; -} - -void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { - glEGLImageTargetTexture2DOES(texTarget, static_cast(mEglImage)); -} - -EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp& graphicBuffer) { - EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); - const bool createProtectedImage = - (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); - EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, - EGL_TRUE, - createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, - createProtectedImage ? EGL_TRUE : EGL_NONE, - EGL_NONE, - }; - eglInitialize(dpy, nullptr, nullptr); - EGLImageKHR image = - eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); - if (image == EGL_NO_IMAGE_KHR) { - EGLint error = eglGetError(); - ALOGE("error creating EGLImage: %#x", error); - eglTerminate(dpy); - } - return image; -} - -}; // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h deleted file mode 100644 index eccb08298f6f..000000000000 --- a/libs/hwui/surfacetexture/EGLConsumer.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include - -#include -#include -#include - -namespace android { - -class SurfaceTexture; - -/* - * EGLConsumer implements the parts of SurfaceTexture that deal with - * textures attached to an GL context. - */ -class EGLConsumer { -public: - EGLConsumer(); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(SurfaceTexture& st); - - /* - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(SurfaceTexture& st); - - /** - * detachFromContext detaches the EGLConsumer from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a EGLConsumer from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a EGLConsumer has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the EGLConsumer is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(SurfaceTexture& st); - - /** - * attachToContext attaches a EGLConsumer that is currently in the - * 'detached' state to the current OpenGL ES context. A EGLConsumer is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * EGLConsumer that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex, SurfaceTexture& st); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - - /** - * onAbandonLocked amends the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - void onAbandonLocked(); - -protected: - struct PendingRelease { - PendingRelease() - : isPending(false) - , currentTexture(-1) - , graphicBuffer() - , display(nullptr) - , fence(nullptr) {} - - bool isPending; - int currentTexture; - sp graphicBuffer; - EGLDisplay display; - EGLSyncKHR fence; - }; - - /** - * This releases the buffer in the slot referenced by mCurrentTexture, - * then updates state to refer to the BufferItem, which must be a - * newly-acquired buffer. If pendingRelease is not null, the parameters - * which would have been passed to releaseBufferLocked upon the successful - * completion of the method will instead be returned to the caller, so that - * it may call releaseBufferLocked itself later. - */ - status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, - SurfaceTexture& st); - - /** - * Binds mTexName and the current buffer to mTexTarget. Uses - * mCurrentTexture if it's set, mCurrentTextureImage if not. If the - * bind succeeds, this calls doGLFenceWait. - */ - status_t bindTextureImageLocked(SurfaceTexture& st); - - /** - * Gets the current EGLDisplay and EGLContext values, and compares them - * to mEglDisplay and mEglContext. If the fields have been previously - * set, the values must match; if not, the fields are set to the current - * values. - * The contextCheck argument is used to ensure that a GL context is - * properly set; when set to false, the check is not performed. - */ - status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); - - /** - * EglImage is a utility class for tracking and creating EGLImageKHRs. There - * is primarily just one image per slot, but there is also special cases: - * - For releaseTexImage, we use a debug image (mReleasedTexImage) - * - After freeBuffer, we must still keep the current image/buffer - * Reference counting EGLImages lets us handle all these cases easily while - * also only creating new EGLImages from buffers when required. - */ - class EglImage : public LightRefBase { - public: - EglImage(sp graphicBuffer); - - /** - * createIfNeeded creates an EGLImage if required (we haven't created - * one yet, or the EGLDisplay or crop-rect has changed). - */ - status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); - - /** - * This calls glEGLImageTargetTexture2DOES to bind the image to the - * texture in the specified texture target. - */ - void bindToTextureTarget(uint32_t texTarget); - - const sp& graphicBuffer() { return mGraphicBuffer; } - const native_handle* graphicBufferHandle() { - return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; - } - - private: - // Only allow instantiation using ref counting. - friend class LightRefBase; - virtual ~EglImage(); - - // createImage creates a new EGLImage from a GraphicBuffer. - EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer); - - // Disallow copying - EglImage(const EglImage& rhs); - void operator=(const EglImage& rhs); - - // mGraphicBuffer is the buffer that was used to create this image. - sp mGraphicBuffer; - - // mEglImage is the EGLImage created from mGraphicBuffer. - EGLImageKHR mEglImage; - - // mEGLDisplay is the EGLDisplay that was used to create mEglImage. - EGLDisplay mEglDisplay; - - // mCropRect is the crop rectangle passed to EGL when mEglImage - // was created. - Rect mCropRect; - }; - - /** - * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command - * stream to ensure that it is safe for future OpenGL ES commands to - * access the current texture buffer. - */ - status_t doGLFenceWaitLocked(SurfaceTexture& st) const; - - /** - * syncForReleaseLocked performs the synchronization needed to release the - * current slot from an OpenGL ES context. If needed it will set the - * current slot's fence to guard against a producer accessing the buffer - * before the outstanding accesses have completed. - */ - status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); - - /** - * returns a graphic buffer used when the texture image has been released - */ - static sp getDebugTexImageBuffer(); - - /** - * The default consumer usage flags that EGLConsumer always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the EGLConsumer user. In particular, EGLConsumer will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentTextureImage is the EglImage/buffer of the current texture. It's - * possible that this buffer is not associated with any buffer slot, so we - * must track it separately in order to support the getCurrentBuffer method. - */ - sp mCurrentTextureImage; - - /** - * EGLSlot contains the information and object references that - * EGLConsumer maintains about a BufferQueue buffer slot. - */ - struct EglSlot { - EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - - /** - * mEglImage is the EGLImage created from mGraphicBuffer. - */ - sp mEglImage; - - /** - * mFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. It is initialized - * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based - * on a compile-time option) set to a new sync object in updateTexImage. - */ - EGLSyncKHR mEglFence; - }; - - /** - * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently - * associated. It is intialized to EGL_NO_DISPLAY and gets set to the - * current display when updateTexImage is called for the first time and when - * attachToContext is called. - */ - EGLDisplay mEglDisplay; - - /** - * mEglContext is the OpenGL ES context with which this EGLConsumer is - * currently associated. It is initialized to EGL_NO_CONTEXT and gets set - * to the current GL context when updateTexImage is called for the first - * time and when attachToContext is called. - */ - EGLContext mEglContext; - - /** - * mEGLSlots stores the buffers that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; - - /** - * protects static initialization - */ - static Mutex sStaticInitLock; - - /** - * mReleasedTexImageBuffer is a dummy buffer used when in single buffer - * mode and releaseTexImage() has been called - */ - static sp sReleasedTexImageBuffer; - sp mReleasedTexImage; -}; - -}; // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp deleted file mode 100644 index c86cd962ebed..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2018 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 "ImageConsumer.h" -#include -#include "Properties.h" -#include "SurfaceTexture.h" -#include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" -#include "renderthread/VulkanManager.h" - -// Macro for including the SurfaceTexture name in log messages -#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) - -namespace android { - -void ImageConsumer::onFreeBufferLocked(int slotIndex) { - mImageSlots[slotIndex].mImage.reset(); -} - -void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { - // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. - if (item->mGraphicBuffer != nullptr) { - mImageSlots[item->mSlot].mImage.reset(); - } -} - -void ImageConsumer::onReleaseBufferLocked(int buf) { - mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR; -} - -void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer) { - if (!mImage.get()) { - mImage = graphicBuffer.get() - ? SkImage::MakeFromAHardwareBuffer( - reinterpret_cast(graphicBuffer.get()), - kPremul_SkAlphaType, SkColorSpace::MakeSRGB()) - : nullptr; - } -} - -sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, - uirenderer::RenderState& renderState) { - BufferItem item; - status_t err; - err = st.acquireBufferLocked(&item, 0); - if (err != OK) { - if (err != BufferQueue::NO_BUFFER_AVAILABLE) { - IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); - } else { - int slot = st.mCurrentTexture; - if (slot != BufferItem::INVALID_BUFFER_SLOT) { - *queueEmpty = true; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); - return mImageSlots[slot].mImage; - } - } - return nullptr; - } - - int slot = item.mSlot; - if (item.mFence->isValid()) { - // Wait on the producer fence for the buffer to be ready. - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); - } else { - err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence); - } - if (err != OK) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Release old buffer. - if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { - // If needed, set the released slot's fence to guard against a producer accessing the - // buffer before the outstanding accesses have completed. - sp releaseFence; - EGLDisplay display = EGL_NO_DISPLAY; - if (uirenderer::Properties::getRenderPipelineType() == - uirenderer::RenderPipelineType::SkiaGL) { - auto& eglManager = renderState.getRenderThread().eglManager(); - display = eglManager.eglDisplay(); - err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence, - releaseFence); - } else { - err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence); - } - if (OK != err) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - - if (releaseFence.get()) { - status_t err = st.addReleaseFenceLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); - if (err != OK) { - IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); - return nullptr; - } - } - - // Finally release the old buffer. - status_t status = st.releaseBufferLocked( - st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, - mImageSlots[st.mCurrentTexture].mEglFence); - if (status < NO_ERROR) { - IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); - err = status; - // Keep going, with error raised. - } - } - - // Update the state. - st.mCurrentTexture = slot; - st.mCurrentCrop = item.mCrop; - st.mCurrentTransform = item.mTransform; - st.mCurrentScalingMode = item.mScalingMode; - st.mCurrentTimestamp = item.mTimestamp; - st.mCurrentDataSpace = item.mDataSpace; - st.mCurrentFence = item.mFence; - st.mCurrentFenceTime = item.mFenceTime; - st.mCurrentFrameNumber = item.mFrameNumber; - st.computeCurrentTransformMatrixLocked(); - - *queueEmpty = false; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); - return mImageSlots[slot].mImage; -} - -} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h deleted file mode 100644 index 31ee8db52874..000000000000 --- a/libs/hwui/surfacetexture/ImageConsumer.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include - -#include -#include -#include -#include - -namespace android { - -namespace uirenderer { -class RenderState; -} - -class SurfaceTexture; - -/* - * ImageConsumer implements the parts of SurfaceTexture that deal with - * images consumed by HWUI view system. - */ -class ImageConsumer { -public: - sk_sp dequeueImage(bool* queueEmpty, SurfaceTexture& cb, - uirenderer::RenderState& renderState); - - /** - * onAcquireBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase behavior. - */ - void onAcquireBufferLocked(BufferItem* item); - - /** - * onReleaseBufferLocked amends the ConsumerBase method to update the - * mImageSlots array in addition to the ConsumerBase. - */ - void onReleaseBufferLocked(int slot); - - /** - * onFreeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the SkImage in that slot. Otherwise it has no effect. - */ - void onFreeBufferLocked(int slotIndex); - -private: - /** - * ImageSlot contains the information and object references that - * ImageConsumer maintains about a BufferQueue buffer slot. - */ - struct ImageSlot { - ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {} - - // mImage is the SkImage created from mGraphicBuffer. - sk_sp mImage; - - /** - * mEglFence is the EGL sync object that must signal before the buffer - * associated with this buffer slot may be dequeued. - */ - EGLSyncKHR mEglFence; - - void createIfNeeded(sp graphicBuffer); - }; - - /** - * ImageConsumer stores the SkImages that have been allocated by the BufferQueue - * for each buffer slot. It is initialized to null pointers, and gets - * filled in with the result of BufferQueue::acquire when the - * client dequeues a buffer from a - * slot that has not yet been used. The buffer allocated to a slot will also - * be replaced if the requested buffer usage or geometry differs from that - * of the buffer allocated to a slot. - */ - ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; -}; - -}; /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp deleted file mode 100644 index 4bff715822e8..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.cpp +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2018 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 -#include -#include -#include - -#include - -#include "Matrix.h" -#include "SurfaceTexture.h" - -namespace android { - -// Macros for including the SurfaceTexture name in log messages -#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) - -static const mat4 mtxIdentity; - -SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t tex, - uint32_t texTarget, bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(tex) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::attachedToGL) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t texTarget, - bool useFenceSync, bool isControlledByApp) - : ConsumerBase(bq, isControlledByApp) - , mCurrentCrop(Rect::EMPTY_RECT) - , mCurrentTransform(0) - , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) - , mCurrentFence(Fence::NO_FENCE) - , mCurrentTimestamp(0) - , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) - , mCurrentFrameNumber(0) - , mDefaultWidth(1) - , mDefaultHeight(1) - , mFilteringEnabled(true) - , mTexName(0) - , mUseFenceSync(useFenceSync) - , mTexTarget(texTarget) - , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) - , mOpMode(OpMode::detached) { - SFT_LOGV("SurfaceTexture"); - - memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); - - mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); -} - -status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); - return NO_INIT; - } - mDefaultWidth = w; - mDefaultHeight = h; - return mConsumer->setDefaultBufferSize(w, h); -} - -status_t SurfaceTexture::updateTexImage() { - ATRACE_CALL(); - SFT_LOGV("updateTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.updateTexImage(*this); -} - -status_t SurfaceTexture::releaseTexImage() { - // releaseTexImage can be invoked even when not attached to a GL context. - ATRACE_CALL(); - SFT_LOGV("releaseTexImage"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); - return NO_INIT; - } - - return mEGLConsumer.releaseTexImage(*this); -} - -status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber) { - status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); - if (err != NO_ERROR) { - return err; - } - - switch (mOpMode) { - case OpMode::attachedToView: - mImageConsumer.onAcquireBufferLocked(item); - break; - case OpMode::attachedToGL: - mEGLConsumer.onAcquireBufferLocked(item, *this); - break; - case OpMode::detached: - break; - } - - return NO_ERROR; -} - -status_t SurfaceTexture::releaseBufferLocked(int buf, sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) { - // release the buffer if it hasn't already been discarded by the - // BufferQueue. This can happen, for example, when the producer of this - // buffer has reallocated the original buffer slot after this buffer - // was acquired. - status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); - // We could be releasing an EGL buffer, even if not currently attached to a GL context. - mImageConsumer.onReleaseBufferLocked(buf); - mEGLConsumer.onReleaseBufferLocked(buf); - return err; -} - -status_t SurfaceTexture::detachFromContext() { - ATRACE_CALL(); - SFT_LOGV("detachFromContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::attachedToGL) { - SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); - return INVALID_OPERATION; - } - - status_t err = mEGLConsumer.detachFromContext(*this); - if (err == OK) { - mOpMode = OpMode::detached; - } - - return err; -} - -status_t SurfaceTexture::attachToContext(uint32_t tex) { - ATRACE_CALL(); - SFT_LOGV("attachToContext"); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - SFT_LOGE("attachToContext: abandoned SurfaceTexture"); - return NO_INIT; - } - - if (mOpMode != OpMode::detached) { - SFT_LOGE( - "attachToContext: SurfaceTexture is already attached to a " - "context"); - return INVALID_OPERATION; - } - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible ImageConsumer cache - mImageConsumer.onFreeBufferLocked(mCurrentTexture); - } - - return mEGLConsumer.attachToContext(tex, *this); -} - -void SurfaceTexture::attachToView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - if (mAbandoned) { - SFT_LOGE("attachToView: abandoned SurfaceTexture"); - return; - } - if (mOpMode == OpMode::detached) { - mOpMode = OpMode::attachedToView; - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - // release possible EGLConsumer texture cache - mEGLConsumer.onFreeBufferLocked(mCurrentTexture); - mEGLConsumer.onAbandonLocked(); - } - } else { - SFT_LOGE("attachToView: already attached"); - } -} - -void SurfaceTexture::detachFromView() { - ATRACE_CALL(); - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("detachFromView: abandoned SurfaceTexture"); - return; - } - - if (mOpMode == OpMode::attachedToView) { - mOpMode = OpMode::detached; - } else { - SFT_LOGE("detachFromView: not attached to View"); - } -} - -uint32_t SurfaceTexture::getCurrentTextureTarget() const { - return mTexTarget; -} - -void SurfaceTexture::getTransformMatrix(float mtx[16]) { - Mutex::Autolock lock(mMutex); - memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); -} - -void SurfaceTexture::setFilteringEnabled(bool enabled) { - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); - return; - } - bool needsRecompute = mFilteringEnabled != enabled; - mFilteringEnabled = enabled; - - if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { - SFT_LOGD("setFilteringEnabled called with no current item"); - } - - if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - computeCurrentTransformMatrixLocked(); - } -} - -void SurfaceTexture::computeCurrentTransformMatrixLocked() { - SFT_LOGV("computeCurrentTransformMatrixLocked"); - sp buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) - ? nullptr - : mSlots[mCurrentTexture].mGraphicBuffer; - if (buf == nullptr) { - SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); - } - computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, - mFilteringEnabled); -} - -void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp& buf, - const Rect& cropRect, uint32_t transform, - bool filtering) { - // Transform matrices - static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); - static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); - - mat4 xform; - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { - xform *= mtxFlipH; - } - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { - xform *= mtxFlipV; - } - if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - xform *= mtxRot90; - } - - if (!cropRect.isEmpty() && buf.get()) { - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - float shrinkAmount = 0.0f; - if (filtering) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGBA_FP16: - case PIXEL_FORMAT_RGBA_1010102: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - // We know there's no subsampling of any channels, so we - // only need to shrink by a half a pixel. - shrinkAmount = 0.5; - break; - - default: - // If we don't recognize the format, we must assume the - // worst case (that we care about), which is YUV420. - shrinkAmount = 1.0; - break; - } - } - - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; - } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; - } - - mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); - xform = crop * xform; - } - - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so SurfaceTexture must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. - xform = mtxFlipV * xform; - - memcpy(outTransform, xform.asArray(), sizeof(xform)); -} - -Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { - Rect outCrop = crop; - - uint32_t newWidth = static_cast(crop.width()); - uint32_t newHeight = static_cast(crop.height()); - - if (newWidth * bufferHeight > newHeight * bufferWidth) { - newWidth = newHeight * bufferWidth / bufferHeight; - ALOGV("too wide: newWidth = %d", newWidth); - } else if (newWidth * bufferHeight < newHeight * bufferWidth) { - newHeight = newWidth * bufferHeight / bufferWidth; - ALOGV("too tall: newHeight = %d", newHeight); - } - - uint32_t currentWidth = static_cast(crop.width()); - uint32_t currentHeight = static_cast(crop.height()); - - // The crop is too wide - if (newWidth < currentWidth) { - uint32_t dw = currentWidth - newWidth; - auto halfdw = dw / 2; - outCrop.left += halfdw; - // Not halfdw because it would subtract 1 too few when dw is odd - outCrop.right -= (dw - halfdw); - // The crop is too tall - } else if (newHeight < currentHeight) { - uint32_t dh = currentHeight - newHeight; - auto halfdh = dh / 2; - outCrop.top += halfdh; - // Not halfdh because it would subtract 1 too few when dh is odd - outCrop.bottom -= (dh - halfdh); - } - - ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, - outCrop.bottom); - - return outCrop; -} - -nsecs_t SurfaceTexture::getTimestamp() { - SFT_LOGV("getTimestamp"); - Mutex::Autolock lock(mMutex); - return mCurrentTimestamp; -} - -android_dataspace SurfaceTexture::getCurrentDataSpace() { - SFT_LOGV("getCurrentDataSpace"); - Mutex::Autolock lock(mMutex); - return mCurrentDataSpace; -} - -uint64_t SurfaceTexture::getFrameNumber() { - SFT_LOGV("getFrameNumber"); - Mutex::Autolock lock(mMutex); - return mCurrentFrameNumber; -} - -Rect SurfaceTexture::getCurrentCrop() const { - Mutex::Autolock lock(mMutex); - return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) - ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) - : mCurrentCrop; -} - -uint32_t SurfaceTexture::getCurrentTransform() const { - Mutex::Autolock lock(mMutex); - return mCurrentTransform; -} - -uint32_t SurfaceTexture::getCurrentScalingMode() const { - Mutex::Autolock lock(mMutex); - return mCurrentScalingMode; -} - -sp SurfaceTexture::getCurrentFence() const { - Mutex::Autolock lock(mMutex); - return mCurrentFence; -} - -std::shared_ptr SurfaceTexture::getCurrentFenceTime() const { - Mutex::Autolock lock(mMutex); - return mCurrentFenceTime; -} - -void SurfaceTexture::freeBufferLocked(int slotIndex) { - SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - if (slotIndex == mCurrentTexture) { - mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - } - // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. - // Buffers can be freed after SurfaceTexture has detached from GL context or View. - mImageConsumer.onFreeBufferLocked(slotIndex); - mEGLConsumer.onFreeBufferLocked(slotIndex); - ConsumerBase::freeBufferLocked(slotIndex); -} - -void SurfaceTexture::abandonLocked() { - SFT_LOGV("abandonLocked"); - mEGLConsumer.onAbandonLocked(); - ConsumerBase::abandonLocked(); -} - -status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { - return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); -} - -void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { - result.appendFormat( - "%smTexName=%d mCurrentTexture=%d\n" - "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", - prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, - mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); - - ConsumerBase::dumpLocked(result, prefix); -} - -sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, - bool* queueEmpty, - uirenderer::RenderState& renderState) { - Mutex::Autolock _l(mMutex); - - if (mAbandoned) { - SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); - return nullptr; - } - - if (mOpMode != OpMode::attachedToView) { - SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); - return nullptr; - } - - auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); - if (image.get()) { - uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); - dataSpace = mCurrentDataSpace; - } - return image; -} - -}; // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h deleted file mode 100644 index db392a9f8476..000000000000 --- a/libs/hwui/surfacetexture/SurfaceTexture.h +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -#pragma once - -#include -#include - -#include -#include - -#include -#include - -#include "EGLConsumer.h" -#include "ImageConsumer.h" - -namespace android { - -namespace uirenderer { -class RenderState; -} - -/* - * SurfaceTexture consumes buffers of graphics data from a BufferQueue, - * and makes them available to HWUI render thread as a SkImage and to - * an application GL render thread as an OpenGL texture. - * - * When attached to an application GL render thread, a typical usage - * pattern is to set up the SurfaceTexture with the - * desired options, and call updateTexImage() when a new frame is desired. - * If a new frame is available, the texture will be updated. If not, - * the previous contents are retained. - * - * When attached to a HWUI render thread, the TextureView implementation - * calls dequeueImage, which either pulls a new SkImage or returns the - * last cached SkImage if BufferQueue is empty. - * When attached to HWUI render thread, SurfaceTexture is compatible to - * both Vulkan and GL drawing pipelines. - */ -class ANDROID_API SurfaceTexture : public ConsumerBase { -public: - enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES - typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; - - /** - * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with - * the tex parameter is used, tex indicates the name of the OpenGL ES - * texture to which images are to be streamed. texTarget specifies the - * OpenGL ES texture target to which the texture will be bound in - * updateTexImage. useFenceSync specifies whether fences should be used to - * synchronize access to buffers if that behavior is enabled at - * compile-time. - * - * A SurfaceTexture may be detached from one OpenGL ES context and then - * attached to a different context using the detachFromContext and - * attachToContext methods, respectively. The intention of these methods is - * purely to allow a SurfaceTexture to be transferred from one consumer - * context to another. If such a transfer is not needed there is no - * requirement that either of these methods be called. - * - * If the constructor with the tex parameter is used, the SurfaceTexture is - * created in a state where it is considered attached to an OpenGL ES - * context for the purposes of the attachToContext and detachFromContext - * methods. However, despite being considered "attached" to a context, the - * specific OpenGL ES context doesn't get latched until the first call to - * updateTexImage. After that point, all calls to updateTexImage must be - * made with the same OpenGL ES context current. - * - * If the constructor without the tex parameter is used, the SurfaceTexture is - * created in a detached state, and attachToContext must be called before - * calls to updateTexImage. - */ - SurfaceTexture(const sp& bq, uint32_t tex, uint32_t texureTarget, - bool useFenceSync, bool isControlledByApp); - - SurfaceTexture(const sp& bq, uint32_t texureTarget, bool useFenceSync, - bool isControlledByApp); - - /** - * updateTexImage acquires the most recently queued buffer, and sets the - * image contents of the target texture to it. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - * - * This calls doGLFenceWait to ensure proper synchronization. - */ - status_t updateTexImage(); - - /** - * releaseTexImage releases the texture acquired in updateTexImage(). - * This is intended to be used in single buffer mode. - * - * This call may only be made while the OpenGL ES context to which the - * target texture belongs is bound to the calling thread. - */ - status_t releaseTexImage(); - - /** - * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix - * associated with the texture image set by the most recent call to - * updateTexImage. - * - * This transform matrix maps 2D homogeneous texture coordinates of the form - * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture - * coordinate that should be used to sample that location from the texture. - * Sampling the texture outside of the range of this transform is undefined. - * - * This transform is necessary to compensate for transforms that the stream - * content producer may implicitly apply to the content. By forcing users of - * a SurfaceTexture to apply this transform we avoid performing an extra - * copy of the data that would be needed to hide the transform from the - * user. - * - * The matrix is stored in column-major order so that it may be passed - * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv - * functions. - */ - void getTransformMatrix(float mtx[16]); - - /** - * Computes the transform matrix documented by getTransformMatrix - * from the BufferItem sub parts. - */ - static void computeTransformMatrix(float outTransform[16], const sp& buf, - const Rect& cropRect, uint32_t transform, bool filtering); - - /** - * Scale the crop down horizontally or vertically such that it has the - * same aspect ratio as the buffer does. - */ - static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); - - /** - * getTimestamp retrieves the timestamp associated with the texture image - * set by the most recent call to updateTexImage. - * - * The timestamp is in nanoseconds, and is monotonically increasing. Its - * other semantics (zero point, etc) are source-dependent and should be - * documented by the source. - */ - int64_t getTimestamp(); - - /** - * getDataSpace retrieves the DataSpace associated with the texture image - * set by the most recent call to updateTexImage. - */ - android_dataspace getCurrentDataSpace(); - - /** - * getFrameNumber retrieves the frame number associated with the texture - * image set by the most recent call to updateTexImage. - * - * The frame number is an incrementing counter set to 0 at the creation of - * the BufferQueue associated with this consumer. - */ - uint64_t getFrameNumber(); - - /** - * setDefaultBufferSize is used to set the size of buffers returned by - * requestBuffers when a with and height of zero is requested. - * A call to setDefaultBufferSize() may trigger requestBuffers() to - * be called from the client. - * The width and height parameters must be no greater than the minimum of - * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - * An error due to invalid dimensions might not be reported until - * updateTexImage() is called. - */ - status_t setDefaultBufferSize(uint32_t width, uint32_t height); - - /** - * setFilteringEnabled sets whether the transform matrix should be computed - * for use with bilinear filtering. - */ - void setFilteringEnabled(bool enabled); - - /** - * getCurrentTextureTarget returns the texture target of the current - * texture as returned by updateTexImage(). - */ - uint32_t getCurrentTextureTarget() const; - - /** - * getCurrentCrop returns the cropping rectangle of the current buffer. - */ - Rect getCurrentCrop() const; - - /** - * getCurrentTransform returns the transform of the current buffer. - */ - uint32_t getCurrentTransform() const; - - /** - * getCurrentScalingMode returns the scaling mode of the current buffer. - */ - uint32_t getCurrentScalingMode() const; - - /** - * getCurrentFence returns the fence indicating when the current buffer is - * ready to be read from. - */ - sp getCurrentFence() const; - - /** - * getCurrentFence returns the FenceTime indicating when the current - * buffer is ready to be read from. - */ - std::shared_ptr getCurrentFenceTime() const; - - /** - * setConsumerUsageBits overrides the ConsumerBase method to OR - * DEFAULT_USAGE_FLAGS to usage. - */ - status_t setConsumerUsageBits(uint64_t usage); - - /** - * detachFromContext detaches the SurfaceTexture from the calling thread's - * current OpenGL ES context. This context must be the same as the context - * that was current for previous calls to updateTexImage. - * - * Detaching a SurfaceTexture from an OpenGL ES context will result in the - * deletion of the OpenGL ES texture object into which the images were being - * streamed. After a SurfaceTexture has been detached from the OpenGL ES - * context calls to updateTexImage will fail returning INVALID_OPERATION - * until the SurfaceTexture is attached to a new OpenGL ES context using the - * attachToContext method. - */ - status_t detachFromContext(); - - /** - * attachToContext attaches a SurfaceTexture that is currently in the - * 'detached' state to the current OpenGL ES context. A SurfaceTexture is - * in the 'detached' state iff detachFromContext has successfully been - * called and no calls to attachToContext have succeeded since the last - * detachFromContext call. Calls to attachToContext made on a - * SurfaceTexture that is not in the 'detached' state will result in an - * INVALID_OPERATION error. - * - * The tex argument specifies the OpenGL ES texture object name in the - * new context into which the image contents will be streamed. A successful - * call to attachToContext will result in this texture object being bound to - * the texture target and populated with the image contents that were - * current at the time of the last call to detachFromContext. - */ - status_t attachToContext(uint32_t tex); - - sk_sp dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, - bool* queueEmpty, uirenderer::RenderState& renderState); - - /** - * attachToView attaches a SurfaceTexture that is currently in the - * 'detached' state to HWUI View system. - */ - void attachToView(); - - /** - * detachFromView detaches a SurfaceTexture from HWUI View system. - */ - void detachFromView(); - -protected: - /** - * abandonLocked overrides the ConsumerBase method to clear - * mCurrentTextureImage in addition to the ConsumerBase behavior. - */ - virtual void abandonLocked(); - - /** - * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- - * specific info in addition to the ConsumerBase behavior. - */ - virtual void dumpLocked(String8& result, const char* prefix) const override; - - /** - * acquireBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase behavior. - */ - virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, - uint64_t maxFrameNumber = 0) override; - - /** - * releaseBufferLocked overrides the ConsumerBase method to update the - * mEglSlots array in addition to the ConsumerBase. - */ - virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; - - /** - * freeBufferLocked frees up the given buffer slot. If the slot has been - * initialized this will release the reference to the GraphicBuffer in that - * slot and destroy the EGLImage in that slot. Otherwise it has no effect. - * - * This method must be called with mMutex locked. - */ - virtual void freeBufferLocked(int slotIndex); - - /** - * computeCurrentTransformMatrixLocked computes the transform matrix for the - * current texture. It uses mCurrentTransform and the current GraphicBuffer - * to compute this matrix and stores it in mCurrentTransformMatrix. - * mCurrentTextureImage must not be NULL. - */ - void computeCurrentTransformMatrixLocked(); - - /** - * The default consumer usage flags that SurfaceTexture always sets on its - * BufferQueue instance; these will be OR:d with any additional flags passed - * from the SurfaceTexture user. In particular, SurfaceTexture will always - * consume buffers as hardware textures. - */ - static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - - /** - * mCurrentCrop is the crop rectangle that applies to the current texture. - * It gets set each time updateTexImage is called. - */ - Rect mCurrentCrop; - - /** - * mCurrentTransform is the transform identifier for the current texture. It - * gets set each time updateTexImage is called. - */ - uint32_t mCurrentTransform; - - /** - * mCurrentScalingMode is the scaling mode for the current texture. It gets - * set each time updateTexImage is called. - */ - uint32_t mCurrentScalingMode; - - /** - * mCurrentFence is the fence received from BufferQueue in updateTexImage. - */ - sp mCurrentFence; - - /** - * The FenceTime wrapper around mCurrentFence. - */ - std::shared_ptr mCurrentFenceTime{FenceTime::NO_FENCE}; - - /** - * mCurrentTransformMatrix is the transform matrix for the current texture. - * It gets computed by computeTransformMatrix each time updateTexImage is - * called. - */ - float mCurrentTransformMatrix[16]; - - /** - * mCurrentTimestamp is the timestamp for the current texture. It - * gets set each time updateTexImage is called. - */ - int64_t mCurrentTimestamp; - - /** - * mCurrentDataSpace is the dataspace for the current texture. It - * gets set each time updateTexImage is called. - */ - android_dataspace mCurrentDataSpace; - - /** - * mCurrentFrameNumber is the frame counter for the current texture. - * It gets set each time updateTexImage is called. - */ - uint64_t mCurrentFrameNumber; - - uint32_t mDefaultWidth, mDefaultHeight; - - /** - * mFilteringEnabled indicates whether the transform matrix is computed for - * use with bilinear filtering. It defaults to true and is changed by - * setFilteringEnabled(). - */ - bool mFilteringEnabled; - - /** - * mTexName is the name of the OpenGL texture to which streamed images will - * be bound when updateTexImage is called. It is set at construction time - * and can be changed with a call to attachToContext. - */ - uint32_t mTexName; - - /** - * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync - * extension should be used to prevent buffers from being dequeued before - * it's safe for them to be written. It gets set at construction time and - * never changes. - */ - const bool mUseFenceSync; - - /** - * mTexTarget is the GL texture target with which the GL texture object is - * associated. It is set in the constructor and never changed. It is - * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android - * Browser. In that case it is set to GL_TEXTURE_2D to allow - * glCopyTexSubImage to read from the texture. This is a hack to work - * around a GL driver limitation on the number of FBO attachments, which the - * browser's tile cache exceeds. - */ - const uint32_t mTexTarget; - - /** - * mCurrentTexture is the buffer slot index of the buffer that is currently - * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, - * indicating that no buffer slot is currently bound to the texture. Note, - * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - * that no buffer is bound to the texture. A call to setBufferCount will - * reset mCurrentTexture to INVALID_BUFFER_SLOT. - */ - int mCurrentTexture; - - enum class OpMode { detached, attachedToView, attachedToGL }; - /** - * mOpMode indicates whether the SurfaceTexture is currently attached to - * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, - * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to - * whatever GL context is current at the time of the first updateTexImage call. - * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by - * attachToContext. - * attachToView/detachFromView are used to attach/detach from HWUI view system. - */ - OpMode mOpMode; - - /** - * mEGLConsumer has SurfaceTexture logic used when attached to GL context. - */ - EGLConsumer mEGLConsumer; - - /** - * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. - */ - ImageConsumer mImageConsumer; - - friend class ImageConsumer; - friend class EGLConsumer; -}; - -// ---------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp index d2d37dcb34f2..5b361548eeda 100644 --- a/libs/hwui/tests/common/LeakChecker.cpp +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -16,6 +16,7 @@ #include "LeakChecker.h" +#include "Caches.h" #include "TestUtils.h" #include @@ -70,6 +71,9 @@ void LeakChecker::checkForLeaks() { // thread-local caches so some leaks will not be properly tagged as leaks UnreachableMemoryInfo rtMemInfo; TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { + if (Caches::hasInstance()) { + Caches::getInstance().tasks.stop(); + } // Check for leaks if (!GetUnreachableMemory(rtMemInfo)) { cerr << "Failed to get unreachable memory!" << endl; diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 66b9b85bdbe7..69586345319e 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -67,14 +67,16 @@ sp TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { sp layerUpdater = createTextureLayerUpdater(renderThread); - layerUpdater->backingLayer()->getTransform() = transform; + layerUpdater->backingLayer()->getTransform().load(transform); layerUpdater->setSize(width, height); layerUpdater->setTransform(&transform); // updateLayer so it's ready to draw - SkMatrix identity; - identity.setIdentity(); - layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr); + layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN); + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + static_cast(layerUpdater->backingLayer()) + ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); + } return layerUpdater; } @@ -115,6 +117,7 @@ void TestUtils::TestTask::run() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { renderThread.vulkanManager().destroy(); } else { + renderThread.renderState().flush(Caches::FlushMode::Full); renderThread.destroyGlContext(); } } diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 0e6582c59a36..743f8093bfa8 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index 6c8775b1bdbb..f29830f0e34b 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -15,13 +15,12 @@ */ #include "DeferredLayerUpdater.h" +#include "GlLayer.h" #include "Properties.h" #include "tests/common/TestUtils.h" #include -#include -#include using namespace android; using namespace android::uirenderer; @@ -32,6 +31,10 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { layerUpdater->setBlend(true); // updates are deferred so the backing layer should still be in its default state + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); + EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget()); + } EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight()); EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter()); @@ -39,13 +42,19 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); // push the deferred updates to the layer - SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5); - SkBitmap bitmap; - bitmap.allocN32Pixels(16, 16); - sk_sp layerImage = SkImage::MakeFromBitmap(bitmap); - layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage); + Matrix4 scaledMatrix; + scaledMatrix.loadScale(0.5, 0.5, 0.0); + layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN); + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); + glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); + } // the backing layer should now have all the properties applied. + if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); + EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget()); + } EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight()); EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter()); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index aecceb3609f5..9e6d9a8c27de 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -17,13 +17,12 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "Caches.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "Properties.h" #include "tests/common/LeakChecker.h" -#include "thread/TaskProcessor.h" -#include "thread/Task.h" #include "thread/TaskManager.h" #include diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index ebf2343c5518..f8e8a0a18284 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -16,7 +16,6 @@ #ifndef PAINT_UTILS_H #define PAINT_UTILS_H -#include #include #include diff --git a/native/android/Android.bp b/native/android/Android.bp index 43847cc4ab06..4fb5e748aaac 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -64,7 +64,6 @@ cc_library_shared { "libsensor", "libandroid_runtime", "libnetd_client", - "libhwui", ], static_libs: [ diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp index ced2792775d4..b26688190ccd 100644 --- a/native/android/surface_texture.cpp +++ b/native/android/surface_texture.cpp @@ -21,16 +21,15 @@ #include +#include #include #include -#include "surfacetexture/SurfaceTexture.h" - using namespace android; struct ASurfaceTexture { - sp consumer; + sp consumer; sp producer; }; -- cgit v1.2.3-59-g8ed1b From 564ca3e2c9ed752f2a9d549ebfb31a78942ecfec Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Tue, 4 Sep 2018 22:00:00 +0000 Subject: Reland "TextureView Vulkan support and optimized OpenGL draw" Fix an issue with incorrect texture matrix, when there is 90/270 rotation. This happened, because after refactoring SkImage has buffer width/height, instead of layer width/height. This reverts commit a683eb3945320f592f58705c3c31b3c044f4bd72. Bug: 113673613 Test: Ran TextureView CTS and lensblur in camera app Change-Id: If3bcf9cd5195de09fd67a753708568a8e3ca3a9a --- core/jni/android/graphics/ColorFilter.cpp | 2 - core/jni/android/graphics/Matrix.cpp | 1 - core/jni/android/graphics/Shader.cpp | 1 - core/jni/android/graphics/SurfaceTexture.cpp | 47 +- core/jni/android_view_TextureLayer.cpp | 3 +- .../android_graphics_SurfaceTexture.h | 6 +- libs/hwui/Android.bp | 10 +- libs/hwui/Caches.cpp | 103 ---- libs/hwui/Caches.h | 135 ----- libs/hwui/DeferredLayerUpdater.cpp | 123 +--- libs/hwui/DeferredLayerUpdater.h | 34 +- libs/hwui/GlLayer.cpp | 68 --- libs/hwui/GlLayer.h | 75 --- libs/hwui/GpuMemoryTracker.cpp | 17 - libs/hwui/Layer.cpp | 8 +- libs/hwui/Layer.h | 57 +- libs/hwui/PixelBuffer.cpp | 156 ----- libs/hwui/PixelBuffer.h | 198 ------ libs/hwui/RenderProperties.h | 1 - libs/hwui/ResourceCache.cpp | 23 +- libs/hwui/Texture.cpp | 413 ------------- libs/hwui/Texture.h | 228 ------- libs/hwui/VkLayer.cpp | 40 -- libs/hwui/VkLayer.h | 70 --- libs/hwui/hwui/Bitmap.cpp | 2 +- libs/hwui/pipeline/skia/LayerDrawable.cpp | 34 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 12 +- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 9 +- libs/hwui/renderstate/PixelBufferState.cpp | 42 -- libs/hwui/renderstate/PixelBufferState.h | 38 -- libs/hwui/renderstate/RenderState.cpp | 83 +-- libs/hwui/renderstate/RenderState.h | 20 +- libs/hwui/renderstate/TextureState.cpp | 147 ----- libs/hwui/renderstate/TextureState.h | 98 --- libs/hwui/renderthread/CacheManager.cpp | 4 +- libs/hwui/renderthread/CanvasContext.cpp | 8 - libs/hwui/renderthread/EglManager.cpp | 104 ++++ libs/hwui/renderthread/EglManager.h | 10 + libs/hwui/renderthread/RenderThread.cpp | 4 +- libs/hwui/renderthread/VulkanManager.cpp | 20 +- libs/hwui/renderthread/VulkanManager.h | 8 + libs/hwui/surfacetexture/EGLConsumer.cpp | 675 +++++++++++++++++++++ libs/hwui/surfacetexture/EGLConsumer.h | 311 ++++++++++ libs/hwui/surfacetexture/ImageConsumer.cpp | 152 +++++ libs/hwui/surfacetexture/ImageConsumer.h | 97 +++ libs/hwui/surfacetexture/SurfaceTexture.cpp | 496 +++++++++++++++ libs/hwui/surfacetexture/SurfaceTexture.h | 452 ++++++++++++++ libs/hwui/tests/common/LeakChecker.cpp | 4 - libs/hwui/tests/common/TestUtils.cpp | 11 +- libs/hwui/tests/common/TestUtils.h | 1 - libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp | 23 +- libs/hwui/tests/unit/main.cpp | 3 +- libs/hwui/utils/PaintUtils.h | 1 + native/android/Android.bp | 1 + native/android/surface_texture.cpp | 5 +- 55 files changed, 2491 insertions(+), 2203 deletions(-) delete mode 100644 libs/hwui/Caches.cpp delete mode 100644 libs/hwui/Caches.h delete mode 100644 libs/hwui/GlLayer.cpp delete mode 100644 libs/hwui/GlLayer.h delete mode 100644 libs/hwui/PixelBuffer.cpp delete mode 100644 libs/hwui/PixelBuffer.h delete mode 100644 libs/hwui/Texture.cpp delete mode 100644 libs/hwui/Texture.h delete mode 100644 libs/hwui/VkLayer.cpp delete mode 100644 libs/hwui/VkLayer.h delete mode 100644 libs/hwui/renderstate/PixelBufferState.cpp delete mode 100644 libs/hwui/renderstate/PixelBufferState.h delete mode 100644 libs/hwui/renderstate/TextureState.cpp delete mode 100644 libs/hwui/renderstate/TextureState.h create mode 100644 libs/hwui/surfacetexture/EGLConsumer.cpp create mode 100644 libs/hwui/surfacetexture/EGLConsumer.h create mode 100644 libs/hwui/surfacetexture/ImageConsumer.cpp create mode 100644 libs/hwui/surfacetexture/ImageConsumer.h create mode 100644 libs/hwui/surfacetexture/SurfaceTexture.cpp create mode 100644 libs/hwui/surfacetexture/SurfaceTexture.h (limited to 'libs/hwui/ResourceCache.cpp') diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index 3fcedd0264ae..6ebf35c8e1dc 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -22,8 +22,6 @@ #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" -#include - namespace android { using namespace uirenderer; diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index f8bb77a9650c..755fcfb27141 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -20,7 +20,6 @@ #include "SkMatrix.h" #include "core_jni_helpers.h" -#include #include namespace android { diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index cff772002b14..68f5bef18de1 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -6,7 +6,6 @@ #include "SkBlendMode.h" #include "core_jni_helpers.h" -#include #include using namespace android::uirenderer; diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index d098a355085e..3e464c61665f 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -36,6 +36,7 @@ #include "jni.h" #include #include +#include "surfacetexture/SurfaceTexture.h" // ---------------------------------------------------------------------------- @@ -80,10 +81,10 @@ static bool isProtectedContext() { // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp& surfaceTexture) + const sp& surfaceTexture) { - GLConsumer* const p = - (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); + SurfaceTexture* const p = + (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); if (surfaceTexture.get()) { surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture); } @@ -108,10 +109,10 @@ static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz, } static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, - jobject thiz, sp listener) + jobject thiz, sp listener) { - GLConsumer::FrameAvailableListener* const p = - (GLConsumer::FrameAvailableListener*) + SurfaceTexture::FrameAvailableListener* const p = + (SurfaceTexture::FrameAvailableListener*) env->GetLongField(thiz, fields.frameAvailableListener); if (listener.get()) { listener->incStrong((void*)SurfaceTexture_setSurfaceTexture); @@ -122,8 +123,8 @@ static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get()); } -sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { - return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); +sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { + return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); } sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) { @@ -131,7 +132,7 @@ sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) } sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); sp producer(SurfaceTexture_getProducer(env, thiz)); sp surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL); return surfaceTextureClient; @@ -144,7 +145,7 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) { // ---------------------------------------------------------------------------- -class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener +class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener { public: JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz); @@ -266,12 +267,12 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, consumer->setMaxBufferCount(1); } - sp surfaceTexture; + sp surfaceTexture; if (isDetached) { - surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, + surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } else { - surfaceTexture = new GLConsumer(consumer, texName, + surfaceTexture = new SurfaceTexture(consumer, texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } @@ -306,7 +307,7 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setFrameAvailableListener(0); SurfaceTexture_setFrameAvailableListener(env, thiz, 0); SurfaceTexture_setSurfaceTexture(env, thiz, 0); @@ -315,13 +316,13 @@ static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) static void SurfaceTexture_setDefaultBufferSize( JNIEnv* env, jobject thiz, jint width, jint height) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->updateTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to update texture contents (see " @@ -333,7 +334,7 @@ static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->releaseTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to release texture contents (see " @@ -345,20 +346,20 @@ static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); float* mtx = env->GetFloatArrayElements(jmtx, NULL); surfaceTexture->getTransformMatrix(mtx); env->ReleaseFloatArrayElements(jmtx, mtx, 0); @@ -366,19 +367,19 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz) { - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->isAbandoned(); } diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp index d3a447f1f7dc..1ccb6a8f610c 100644 --- a/core/jni/android_view_TextureLayer.cpp +++ b/core/jni/android_view_TextureLayer.cpp @@ -67,8 +67,7 @@ static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr, jobject surface) { DeferredLayerUpdater* layer = reinterpret_cast(layerUpdaterPtr); - sp surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); - layer->setSurfaceTexture(surfaceTexture); + layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); } static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h index c534d4bb9e0a..0ad25876a008 100644 --- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h +++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h @@ -23,14 +23,14 @@ namespace android { -class GLConsumer; class IGraphicBufferProducer; +class SurfaceTexture; extern sp android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz); extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz); -/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */ -extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */ +extern sp SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); /* gets the producer end of the SurfaceTexture */ extern sp SurfaceTexture_getProducer(JNIEnv* env, jobject thiz); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index edce3050255b..2baacbf541dc 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -176,9 +176,7 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", - "renderstate/PixelBufferState.cpp", "renderstate/RenderState.cpp", - "renderstate/TextureState.cpp", "renderthread/CacheManager.cpp", "renderthread/CanvasContext.cpp", "renderthread/DrawFrameTask.cpp", @@ -190,6 +188,9 @@ cc_defaults { "renderthread/TimeLord.cpp", "renderthread/Frame.cpp", "service/GraphicsStatsService.cpp", + "surfacetexture/EGLConsumer.cpp", + "surfacetexture/ImageConsumer.cpp", + "surfacetexture/SurfaceTexture.cpp", "thread/TaskManager.cpp", "utils/Blur.cpp", "utils/Color.cpp", @@ -201,7 +202,6 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", - "Caches.cpp", "CanvasState.cpp", "CanvasTransform.cpp", "ClipArea.cpp", @@ -210,7 +210,6 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", - "GlLayer.cpp", "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "Interpolator.cpp", @@ -220,7 +219,6 @@ cc_defaults { "Matrix.cpp", "EglReadback.cpp", "PathParser.cpp", - "PixelBuffer.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Properties.cpp", @@ -232,9 +230,7 @@ cc_defaults { "ResourceCache.cpp", "SkiaCanvas.cpp", "Snapshot.cpp", - "Texture.cpp", "VectorDrawable.cpp", - "VkLayer.cpp", "protos/graphicsstats.proto", ], diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp deleted file mode 100644 index 254144448859..000000000000 --- a/libs/hwui/Caches.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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 "GlLayer.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -Caches* Caches::sInstance = nullptr; - -/////////////////////////////////////////////////////////////////////////////// -// Macros -/////////////////////////////////////////////////////////////////////////////// - -#if DEBUG_CACHE_FLUSH -#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) -#else -#define FLUSH_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -Caches::Caches(RenderState& renderState) : mInitialized(false) { - INIT_LOGD("Creating OpenGL renderer caches"); - init(); - initStaticProperties(); -} - -bool Caches::init() { - if (mInitialized) return false; - - ATRACE_NAME("Caches::init"); - - mRegionMesh = nullptr; - - mInitialized = true; - - mPixelBufferState = new PixelBufferState(); - mTextureState = new TextureState(); - mTextureState->constructTexture(*this); - - return true; -} - -void Caches::initStaticProperties() { - // OpenGL ES 3.0+ specific features - gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() && - property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); -} - -void Caches::terminate() { - if (!mInitialized) return; - mRegionMesh.reset(nullptr); - - clearGarbage(); - - delete mPixelBufferState; - mPixelBufferState = nullptr; - delete mTextureState; - mTextureState = nullptr; - mInitialized = false; -} - -/////////////////////////////////////////////////////////////////////////////// -// Memory management -/////////////////////////////////////////////////////////////////////////////// - -void Caches::clearGarbage() {} - -void Caches::flush(FlushMode mode) { - clearGarbage(); - glFinish(); - // Errors during cleanup should be considered non-fatal, dump them and - // and move on. TODO: All errors or just errors like bad surface? - GLUtils::dumpGLErrors(); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h deleted file mode 100644 index 642f9dc50eb1..000000000000 --- a/libs/hwui/Caches.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#pragma once - -#include "DeviceInfo.h" -#include "Extensions.h" -#include "ResourceCache.h" -#include "renderstate/PixelBufferState.h" -#include "renderstate/TextureState.h" -#include "thread/TaskManager.h" -#include "thread/TaskProcessor.h" - -#include -#include - -#include - -#include - -#include - -#include - -#include - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Caches -/////////////////////////////////////////////////////////////////////////////// - -class RenderNode; -class RenderState; - -class ANDROID_API Caches { -public: - static Caches& createInstance(RenderState& renderState) { - LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted"); - sInstance = new Caches(renderState); - return *sInstance; - } - - static Caches& getInstance() { - LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created"); - return *sInstance; - } - - static bool hasInstance() { return sInstance != nullptr; } - -private: - explicit Caches(RenderState& renderState); - static Caches* sInstance; - -public: - enum class FlushMode { Layers = 0, Moderate, Full }; - - /** - * Initialize caches. - */ - bool init(); - - bool isInitialized() { return mInitialized; } - - /** - * Flush the cache. - * - * @param mode Indicates how much of the cache should be flushed - */ - void flush(FlushMode mode); - - /** - * Destroys all resources associated with this cache. This should - * be called after a flush(FlushMode::Full). - */ - void terminate(); - - /** - * Call this on each frame to ensure that garbage is deleted from - * GPU memory. - */ - void clearGarbage(); - - /** - * 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().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; - } - -public: - TaskManager tasks; - - bool gpuPixelBuffersEnabled; - - const Extensions& extensions() const { return DeviceInfo::get()->extensions(); } - PixelBufferState& pixelBufferState() { return *mPixelBufferState; } - TextureState& textureState() { return *mTextureState; } - -private: - void initStaticProperties(); - - static void eventMarkNull(GLsizei length, const GLchar* marker) {} - static void startMarkNull(GLsizei length, const GLchar* marker) {} - static void endMarkNull() {} - - // Used to render layers - std::unique_ptr mRegionMesh; - - bool mInitialized; - - // TODO: move below to RenderState - PixelBufferState* mPixelBufferState = nullptr; - TextureState* mTextureState = nullptr; - -}; // class Caches - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 569de76f294e..00916559a9c2 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,27 +15,20 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderTask.h" #include "utils/PaintUtils.h" namespace android { namespace uirenderer { -DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi) +DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) - , mLayer(nullptr) - , mLayerApi(layerApi) - , mCreateLayerFn(createLayerFn) { + , mLayer(nullptr) { renderState.registerDeferredLayerUpdater(this); } @@ -50,13 +43,9 @@ void DeferredLayerUpdater::destroyLayer() { return; } - if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) { - status_t err = mSurfaceTexture->detachFromContext(); + if (mSurfaceTexture.get() && mGLContextAttached) { + mSurfaceTexture->detachFromView(); mGLContextAttached = false; - if (err != 0) { - // TODO: Elevate to fatal exception - ALOGE("Failed to detach SurfaceTexture from context %d", err); - } } mLayer->postDecStrong(); @@ -75,99 +64,53 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { void DeferredLayerUpdater::apply() { if (!mLayer) { - mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend); + mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); } mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { - if (mLayer->getApi() == Layer::Api::Vulkan) { - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateVkTexImage(); - } - } else { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "apply surfaceTexture with non GL backend %x, GL %x, VK %x", - mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); - if (!mGLContextAttached) { - mGLContextAttached = true; - mUpdateTexImage = true; - mSurfaceTexture->attachToContext(static_cast(mLayer)->getTextureId()); - } - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateTexImage(); + if (!mGLContextAttached) { + mGLContextAttached = true; + mUpdateTexImage = true; + mSurfaceTexture->attachToView(); + } + if (mUpdateTexImage) { + mUpdateTexImage = false; + sk_sp layerImage; + SkMatrix textureTransform; + android_dataspace dataSpace; + bool queueEmpty = true; + // If the SurfaceTexture queue is in synchronous mode, need to discard all + // but latest frame. Since we can't tell which mode it is in, + // do this unconditionally. + do { + layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty, + mRenderState); + } while (layerImage.get() && (!queueEmpty)); + if (layerImage.get()) { + // force filtration if buffer size != layer size + bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); + updateLayer(forceFilter, textureTransform, dataSpace, layerImage); } - GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); - static_cast(mLayer)->setRenderTarget(renderTarget); } + if (mTransform) { - mLayer->getTransform().load(*mTransform); + mLayer->getTransform() = *mTransform; setTransform(nullptr); } } } -void DeferredLayerUpdater::doUpdateTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - if (mSurfaceTexture->updateTexImage() == NO_ERROR) { - float transform[16]; - - int64_t frameNumber = mSurfaceTexture->getFrameNumber(); - // If the GLConsumer queue is in synchronous mode, need to discard all - // but latest frame, using the frame number to tell when we no longer - // have newer frames to target. Since we can't tell which mode it is in, - // do this unconditionally. - int dropCounter = 0; - while (mSurfaceTexture->updateTexImage() == NO_ERROR) { - int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); - if (newFrameNumber == frameNumber) break; - frameNumber = newFrameNumber; - dropCounter++; - } - - bool forceFilter = false; - sp buffer = mSurfaceTexture->getCurrentBuffer(); - if (buffer != nullptr) { - // force filtration if buffer size != layer size - forceFilter = mWidth != static_cast(buffer->getWidth()) || - mHeight != static_cast(buffer->getHeight()); - } - -#if DEBUG_RENDERER - if (dropCounter > 0) { - RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); - } -#endif - mSurfaceTexture->getTransformMatrix(transform); - - updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace()); - } -} - -void DeferredLayerUpdater::doUpdateVkTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, - "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - - static const mat4 identityMatrix; - updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN); - - VkLayer* vkLayer = static_cast(mLayer); - vkLayer->updateTexture(); -} - -void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform, - android_dataspace dataspace) { +void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp& layerImage) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); - mLayer->getTexTransform().load(textureTransform); + mLayer->getTexTransform() = textureTransform; mLayer->setDataSpace(dataspace); + mLayer->setImage(layerImage); } void DeferredLayerUpdater::detachSurfaceTexture() { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index fe3ee7a2b4c6..4c323b861002 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -17,18 +17,19 @@ #pragma once #include +#include #include #include -#include +#include #include #include #include #include +#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" -#include "renderthread/RenderThread.h" namespace android { namespace uirenderer { @@ -41,12 +42,7 @@ class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - typedef std::function colorFilter, int alpha, - SkBlendMode mode, bool blend)> - CreateLayerFn; - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi); + ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState); ANDROID_API ~DeferredLayerUpdater(); @@ -70,13 +66,13 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp& texture) { - if (texture.get() != mSurfaceTexture.get()) { - mSurfaceTexture = texture; + ANDROID_API void setSurfaceTexture(const sp& consumer) { + if (consumer.get() != mSurfaceTexture.get()) { + mSurfaceTexture = consumer; - GLenum target = texture->getCurrentTextureTarget(); + GLenum target = consumer->getCurrentTextureTarget(); LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported GLConsumer with target %x", target); + "set unsupported SurfaceTexture with target %x", target); } } @@ -97,12 +93,11 @@ public: void detachSurfaceTexture(); - void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace); + void updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp& layerImage); void destroyLayer(); - Layer::Api getBackingLayerApi() { return mLayerApi; } - private: RenderState& mRenderState; @@ -113,17 +108,12 @@ private: sk_sp mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp mSurfaceTexture; + sp mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; Layer* mLayer; - Layer::Api mLayerApi; - CreateLayerFn mCreateLayerFn; - - void doUpdateTexImage(); - void doUpdateVkTexImage(); }; } /* namespace uirenderer */ diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp deleted file mode 100644 index 432bb8526465..000000000000 --- a/libs/hwui/GlLayer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 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 "GlLayer.h" - -#include "Caches.h" -#include "RenderNode.h" -#include "renderstate/RenderState.h" - -namespace android { -namespace uirenderer { - -GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode) - , caches(Caches::getInstance()) - , texture(caches) { - texture.mWidth = layerWidth; - texture.mHeight = layerHeight; - texture.blend = blend; -} - -GlLayer::~GlLayer() { - // There's a rare possibility that Caches could have been destroyed already - // since this method is queued up as a task. - // Since this is a reset method, treat this as non-fatal. - if (caches.isInitialized() && texture.mId) { - texture.deleteTexture(); - } -} - -void GlLayer::onGlContextLost() { - texture.deleteTexture(); -} - -void GlLayer::setRenderTarget(GLenum renderTarget) { - if (renderTarget != getRenderTarget()) { - // new render target: bind with new target, and update filter/wrap - texture.mTarget = renderTarget; - if (texture.mId) { - caches.textureState().bindTexture(texture.target(), texture.mId); - } - texture.setFilter(GL_NEAREST, false, true); - texture.setWrap(GL_CLAMP_TO_EDGE, false, true); - } -} - -void GlLayer::generateTexture() { - if (!texture.mId) { - glGenTextures(1, &texture.mId); - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h deleted file mode 100644 index 9f70fdae6790..000000000000 --- a/libs/hwui/GlLayer.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include "Layer.h" - -#include "Texture.h" - -namespace android { -namespace uirenderer { - -// Forward declarations -class Caches; - -/** - * A layer has dimensions and is backed by an OpenGL texture or FBO. - */ -class GlLayer : public Layer { -public: - GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend); - virtual ~GlLayer(); - - uint32_t getWidth() const override { return texture.mWidth; } - - uint32_t getHeight() const override { return texture.mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - texture.updateLayout(width, height, texture.internalFormat(), texture.format(), - texture.target()); - } - - void setBlend(bool blend) override { texture.blend = blend; } - - bool isBlend() const override { return texture.blend; } - - inline GLuint getTextureId() const { return texture.id(); } - - inline GLenum getRenderTarget() const { return texture.target(); } - - void setRenderTarget(GLenum renderTarget); - - void generateTexture(); - - /** - * Lost the GL context but the layer is still around, mark it invalid internally - * so the dtor knows not to do any GL work - */ - void onGlContextLost(); - -private: - Caches& caches; - - /** - * The texture backing this layer. - */ - Texture texture; -}; // struct GlLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index 612bfde1a3fa..a9a7af8f22f3 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "Texture.h" #include "utils/StringUtils.h" #include @@ -117,22 +116,6 @@ void GpuMemoryTracker::onFrameCompleted() { ATRACE_INT(buf, stats.count); } } - - std::vector freeList; - for (const auto& obj : gObjectSet) { - if (obj->objectType() == GpuObjectType::Texture) { - const Texture* texture = static_cast(obj); - if (texture->cleanup) { - ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(), - texture->width(), texture->height()); - freeList.push_back(texture); - } - } - } - for (auto& texture : freeList) { - const_cast(texture)->deleteTexture(); - delete texture; - } } } // namespace uirenderer diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index fb8f0337c95e..f59a2e6ee5c1 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -17,17 +17,17 @@ #include "Layer.h" #include "renderstate/RenderState.h" +#include "utils/Color.h" #include namespace android { namespace uirenderer { -Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter, int alpha, - SkBlendMode mode) +Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alpha, + SkBlendMode mode) : GpuMemoryTracker(GpuObjectType::Layer) , mRenderState(renderState) - , mApi(api) , mColorFilter(colorFilter) , alpha(alpha) , mode(mode) { @@ -36,6 +36,8 @@ Layer::Layer(RenderState& renderState, Api api, sk_sp colorFilter incStrong(nullptr); buildColorSpaceWithFilter(); renderState.registerLayer(this); + texTransform.setIdentity(); + transform.setIdentity(); } Layer::~Layer() { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 31878ac23642..c4e4c1c96ba6 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -23,8 +23,9 @@ #include #include #include - -#include "Matrix.h" +#include +#include +#include namespace android { namespace uirenderer { @@ -40,24 +41,19 @@ class RenderState; */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - enum class Api { - OpenGL = 0, - Vulkan = 1, - }; - - Api getApi() const { return mApi; } + Layer(RenderState& renderState, sk_sp, int alpha, SkBlendMode mode); ~Layer(); - virtual uint32_t getWidth() const = 0; + virtual uint32_t getWidth() const { return mWidth; } - virtual uint32_t getHeight() const = 0; + virtual uint32_t getHeight() const { return mHeight; } - virtual void setSize(uint32_t width, uint32_t height) = 0; + virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } - virtual void setBlend(bool blend) = 0; + virtual void setBlend(bool blend) { mBlend = blend; } - virtual bool isBlend() const = 0; + virtual bool isBlend() const { return mBlend; } inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } @@ -84,9 +80,9 @@ public: inline sk_sp getColorSpaceWithFilter() const { return mColorSpaceWithFilter; } - inline mat4& getTexTransform() { return texTransform; } + inline SkMatrix& getTexTransform() { return texTransform; } - inline mat4& getTransform() { return transform; } + inline SkMatrix& getTransform() { return transform; } /** * Posts a decStrong call to the appropriate thread. @@ -94,16 +90,17 @@ public: */ void postDecStrong(); + inline void setImage(const sk_sp& image) { this->layerImage = image; } + + inline sk_sp getImage() const { return this->layerImage; } + protected: - Layer(RenderState& renderState, Api api, sk_sp, int alpha, SkBlendMode mode); RenderState& mRenderState; private: void buildColorSpaceWithFilter(); - Api mApi; - /** * Color filter used to draw this layer. Optional. */ @@ -137,12 +134,32 @@ private: /** * Optional texture coordinates transform. */ - mat4 texTransform; + SkMatrix texTransform; /** * Optional transform. */ - mat4 transform; + SkMatrix transform; + + /** + * An image backing the layer. + */ + sk_sp layerImage; + + /** + * layer width. + */ + uint32_t mWidth = 0; + + /** + * layer height. + */ + uint32_t mHeight = 0; + + /** + * enable blending + */ + bool mBlend = false; }; // struct Layer diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp deleted file mode 100644 index 910a9889db1f..000000000000 --- a/libs/hwui/PixelBuffer.cpp +++ /dev/null @@ -1,156 +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 "PixelBuffer.h" - -#include "Debug.h" -#include "Extensions.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// CPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class CpuPixelBuffer : public PixelBuffer { -public: - CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - std::unique_ptr mBuffer; -}; - -CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mBuffer(new uint8_t[width * height * formatSize(format)]) {} - -uint8_t* CpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mAccessMode = mode; - } - return mBuffer.get(); -} - -void CpuPixelBuffer::unmap() { - mAccessMode = kAccessMode_None; -} - -void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - &mBuffer[offset]); -} - -/////////////////////////////////////////////////////////////////////////////// -// GPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class GpuPixelBuffer : public PixelBuffer { -public: - GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - ~GpuPixelBuffer(); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - GLuint mBuffer; - uint8_t* mMappedPointer; - Caches& mCaches; -}; - -GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mMappedPointer(nullptr) - , mCaches(Caches::getInstance()) { - glGenBuffers(1, &mBuffer); - - mCaches.pixelBufferState().bind(mBuffer); - glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW); - mCaches.pixelBufferState().unbind(); -} - -GpuPixelBuffer::~GpuPixelBuffer() { - glDeleteBuffers(1, &mBuffer); -} - -uint8_t* GpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mCaches.pixelBufferState().bind(mBuffer); - mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); - if (CC_UNLIKELY(!mMappedPointer)) { - GLUtils::dumpGLErrors(); - LOG_ALWAYS_FATAL("Failed to map PBO"); - } - mAccessMode = mode; - mCaches.pixelBufferState().unbind(); - } - - return mMappedPointer; -} - -void GpuPixelBuffer::unmap() { - if (mAccessMode != kAccessMode_None) { - if (mMappedPointer) { - mCaches.pixelBufferState().bind(mBuffer); - GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - if (status == GL_FALSE) { - ALOGE("Corrupted GPU pixel buffer"); - } - } - mAccessMode = kAccessMode_None; - mMappedPointer = nullptr; - } -} - -void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - // If the buffer is not mapped, unmap() will not bind it - mCaches.pixelBufferState().bind(mBuffer); - unmap(); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - reinterpret_cast(offset)); - mCaches.pixelBufferState().unbind(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Factory -/////////////////////////////////////////////////////////////////////////////// - -PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { - if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { - return new GpuPixelBuffer(format, width, height); - } - return new CpuPixelBuffer(format, width, height); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h deleted file mode 100644 index e7e341b90ad3..000000000000 --- a/libs/hwui/PixelBuffer.h +++ /dev/null @@ -1,198 +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_PIXEL_BUFFER_H -#define ANDROID_HWUI_PIXEL_BUFFER_H - -#include - -#include - -namespace android { -namespace uirenderer { - -/** - * Represents a pixel buffer. A pixel buffer will be backed either by a - * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other - * versions. If the buffer is backed by a PBO it will of type - * GL_PIXEL_UNPACK_BUFFER. - * - * To read from or write into a PixelBuffer you must first map the - * buffer using the map(AccessMode) method. This method returns a - * pointer to the beginning of the buffer. - * - * Before the buffer can be used by the GPU, for instance to upload - * a texture, you must first unmap the buffer. To do so, call the - * unmap() method. - * - * Mapping and unmapping a PixelBuffer can have the side effect of - * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is - * therefore recommended to call Caches::unbindPixelbuffer() after - * using a PixelBuffer to upload to a texture. - */ -class PixelBuffer { -public: - enum BufferType { kBufferType_Auto, kBufferType_CPU }; - - enum AccessMode { - kAccessMode_None = 0, - kAccessMode_Read = GL_MAP_READ_BIT, - kAccessMode_Write = GL_MAP_WRITE_BIT, - kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT - }; - - /** - * Creates a new PixelBuffer object with the specified format and - * dimensions. The buffer is immediately allocated. - * - * The buffer type specifies how the buffer should be allocated. - * By default this method will automatically choose whether to allocate - * a CPU or GPU buffer. - */ - static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height, - BufferType type = kBufferType_Auto); - - virtual ~PixelBuffer() {} - - /** - * Returns the format of this render buffer. - */ - GLenum getFormat() const { return mFormat; } - - /** - * Maps this before with the specified access mode. This method - * returns a pointer to the region of memory where the buffer was - * mapped. - * - * If the buffer is already mapped when this method is invoked, - * this method will return the previously mapped pointer. The - * access mode can only be changed by calling unmap() first. - * - * The specified access mode cannot be kAccessMode_None. - */ - virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0; - - /** - * Returns the current access mode for this buffer. If the buffer - * is not mapped, this method returns kAccessMode_None. - */ - AccessMode getAccessMode() const { return mAccessMode; } - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - */ - virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0; - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - * - * This is a convenience function provided to save callers the - * trouble of computing the offset parameter. - */ - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - upload(x, y, width, height, getOffset(x, y)); - } - - /** - * Returns the width of the render buffer in pixels. - */ - uint32_t getWidth() const { return mWidth; } - - /** - * Returns the height of the render buffer in pixels. - */ - uint32_t getHeight() const { return mHeight; } - - /** - * Returns the size of this pixel buffer in bytes. - */ - uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); } - - /** - * Returns the offset of a pixel in this pixel buffer, in bytes. - */ - uint32_t getOffset(uint32_t x, uint32_t y) const { - return (y * mWidth + x) * formatSize(mFormat); - } - - /** - * Returns the number of bytes per pixel in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatSize(GLenum format) { - switch (format) { - case GL_ALPHA: - return 1; - case GL_RGBA: - return 4; - } - return 0; - } - - /** - * Returns the alpha channel offset in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatAlphaOffset(GLenum format) { - switch (format) { - case GL_ALPHA: - return 0; - case GL_RGBA: - return 3; - } - - ALOGE("unsupported format: %d", format); - return 0; - } - -protected: - /** - * Creates a new render buffer in the specified format and dimensions. - * The format must be GL_ALPHA or GL_RGBA. - */ - PixelBuffer(GLenum format, uint32_t width, uint32_t height) - : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {} - - /** - * Unmaps this buffer, if needed. After the buffer is unmapped, - * the pointer previously returned by map() becomes invalid and - * should not be used. - */ - virtual void unmap() = 0; - - GLenum mFormat; - - uint32_t mWidth; - uint32_t mHeight; - - AccessMode mAccessMode; - -}; // class PixelBuffer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_PIXEL_BUFFER_H diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 0766e3b7ed28..7966845ff814 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,6 @@ #pragma once -#include "Caches.h" #include "DeviceInfo.h" #include "Outline.h" #include "Rect.h" diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 464a58d0c0f8..65bee476f14d 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -15,7 +15,6 @@ */ #include "ResourceCache.h" -#include "Caches.h" namespace android { @@ -112,13 +111,9 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; if (ref == nullptr) { // If we're not tracking this resource, just delete it - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + delete[](int8_t*) resource; return; } ref->destroyed = true; @@ -135,14 +130,10 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource if (ref->destroyed) { switch (ref->resourceType) { case kNinePatch: { - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + int8_t* patch = (int8_t*)resource; + delete[] patch; } break; } } diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp deleted file mode 100644 index 1e90eebe3bb8..000000000000 --- a/libs/hwui/Texture.cpp +++ /dev/null @@ -1,413 +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 "Texture.h" -#include "Caches.h" -#include "utils/GLUtils.h" -#include "utils/MathUtils.h" -#include "utils/TraceUtils.h" - -#include - -#include - -#include - -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 8; - default: - LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); - } -} - -void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { - if (force || wrapS != mWrapS || wrapT != mWrapT) { - mWrapS = wrapS; - mWrapT = wrapT; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); - glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); - } -} - -void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { - if (force || min != mMinFilter || mag != mMagFilter) { - mMinFilter = min; - mMagFilter = mag; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; - - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); - glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); - } -} - -void Texture::deleteTexture() { - mCaches.textureState().deleteTexture(mId); - mId = 0; - mTarget = GL_NONE; - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } -} - -bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - if (mWidth == width && mHeight == height && mFormat == format && - mInternalFormat == internalFormat && mTarget == target) { - return false; - } - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); - return true; -} - -void Texture::resetCachedParams() { - mWrapS = GL_REPEAT; - mWrapT = GL_REPEAT; - mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - mMagFilter = GL_LINEAR; -} - -void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, - GLenum type, const void* pixels) { - GL_CHECKPOINT(MODERATE); - - // We don't have color space information, we assume the data is gamma encoded - mIsLinear = false; - - bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D); - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - resetCachedParams(); - } - mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); - if (needsAlloc) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } - GL_CHECKPOINT(MODERATE); -} - -void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } - mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - buffer->getNativeBuffer(), 0); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); -} - -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(); - if ((stride == width) || useStride) { - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } - - if (resize) { - 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); - } - - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - } else { - // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width - - GLvoid* temp = (GLvoid*)malloc(width * height * bpp); - if (!temp) return; - - uint8_t* pDst = (uint8_t*)temp; - uint8_t* pSrc = (uint8_t*)data; - for (GLsizei i = 0; i < height; i++) { - memcpy(pDst, pSrc, width * bpp); - pDst += width * bpp; - pSrc += stride * bpp; - } - - if (resize) { - 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); - } - - free(temp); - } -} - -void Texture::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: - 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 is upconverted to RGBA_8888 - case kARGB_4444_SkColorType: - case kN32_SkColorType: - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(needSRGB); - *outType = GL_UNSIGNED_BYTE; - break; - case kGray_8_SkColorType: - *outFormat = GL_LUMINANCE; - *outInternalFormat = GL_LUMINANCE; - *outType = GL_UNSIGNED_BYTE; - break; - case kRGBA_F16_SkColorType: - if (caches.extensions().getMajorGlVersion() >= 3) { - // This format is always linear - *outFormat = GL_RGBA; - *outInternalFormat = GL_RGBA16F; - *outType = GL_HALF_FLOAT; - } else { - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(true); - *outType = GL_UNSIGNED_BYTE; - } - break; - default: - LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); - break; - } -} - -SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp sRGB) { - SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), - bitmap.info().alphaType(), - hasLinearBlending ? sRGB : nullptr)); - rgbaBitmap.eraseColor(0); - - if (bitmap.colorType() == kRGBA_F16_SkColorType) { - // Drawing RGBA_F16 onto ARGB_8888 is not supported - bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), - rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); - } else { - SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); - } - - return rgbaBitmap; -} - -bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) { - return info.colorType() == kARGB_4444_SkColorType || - (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && - info.colorSpace()->isSRGB()) || - (info.colorType() == kRGBA_F16_SkColorType && - Caches::getInstance().extensions().getMajorGlVersion() < 3); -} - -void Texture::upload(Bitmap& bitmap) { - ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); - - // We could also enable mipmapping if both bitmap dimensions are powers - // of 2 but we'd have to deal with size changes. Let's keep this simple - const bool canMipMap = mCaches.extensions().hasNPot(); - - // If the texture had mipmap enabled but not anymore, - // force a glTexImage2D to discard the mipmap levels - bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); - bool setDefaultParams = false; - - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - setDefaultParams = true; - } - - bool hasLinearBlending = mCaches.extensions().hasLinearBlending(); - bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace()); - - GLint internalFormat, format, type; - colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending, - &internalFormat, &format, &type); - - // Some devices don't support GL_RGBA16F, so we need to compare the color type - // and internal GL format to decide what to do with 16 bit bitmaps - bool rgba16fNeedsConversion = - bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F; - - // RGBA16F is always linear extended sRGB - if (internalFormat == GL_RGBA16F) { - mIsLinear = true; - } - - mConnector.reset(); - - // Alpha masks don't have color profiles - // If an RGBA16F bitmap needs conversion, we know the target will be sRGB - if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) { - SkColorSpace* colorSpace = bitmap.info().colorSpace(); - // If the bitmap is sRGB we don't need conversion - if (colorSpace != nullptr && !colorSpace->isSRGB()) { - SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); - if (!colorSpace->toXYZD50(&xyzMatrix)) { - ALOGW("Incompatible color space!"); - } else { - SkColorSpaceTransferFn fn; - if (!colorSpace->isNumericalTransferFn(&fn)) { - ALOGW("Incompatible color space, no numerical transfer function!"); - } else { - float data[16]; - xyzMatrix.asColMajorf(data); - - ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC, - fn.fD, fn.fE, fn.fF}; - ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p); - mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB())); - - // A non-sRGB color space might have a transfer function close enough to sRGB - // that we can save shader instructions by using an sRGB sampler - // This is only possible if we have hardware support for sRGB textures - if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() && - !bitmap.isHardware()) { - internalFormat = GL_SRGB8_ALPHA8; - } - } - } - } - } - - GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; - needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target); - - blend = !bitmap.isOpaque(); - mCaches.textureState().bindTexture(mTarget, mId); - - // TODO: Handle sRGB gray bitmaps - if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) { - SkBitmap skBitmap; - bitmap.getSkBitmap(&skBitmap); - sk_sp sRGB = SkColorSpace::MakeSRGB(); - SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); - uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), - rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), - rgbaBitmap.getPixels()); - } else if (bitmap.isHardware()) { - uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); - } else { - uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), - bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), - bitmap.pixels()); - } - - if (canMipMap) { - mipMap = bitmap.hasHardwareMipMap(); - if (mipMap) { - glGenerateMipmap(GL_TEXTURE_2D); - } - } - - if (setDefaultParams) { - setFilter(GL_NEAREST); - setWrap(GL_CLAMP_TO_EDGE); - } -} - -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - mId = id; - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - mConnector.reset(); - // We're wrapping an existing texture, so don't double count this memory - notifySizeChanged(0); -} - -TransferFunctionType Texture::getTransferFunctionType() const { - if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) { - const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters(); - if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) { - if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) && - MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { - if (MathUtils::areEqual(p.g, 1.0f)) { - return TransferFunctionType::None; - } - return TransferFunctionType::Gamma; - } - return TransferFunctionType::Limited; - } - return TransferFunctionType::Full; - } - return TransferFunctionType::None; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h deleted file mode 100644 index 5b7e4e261f30..000000000000 --- a/libs/hwui/Texture.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2010 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_TEXTURE_H -#define ANDROID_HWUI_TEXTURE_H - -#include "GpuMemoryTracker.h" -#include "hwui/Bitmap.h" -#include "utils/Color.h" - -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace android { - -class GraphicBuffer; - -namespace uirenderer { - -class Caches; -class UvMapper; -class Layer; - -/** - * Represents an OpenGL texture. - */ -class Texture : public GpuMemoryTracker { -public: - static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp sRGB); - static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending); - static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, - bool needSRGB, GLint* outInternalFormat, - GLint* outFormat, GLint* outType); - - explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {} - - virtual ~Texture() {} - - inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { - setWrapST(wrap, wrap, bindTexture, force); - } - - virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false); - - inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { - setFilterMinMag(filter, filter, bindTexture, force); - } - - virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false); - - /** - * Convenience method to call glDeleteTextures() on this texture's id. - */ - void deleteTexture(); - - /** - * Sets the width, height, and format of the texture along with allocating - * the texture ID. Does nothing if the width, height, and format are already - * the requested values. - * - * The image data is undefined after calling this. - */ - void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { - upload(internalFormat, width, height, format, - internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); - } - - /** - * Updates this Texture with the contents of the provided Bitmap, - * also setting the appropriate width, height, and format. It is not necessary - * to call resize() prior to this. - * - * Note this does not set the generation from the Bitmap. - */ - void upload(Bitmap& source); - - /** - * Basically glTexImage2D/glTexSubImage2D. - */ - 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 internalFormat, GLint format, - GLenum target); - - GLuint id() const { return mId; } - - uint32_t width() const { return mWidth; } - - uint32_t height() const { return mHeight; } - - GLint format() const { return mFormat; } - - GLint internalFormat() const { return mInternalFormat; } - - GLenum target() const { return mTarget; } - - /** - * Returns nullptr if this texture does not require color space conversion - * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion - * is required. - */ - constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); } - - constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; } - - TransferFunctionType getTransferFunctionType() const; - - /** - * Returns true if this texture uses a linear encoding format. - */ - constexpr bool isLinear() const { return mIsLinear; } - - /** - * Generation of the backing bitmap, - */ - uint32_t generation = 0; - /** - * Indicates whether the texture requires blending. - */ - bool blend = false; - /** - * Indicates whether this texture should be cleaned up after use. - */ - bool cleanup = false; - /** - * Optional, size of the original bitmap. - */ - uint32_t bitmapSize = 0; - /** - * Indicates whether this texture will use trilinear filtering. - */ - bool mipMap = false; - - /** - * Optional, pointer to a texture coordinates mapper. - */ - const UvMapper* uvMapper = nullptr; - - /** - * Whether or not the Texture is marked in use and thus not evictable for - * the current frame. This is reset at the start of a new frame. - */ - void* isInUse = nullptr; - -private: - // TODO: Temporarily grant private access to GlLayer, remove once - // GlLayer can be de-tangled from being a dual-purpose render target - // and external texture wrapper - friend class GlLayer; - - // Returns true if the texture layout (size, format, etc.) changed, false if it was the same - bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target); - void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); - void resetCachedParams(); - - GLuint mId = 0; - uint32_t mWidth = 0; - uint32_t mHeight = 0; - GLint mFormat = 0; - GLint mInternalFormat = 0; - GLenum mTarget = GL_NONE; - EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR; - - /* See GLES spec section 3.8.14 - * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is - * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR. - * s, t, and r wrap modes are all set to REPEAT." - */ - GLenum mWrapS = GL_REPEAT; - GLenum mWrapT = GL_REPEAT; - GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - GLenum mMagFilter = GL_LINEAR; - - // Indicates whether the content of the texture is in linear space - bool mIsLinear = false; - - Caches& mCaches; - - std::unique_ptr mConnector; -}; // struct Texture - -class AutoTexture { -public: - explicit AutoTexture(Texture* texture) : texture(texture) {} - ~AutoTexture() { - if (texture && texture->cleanup) { - texture->deleteTexture(); - delete texture; - } - } - - Texture* const texture; -}; // class AutoTexture - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp deleted file mode 100644 index 30fba7ae7d9b..000000000000 --- a/libs/hwui/VkLayer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 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 "VkLayer.h" - -#include "renderstate/RenderState.h" - -#include -#include - -namespace android { -namespace uirenderer { - -void VkLayer::updateTexture() { - sk_sp surface; - SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType); - surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info); - surface->getCanvas()->clear(SK_ColorBLUE); - mImage = surface->makeImageSnapshot(); -} - -void VkLayer::onVkContextDestroyed() { - mImage = nullptr; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h deleted file mode 100644 index e9664d04b7a5..000000000000 --- a/libs/hwui/VkLayer.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -#pragma once - -#include "Layer.h" - -#include - -namespace android { -namespace uirenderer { -/** - * A layer has dimensions and is backed by a VkImage. - */ -class VkLayer : public Layer { -public: - VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode) - , mWidth(layerWidth) - , mHeight(layerHeight) - , mBlend(blend) {} - - virtual ~VkLayer() {} - - uint32_t getWidth() const override { return mWidth; } - - uint32_t getHeight() const override { return mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - mWidth = width; - mHeight = height; - } - - void setBlend(bool blend) override { mBlend = blend; } - - bool isBlend() const override { return mBlend; } - - sk_sp getImage() { return mImage; } - - void updateTexture(); - - // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to - // destroy any VkImages that were made with that context. - void onVkContextDestroyed(); - -private: - int mWidth; - int mHeight; - bool mBlend; - - sk_sp mImage; - -}; // struct VkLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index a7d37f8aa45c..3939696692d2 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -15,11 +15,11 @@ */ #include "Bitmap.h" -#include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" +#include #include diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index c41f6a6f0ee6..b6cd4b08e5fe 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -15,8 +15,6 @@ */ #include "LayerDrawable.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "GrBackendSurface.h" #include "SkColorFilter.h" @@ -41,42 +39,21 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer return false; } // transform the matrix based on the layer - SkMatrix layerTransform; - layer->getTransform().copyTo(layerTransform); - sk_sp layerImage; + SkMatrix layerTransform = layer->getTransform(); + sk_sp layerImage = layer->getImage(); const int layerWidth = layer->getWidth(); const int layerHeight = layer->getHeight(); - if (layer->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layer); - GrGLTextureInfo externalTexture; - externalTexture.fTarget = glLayer->getRenderTarget(); - externalTexture.fID = glLayer->getTextureId(); - // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't - // expose that info we use it as our default. Further, given that we only use this texture - // as a source this will not impact how Skia uses the texture. The only potential affect - // this is anticipated to have is that for some format types if we are not bound as an OES - // texture we may get invalid results for SKP capture if we read back the texture. - externalTexture.fFormat = GL_RGBA8; - GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture); - layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, - kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); - } else { - SkASSERT(layer->getApi() == Layer::Api::Vulkan); - VkLayer* vkLayer = static_cast(layer); - canvas->clear(SK_ColorGREEN); - layerImage = vkLayer->getImage(); - } if (layerImage) { SkMatrix textureMatrixInv; - layer->getTexTransform().copyTo(textureMatrixInv); + textureMatrixInv = layer->getTexTransform(); // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed // use bottom left origin and remove flipV and invert transformations. SkMatrix flipV; flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); textureMatrixInv.preConcat(flipV); textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight); - textureMatrixInv.postScale(layerWidth, layerHeight); + textureMatrixInv.postScale(layerImage->width(), layerImage->height()); SkMatrix textureMatrix; if (!textureMatrixInv.invert(&textureMatrix)) { textureMatrix = textureMatrixInv; @@ -95,6 +72,9 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(layer->getColorSpaceWithFilter()); + if (layer->getForceFilter()) { + paint.setFilterQuality(kLow_SkFilterQuality); + } const bool nonIdentityMatrix = !matrix.isIdentity(); if (nonIdentityMatrix) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 78f5a71dee3b..2ae37233098e 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,7 +17,6 @@ #include "SkiaOpenGLPipeline.h" #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "LayerDrawable.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" @@ -187,18 +186,9 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, - bool blend) { - GlLayer* layer = - new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); - layer->generateTexture(); - return layer; -} - DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaOpenGLPipeline::onStop() { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index b2519fe59891..5f2eee4523fc 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -20,7 +20,6 @@ #include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" @@ -114,16 +113,10 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp colorFilter, int alpha, SkBlendMode mode, - bool blend) { - return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); -} - DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { mVkManager.initialize(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaVulkanPipeline::onStop() {} diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp deleted file mode 100644 index 3a6efb833c47..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 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 "renderstate/PixelBufferState.h" - -namespace android { -namespace uirenderer { - -PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {} - -bool PixelBufferState::bind(GLuint buffer) { - if (mCurrentPixelBuffer != buffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); - mCurrentPixelBuffer = buffer; - return true; - } - return false; -} - -bool PixelBufferState::unbind() { - if (mCurrentPixelBuffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - mCurrentPixelBuffer = 0; - return true; - } - return false; -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h deleted file mode 100644 index f7ae6c575f6a..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2015 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 RENDERSTATE_PIXELBUFFERSTATE_H -#define RENDERSTATE_PIXELBUFFERSTATE_H - -#include - -namespace android { -namespace uirenderer { - -class PixelBufferState { - friend class Caches; // TODO: move to RenderState -public: - bool bind(GLuint buffer); - bool unbind(); - -private: - PixelBufferState(); - GLuint mCurrentPixelBuffer; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 3be84f588a20..b524bcb096da 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,8 +16,6 @@ #include "renderstate/RenderState.h" #include #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "Snapshot.h" #include "renderthread/CanvasContext.h" @@ -39,44 +37,11 @@ RenderState::RenderState(renderthread::RenderThread& thread) RenderState::~RenderState() { } -void RenderState::onGLContextCreated() { +void RenderState::onContextCreated() { GpuMemoryTracker::onGpuContextCreated(); - - // This is delayed because the first access of Caches makes GL calls - if (!mCaches) { - mCaches = &Caches::createInstance(*this); - } - mCaches->init(); } -static void layerLostGlContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, - "layerLostGlContext on non GL layer"); - static_cast(layer)->onGlContextLost(); -} - -void RenderState::onGLContextDestroyed() { - // TODO: reset all cached state in state objects - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - - mCaches->terminate(); - - destroyLayersInUpdater(); - GpuMemoryTracker::onGpuContextDestroyed(); -} - -void RenderState::onVkContextCreated() { - GpuMemoryTracker::onGpuContextCreated(); -} - -static void layerDestroyedVkContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan, - "layerLostVkContext on non Vulkan layer"); - static_cast(layer)->onVkContextDestroyed(); -} - -void RenderState::onVkContextDestroyed() { - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); +void RenderState::onContextDestroyed() { destroyLayersInUpdater(); GpuMemoryTracker::onGpuContextDestroyed(); } @@ -85,10 +50,6 @@ GrContext* RenderState::getGrContext() const { return mRenderThread.getGrContext(); } -void RenderState::flush(Caches::FlushMode mode) { - if (mCaches) mCaches->flush(mode); -} - void RenderState::onBitmapDestroyed(uint32_t pixelRefId) { // DEAD CODE } @@ -126,42 +87,6 @@ void RenderState::deleteFramebuffer(GLuint fbo) { glDeleteFramebuffers(1, &fbo); } -void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { - if (mode == DrawGlInfo::kModeProcessNoContext) { - // If there's no context we don't need to interrupt as there's - // no gl state to save/restore - (*functor)(mode, info); - } else { - interruptForFunctorInvoke(); - (*functor)(mode, info); - resumeFromFunctorInvoke(); - } -} - -void RenderState::interruptForFunctorInvoke() { - mCaches->textureState().resetActiveTexture(); - debugOverdraw(false, false); - // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glDisable(GL_FRAMEBUFFER_SRGB_EXT); - } -} - -void RenderState::resumeFromFunctorInvoke() { - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glEnable(GL_FRAMEBUFFER_SRGB_EXT); - } - - glViewport(0, 0, mViewportWidth, mViewportHeight); - glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - debugOverdraw(false, false); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - mCaches->textureState().activateTexture(0); - mCaches->textureState().resetBoundTextures(); -} - void RenderState::debugOverdraw(bool enable, bool clear) { // DEAD CODE } @@ -190,5 +115,9 @@ void RenderState::dump() { // DEAD CODE } +renderthread::RenderThread& RenderState::getRenderThread() { + return mRenderThread; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 97785a46dcd7..f39aa4b96547 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,8 +16,6 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H -#include "Caches.h" -#include "renderstate/PixelBufferState.h" #include "utils/Macros.h" #include @@ -34,7 +32,6 @@ class GrContext; namespace android { namespace uirenderer { -class Caches; class Layer; class DeferredLayerUpdater; @@ -44,22 +41,16 @@ class CanvasContext; class RenderThread; } -// TODO: Replace Cache's GL state tracking with this. For now it's more a thin // wrapper of Caches for users to migrate to. class RenderState { PREVENT_COPY_AND_ASSIGN(RenderState); friend class renderthread::RenderThread; - friend class Caches; friend class renderthread::CacheManager; public: - void onGLContextCreated(); - void onGLContextDestroyed(); + void onContextCreated(); + void onContextDestroyed(); - void onVkContextCreated(); - void onVkContextDestroyed(); - - void flush(Caches::FlushMode flushMode); void onBitmapDestroyed(uint32_t pixelRefId); void setViewport(GLsizei width, GLsizei height); @@ -70,8 +61,6 @@ public: GLuint createFramebuffer(); void deleteFramebuffer(GLuint fbo); - void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); - void debugOverdraw(bool enable, bool clear); void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } @@ -101,16 +90,15 @@ public: void dump(); + renderthread::RenderThread& getRenderThread(); + private: - void interruptForFunctorInvoke(); - void resumeFromFunctorInvoke(); void destroyLayersInUpdater(); explicit RenderState(renderthread::RenderThread& thread); ~RenderState(); renderthread::RenderThread& mRenderThread; - Caches* mCaches = nullptr; std::set mActiveLayers; std::set mActiveLayerUpdaters; diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp deleted file mode 100644 index 470b4f5de97f..000000000000 --- a/libs/hwui/renderstate/TextureState.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2015 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 "renderstate/TextureState.h" - -#include "Caches.h" -#include "utils/TraceUtils.h" - -#include -#include -#include -#include - -namespace android { -namespace uirenderer { - -// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is -static const int SHADOW_LUT_SIZE = 128; - -// Must define as many texture units as specified by kTextureUnitsCount -const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3}; - -TextureState::TextureState() : mTextureUnit(0) { - glActiveTexture(kTextureUnits[0]); - resetBoundTextures(); - - GLint maxTextureUnits; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, - "At least %d texture units are required!", kTextureUnitsCount); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); -} - -TextureState::~TextureState() { - if (mShadowLutTexture != nullptr) { - mShadowLutTexture->deleteTexture(); - } -} - -/** - * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to - * darkness at that spot. Input values of 0->1 should be mapped within the same - * range, but can affect the curve for a different visual falloff. - * - * This is used to populate the shadow LUT texture for quick lookup in the - * shadow shader. - */ -static float computeShadowOpacity(float ratio) { - // exponential falloff function provided by UX - float val = 1 - ratio; - return exp(-val * val * 4.0) - 0.018; -} - -void TextureState::constructTexture(Caches& caches) { - if (mShadowLutTexture == nullptr) { - mShadowLutTexture.reset(new Texture(caches)); - - unsigned char bytes[SHADOW_LUT_SIZE]; - for (int i = 0; i < SHADOW_LUT_SIZE; i++) { - float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f); - bytes[i] = computeShadowOpacity(inputRatio) * 255; - } - mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes); - mShadowLutTexture->setFilter(GL_LINEAR); - mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE); - } -} - -void TextureState::activateTexture(GLuint textureUnit) { - LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, - "Tried to use texture unit index %d, only %d exist", textureUnit, - kTextureUnitsCount); - if (mTextureUnit != textureUnit) { - glActiveTexture(kTextureUnits[textureUnit]); - mTextureUnit = textureUnit; - } -} - -void TextureState::resetActiveTexture() { - mTextureUnit = -1; -} - -void TextureState::bindTexture(GLuint texture) { - if (mBoundTextures[mTextureUnit] != texture) { - glBindTexture(GL_TEXTURE_2D, texture); - mBoundTextures[mTextureUnit] = texture; - } -} - -void TextureState::bindTexture(GLenum target, GLuint texture) { - if (target == GL_TEXTURE_2D) { - bindTexture(texture); - } else { - // GLConsumer directly calls glBindTexture() with - // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target - // since the cached state could be stale - glBindTexture(target, texture); - } -} - -void TextureState::deleteTexture(GLuint texture) { - // When glDeleteTextures() is called on a currently bound texture, - // OpenGL ES specifies that the texture is then considered unbound - // Consider the following series of calls: - // - // glGenTextures -> creates texture name 2 - // glBindTexture(2) - // glDeleteTextures(2) -> 2 is now unbound - // glGenTextures -> can return 2 again - // - // If we don't call glBindTexture(2) after the second glGenTextures - // call, any texture operation will be performed on the default - // texture (name=0) - - unbindTexture(texture); - - glDeleteTextures(1, &texture); -} - -void TextureState::resetBoundTextures() { - for (int i = 0; i < kTextureUnitsCount; i++) { - mBoundTextures[i] = 0; - } -} - -void TextureState::unbindTexture(GLuint texture) { - for (int i = 0; i < kTextureUnitsCount; i++) { - if (mBoundTextures[i] == texture) { - mBoundTextures[i] = 0; - } - } -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h deleted file mode 100644 index f1996d431fa2..000000000000 --- a/libs/hwui/renderstate/TextureState.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2015 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 RENDERSTATE_TEXTURESTATE_H -#define RENDERSTATE_TEXTURESTATE_H - -#include "Texture.h" -#include "Vertex.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -class Texture; - -class TextureState { - friend class Caches; // TODO: move to RenderState -public: - void constructTexture(Caches& caches); - - /** - * Activate the specified texture unit. The texture unit must - * be specified using an integer number (0 for GL_TEXTURE0 etc.) - */ - void activateTexture(GLuint textureUnit); - - /** - * Invalidate the cached value of the active texture unit. - */ - void resetActiveTexture(); - - /** - * Binds the specified texture as a GL_TEXTURE_2D texture. - * All texture bindings must be performed with this method or - * bindTexture(GLenum, GLuint). - */ - void bindTexture(GLuint texture); - - /** - * Binds the specified texture with the specified render target. - * All texture bindings must be performed with this method or - * bindTexture(GLuint). - */ - void bindTexture(GLenum target, GLuint texture); - - /** - * Deletes the specified texture and clears it from the cache - * of bound textures. - * All textures must be deleted using this method. - */ - void deleteTexture(GLuint texture); - - /** - * Signals that the cache of bound textures should be cleared. - * Other users of the context may have altered which textures are bound. - */ - void resetBoundTextures(); - - /** - * Clear the cache of bound textures. - */ - void unbindTexture(GLuint texture); - - Texture* getShadowLutTexture() { return mShadowLutTexture.get(); } - -private: - // total number of texture units available for use - static const int kTextureUnitsCount = 4; - - TextureState(); - ~TextureState(); - GLuint mTextureUnit; - - // Caches texture bindings for the GL_TEXTURE_2D target - GLuint mBoundTextures[kTextureUnitsCount]; - - std::unique_ptr mShadowLutTexture; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 82bfc4943526..7acc44ca65b7 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -21,6 +21,7 @@ #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" +#include "Properties.h" #include "renderstate/RenderState.h" #include @@ -215,11 +216,12 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat(" Layer Info:\n"); } + const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL + ? "GlLayer" : "VkLayer"; size_t layerMemoryTotal = 0; for (std::set::iterator it = renderState->mActiveLayers.begin(); it != renderState->mActiveLayers.end(); it++) { const Layer* layer = *it; - const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5d7252304bf2..8b07d1dadeb6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,7 +18,6 @@ #include #include "AnimationContext.h" -#include "Caches.h" #include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" @@ -495,13 +494,6 @@ void CanvasContext::draw() { } GpuMemoryTracker::onFrameCompleted(); -#ifdef BUGREPORT_FONT_CACHE_USAGE - auto renderType = Properties::getRenderPipelineType(); - if (RenderPipelineType::OpenGL == renderType) { - Caches& caches = Caches::getInstance(); - caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); - } -#endif } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index cd21822df5b1..5f8d7ad3373a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "utils/StringUtils.h" @@ -464,6 +465,109 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } +status_t EglManager::fenceWait(sp& fence) { + if (!hasEglContext()) { + ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Block GPU on the fence. + // Create an EGLSyncKHR from the current fence. + int fenceFd = fence->dup(); + if (fenceFd == -1) { + ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, + EGL_NONE + }; + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(mEglDisplay, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(mEglDisplay, sync); + if (eglErr != EGL_SUCCESS) { + ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + // Block CPU on the fence. + status_t err = fence->waitForever("EglManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); + return err; + } + } + return OK; +} + +status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, + sp& nativeFence) { + if (!hasEglContext()) { + ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync); + eglDestroySyncKHR(mEglDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGE("EglManager::createReleaseFence: error dup'ing native fence " + "fd: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + nativeFence = new Fence(fenceFd); + *eglFence = EGL_NO_SYNC_KHR; + } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { + if (*eglFence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000); + if (result == EGL_FALSE) { + ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(mEglDisplay, *eglFence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr); + if (*eglFence == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + } + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 8e8bb8b68a1c..507673adf26e 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -17,8 +17,10 @@ #define EGLMANAGER_H #include +#include #include #include +#include #include #include @@ -66,6 +68,14 @@ public: EGLDisplay eglDisplay() const { return mEglDisplay; } + // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension + // support is missing, block the CPU on the fence. + status_t fenceWait(sp& fence); + + // Creates a fence that is signaled, when all the pending GL commands are flushed. + // Depending on installed extensions, the result is either Android native fence or EGL fence. + status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp& nativeFence); + private: void initExtensions(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 36ffaee2458b..2322fbf21f27 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -178,7 +178,7 @@ void RenderThread::requireGlContext() { return; } mEglManager->initialize(); - renderState().onGLContextCreated(); + renderState().onContextCreated(); #ifdef HWUI_GLES_WRAP_ENABLED debug::GlesDriver* driver = debug::GlesDriver::get(); @@ -202,7 +202,7 @@ void RenderThread::requireGlContext() { void RenderThread::destroyGlContext() { if (mEglManager->hasEglContext()) { setGrContext(nullptr); - renderState().onGLContextDestroyed(); + renderState().onContextDestroyed(); mEglManager->destroy(); } } diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index cc4b87adfd7b..038e13c513fd 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -40,7 +40,7 @@ namespace renderthread { VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { - mRenderThread.renderState().onVkContextDestroyed(); + mRenderThread.renderState().onContextDestroyed(); mRenderThread.setGrContext(nullptr); if (VK_NULL_HANDLE != mCommandPool) { @@ -405,7 +405,7 @@ void VulkanManager::initialize() { mSwapBehavior = SwapBehavior::BufferAge; } - mRenderThread.renderState().onVkContextCreated(); + mRenderThread.renderState().onContextCreated(); } // Returns the next BackbufferInfo to use for the next draw. The function will make sure all @@ -982,6 +982,22 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } +status_t VulkanManager::fenceWait(sp& fence) { + //TODO: Insert a wait on fence command into the Vulkan command buffer. + // Block CPU on the fence. + status_t err = fence->waitForever("VulkanManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); + return err; + } + return OK; +} + +status_t VulkanManager::createReleaseFence(sp& nativeFence) { + //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 5524c39d7a0c..ebc11a50685e 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -23,6 +23,8 @@ #include #include +#include +#include #include class GrVkExtensions; @@ -110,6 +112,12 @@ public: // Presents the current VkImage. void swapBuffers(VulkanSurface* surface); + // Inserts a wait on fence command into the Vulkan command buffer. + status_t fenceWait(sp& fence); + + // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. + status_t createReleaseFence(sp& nativeFence); + private: friend class RenderThread; diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp new file mode 100644 index 000000000000..c8220c6cb0d4 --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2018 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include "EGLConsumer.h" +#include "SurfaceTexture.h" + +#include +#include +#include + +#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" +#define EGL_PROTECTED_CONTENT_EXT 0x32C0 + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +static const struct { + uint32_t width, height; + char const* bits; +} kDebugData = {15, 12, + "_______________" + "_______________" + "_____XX_XX_____" + "__X_X_____X_X__" + "__X_XXXXXXX_X__" + "__XXXXXXXXXXX__" + "___XX_XXX_XX___" + "____XXXXXXX____" + "_____X___X_____" + "____X_____X____" + "_______________" + "_______________"}; + +Mutex EGLConsumer::sStaticInitLock; +sp EGLConsumer::sReleasedTexImageBuffer; + +static bool hasEglProtectedContentImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); + bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); + bool atEnd = (cropExtLen + 1) < extsLen && + !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); + bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} + +static bool hasEglProtectedContent() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglProtectedContentImpl(); + return hasIt; +} + +EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} + +status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + return err; + } + + BufferItem item; + + // Acquire the next buffer. + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + err = st.acquireBufferLocked(&item, 0); + if (err != NO_ERROR) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // We always bind the texture even if we don't update its contents. + EGC_LOGV("updateTexImage: no buffers were available"); + glBindTexture(st.mTexTarget, st.mTexName); + err = NO_ERROR; + } else { + EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); + } + return err; + } + + // Release the previous buffer. + err = updateAndReleaseLocked(item, nullptr, st); + if (err != NO_ERROR) { + // We always bind the texture. + glBindTexture(st.mTexTarget, st.mTexName); + return err; + } + + // Bind the new buffer to the GL texture, and wait until it's ready. + return bindTextureImageLocked(st); +} + +status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = NO_ERROR; + + // if we're detached, no need to validate EGL's state -- we won't use it. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + err = checkAndUpdateEglStateLocked(st, true); + if (err != NO_ERROR) { + return err; + } + } + + // Update the EGLConsumer state. + int buf = st.mCurrentTexture; + if (buf != BufferQueue::INVALID_BUFFER_SLOT) { + EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); + + // if we're detached, we just use the fence that was created in detachFromContext() + // so... basically, nothing more to do here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // Do whatever sync ops we need to do before releasing the slot. + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); + return err; + } + } + + err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { + EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); + return err; + } + + if (mReleasedTexImage == nullptr) { + mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + } + + st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureImage = mReleasedTexImage; + st.mCurrentCrop.makeInvalid(); + st.mCurrentTransform = 0; + st.mCurrentTimestamp = 0; + st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; + st.mCurrentFence = Fence::NO_FENCE; + st.mCurrentFenceTime = FenceTime::NO_FENCE; + + // detached, don't touch the texture (and we may not even have an + // EGLDisplay here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // This binds a dummy buffer (mReleasedTexImage). + status_t result = bindTextureImageLocked(st); + if (result != NO_ERROR) { + return result; + } + } + } + + return NO_ERROR; +} + +sp EGLConsumer::getDebugTexImageBuffer() { + Mutex::Autolock _l(sStaticInitLock); + if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { + // The first time, create the debug texture in case the application + // continues to use it. + sp buffer = new GraphicBuffer( + kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); + uint32_t* bits; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast(&bits)); + uint32_t stride = buffer->getStride(); + uint32_t height = buffer->getHeight(); + memset(bits, 0, stride * height * 4); + for (uint32_t y = 0; y < kDebugData.height; y++) { + for (uint32_t x = 0; x < kDebugData.width; x++) { + bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 + : 0xFFFFFFFF; + } + bits += stride; + } + buffer->unlock(); + sReleasedTexImageBuffer = buffer; + } + return sReleasedTexImageBuffer; +} + +void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior EglImage created is using a stale buffer. This + // replaces any old EglImage with a new one (using the new buffer). + int slot = item->mSlot; + if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } +} + +void EGLConsumer::onReleaseBufferLocked(int buf) { + mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st) { + status_t err = NO_ERROR; + + int slot = item.mSlot; + + if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { + EGC_LOGE( + "updateAndRelease: EGLConsumer is not attached to an OpenGL " + "ES context"); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return INVALID_OPERATION; + } + + // Confirm state. + err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return err; + } + + // Ensure we have a valid EglImageKHR for the slot, creating an EglImage + // if nessessary, for the gralloc buffer currently in the slot in + // ConsumerBase. + // We may have to do this even when item.mGraphicBuffer == NULL (which + // means the buffer was previously acquired). + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, + slot); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return UNKNOWN_ERROR; + } + + // Do whatever sync ops we need to do before releasing the old slot. + if (slot != st.mCurrentTexture) { + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + // Release the buffer we just acquired. It's not safe to + // release the old buffer, so instead we just drop the new frame. + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + return err; + } + } + + EGC_LOGV( + "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, + mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, + slot, st.mSlots[slot].mGraphicBuffer->handle); + + // Hang onto the pointer so that it isn't freed in the call to + // releaseBufferLocked() if we're in shared buffer mode and both buffers are + // the same. + sp nextTextureImage = mEglSlots[slot].mEglImage; + + // release old buffer + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (pendingRelease == nullptr) { + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, + mEglSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), + status); + err = status; + // keep going, with error raised [?] + } + } else { + pendingRelease->currentTexture = st.mCurrentTexture; + pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); + pendingRelease->display = mEglDisplay; + pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; + pendingRelease->isPending = true; + } + } + + // Update the EGLConsumer state. + st.mCurrentTexture = slot; + mCurrentTextureImage = nextTextureImage; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + + st.computeCurrentTransformMatrixLocked(); + + return err; +} + +status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { + if (mEglDisplay == EGL_NO_DISPLAY) { + ALOGE("bindTextureImage: invalid display"); + return INVALID_OPERATION; + } + + GLenum error; + while ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); + } + + glBindTexture(st.mTexTarget, st.mTexName); + if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { + EGC_LOGE("bindTextureImage: no currently-bound texture"); + return NO_INIT; + } + + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + + // In the rare case that the display is terminated and then initialized + // again, we can't detect that the display changed (it didn't), but the + // image is invalid. In this case, repeat the exact same steps while + // forcing the creation of a new image. + if ((error = glGetError()) != GL_NO_ERROR) { + glBindTexture(st.mTexTarget, st.mTexName); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); + if (result != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + if ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); + return UNKNOWN_ERROR; + } + } + + // Wait for the new buffer to be ready. + return doGLFenceWaitLocked(st); +} + +status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (!contextCheck) { + // if this is the first time we're called, mEglDisplay/mEglContext have + // never been set, so don't error out (below). + if (mEglDisplay == EGL_NO_DISPLAY) { + mEglDisplay = dpy; + } + if (mEglContext == EGL_NO_CONTEXT) { + mEglContext = ctx; + } + } + + if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); + return INVALID_OPERATION; + } + + mEglDisplay = dpy; + mEglContext = ctx; + return NO_ERROR; +} + +status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { + EGC_LOGE("detachFromContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { + EGC_LOGE("detachFromContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { + status_t err = syncForReleaseLocked(dpy, st); + if (err != OK) { + return err; + } + + glDeleteTextures(1, &st.mTexName); + } + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + + return OK; +} + +status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { + // Initialize mCurrentTextureImage if there is a current buffer from past attached state. + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + if (!mEglSlots[slot].mEglImage.get()) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } + mCurrentTextureImage = mEglSlots[slot].mEglImage; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (dpy == EGL_NO_DISPLAY) { + EGC_LOGE("attachToContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (ctx == EGL_NO_CONTEXT) { + EGC_LOGE("attachToContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + // We need to bind the texture regardless of whether there's a current + // buffer. + glBindTexture(st.mTexTarget, GLuint(tex)); + + mEglDisplay = dpy; + mEglContext = ctx; + st.mTexName = tex; + st.mOpMode = SurfaceTexture::OpMode::attachedToGL; + + if (mCurrentTextureImage != nullptr) { + // This may wait for a buffer a second time. This is likely required if + // this is a different context, since otherwise the wait could be skipped + // by bouncing through another context. For the same context the extra + // wait is redundant. + status_t err = bindTextureImageLocked(st); + if (err != NO_ERROR) { + return err; + } + } + + return OK; +} + +status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { + EGC_LOGV("syncForReleaseLocked"); + + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); + eglDestroySyncKHR(dpy, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + EGC_LOGE( + "syncForReleaseLocked: error dup'ing native fence " + "fd: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + sp fence(new Fence(fenceFd)); + status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, + mCurrentTextureImage->graphicBuffer(), fence); + if (err != OK) { + EGC_LOGE( + "syncForReleaseLocked: error adding release fence: " + "%s (%d)", + strerror(-err), err); + return err; + } + } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { + EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; + if (fence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (result == EGL_FALSE) { + EGC_LOGE( + "syncForReleaseLocked: error waiting for previous " + "fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + EGC_LOGE( + "syncForReleaseLocked: timeout waiting for previous " + "fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(dpy, fence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); + if (fence == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + mEglSlots[st.mCurrentTexture].mEglFence = fence; + } + } + + return OK; +} + +status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + EGC_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (st.mCurrentFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = st.mCurrentFence->dup(); + if (fenceFd == -1) { + EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); + if (err != NO_ERROR) { + EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +void EGLConsumer::onFreeBufferLocked(int slotIndex) { + mEglSlots[slotIndex].mEglImage.clear(); +} + +void EGLConsumer::onAbandonLocked() { + mCurrentTextureImage.clear(); +} + +EGLConsumer::EglImage::EglImage(sp graphicBuffer) + : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} + +EGLConsumer::EglImage::~EglImage() { + if (mEglImage != EGL_NO_IMAGE_KHR) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("~EglImage: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + } +} + +status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { + // If there's an image and it's no longer valid, destroy it. + bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; + bool displayInvalid = mEglDisplay != eglDisplay; + if (haveImage && (displayInvalid || forceCreation)) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("createIfNeeded: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + mEglImage = EGL_NO_IMAGE_KHR; + mEglDisplay = EGL_NO_DISPLAY; + } + + // If there's no image, create one. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = eglDisplay; + mEglImage = createImage(mEglDisplay, mGraphicBuffer); + } + + // Fail if we can't create a valid image. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = EGL_NO_DISPLAY; + const sp& buffer = mGraphicBuffer; + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), + buffer->getPixelFormat()); + return UNKNOWN_ERROR; + } + + return OK; +} + +void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { + glEGLImageTargetTexture2DOES(texTarget, static_cast(mEglImage)); +} + +EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, + const sp& graphicBuffer) { + EGLClientBuffer cbuf = static_cast(graphicBuffer->getNativeBuffer()); + const bool createProtectedImage = + (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, + EGL_TRUE, + createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + createProtectedImage ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + eglInitialize(dpy, nullptr, nullptr); + EGLImageKHR image = + eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + if (image == EGL_NO_IMAGE_KHR) { + EGLint error = eglGetError(); + ALOGE("error creating EGLImage: %#x", error); + eglTerminate(dpy); + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h new file mode 100644 index 000000000000..eccb08298f6f --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +namespace android { + +class SurfaceTexture; + +/* + * EGLConsumer implements the parts of SurfaceTexture that deal with + * textures attached to an GL context. + */ +class EGLConsumer { +public: + EGLConsumer(); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(SurfaceTexture& st); + + /* + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(SurfaceTexture& st); + + /** + * detachFromContext detaches the EGLConsumer from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a EGLConsumer from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a EGLConsumer has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the EGLConsumer is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(SurfaceTexture& st); + + /** + * attachToContext attaches a EGLConsumer that is currently in the + * 'detached' state to the current OpenGL ES context. A EGLConsumer is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * EGLConsumer that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex, SurfaceTexture& st); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + + /** + * onAbandonLocked amends the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + void onAbandonLocked(); + +protected: + struct PendingRelease { + PendingRelease() + : isPending(false) + , currentTexture(-1) + , graphicBuffer() + , display(nullptr) + , fence(nullptr) {} + + bool isPending; + int currentTexture; + sp graphicBuffer; + EGLDisplay display; + EGLSyncKHR fence; + }; + + /** + * This releases the buffer in the slot referenced by mCurrentTexture, + * then updates state to refer to the BufferItem, which must be a + * newly-acquired buffer. If pendingRelease is not null, the parameters + * which would have been passed to releaseBufferLocked upon the successful + * completion of the method will instead be returned to the caller, so that + * it may call releaseBufferLocked itself later. + */ + status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st); + + /** + * Binds mTexName and the current buffer to mTexTarget. Uses + * mCurrentTexture if it's set, mCurrentTextureImage if not. If the + * bind succeeds, this calls doGLFenceWait. + */ + status_t bindTextureImageLocked(SurfaceTexture& st); + + /** + * Gets the current EGLDisplay and EGLContext values, and compares them + * to mEglDisplay and mEglContext. If the fields have been previously + * set, the values must match; if not, the fields are set to the current + * values. + * The contextCheck argument is used to ensure that a GL context is + * properly set; when set to false, the check is not performed. + */ + status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); + + /** + * EglImage is a utility class for tracking and creating EGLImageKHRs. There + * is primarily just one image per slot, but there is also special cases: + * - For releaseTexImage, we use a debug image (mReleasedTexImage) + * - After freeBuffer, we must still keep the current image/buffer + * Reference counting EGLImages lets us handle all these cases easily while + * also only creating new EGLImages from buffers when required. + */ + class EglImage : public LightRefBase { + public: + EglImage(sp graphicBuffer); + + /** + * createIfNeeded creates an EGLImage if required (we haven't created + * one yet, or the EGLDisplay or crop-rect has changed). + */ + status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); + + /** + * This calls glEGLImageTargetTexture2DOES to bind the image to the + * texture in the specified texture target. + */ + void bindToTextureTarget(uint32_t texTarget); + + const sp& graphicBuffer() { return mGraphicBuffer; } + const native_handle* graphicBufferHandle() { + return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; + } + + private: + // Only allow instantiation using ref counting. + friend class LightRefBase; + virtual ~EglImage(); + + // createImage creates a new EGLImage from a GraphicBuffer. + EGLImageKHR createImage(EGLDisplay dpy, const sp& graphicBuffer); + + // Disallow copying + EglImage(const EglImage& rhs); + void operator=(const EglImage& rhs); + + // mGraphicBuffer is the buffer that was used to create this image. + sp mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEGLDisplay is the EGLDisplay that was used to create mEglImage. + EGLDisplay mEglDisplay; + + // mCropRect is the crop rectangle passed to EGL when mEglImage + // was created. + Rect mCropRect; + }; + + /** + * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command + * stream to ensure that it is safe for future OpenGL ES commands to + * access the current texture buffer. + */ + status_t doGLFenceWaitLocked(SurfaceTexture& st) const; + + /** + * syncForReleaseLocked performs the synchronization needed to release the + * current slot from an OpenGL ES context. If needed it will set the + * current slot's fence to guard against a producer accessing the buffer + * before the outstanding accesses have completed. + */ + status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); + + /** + * returns a graphic buffer used when the texture image has been released + */ + static sp getDebugTexImageBuffer(); + + /** + * The default consumer usage flags that EGLConsumer always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the EGLConsumer user. In particular, EGLConsumer will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentTextureImage is the EglImage/buffer of the current texture. It's + * possible that this buffer is not associated with any buffer slot, so we + * must track it separately in order to support the getCurrentBuffer method. + */ + sp mCurrentTextureImage; + + /** + * EGLSlot contains the information and object references that + * EGLConsumer maintains about a BufferQueue buffer slot. + */ + struct EglSlot { + EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + /** + * mEglImage is the EGLImage created from mGraphicBuffer. + */ + sp mEglImage; + + /** + * mFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. It is initialized + * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + * on a compile-time option) set to a new sync object in updateTexImage. + */ + EGLSyncKHR mEglFence; + }; + + /** + * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently + * associated. It is intialized to EGL_NO_DISPLAY and gets set to the + * current display when updateTexImage is called for the first time and when + * attachToContext is called. + */ + EGLDisplay mEglDisplay; + + /** + * mEglContext is the OpenGL ES context with which this EGLConsumer is + * currently associated. It is initialized to EGL_NO_CONTEXT and gets set + * to the current GL context when updateTexImage is called for the first + * time and when attachToContext is called. + */ + EGLContext mEglContext; + + /** + * mEGLSlots stores the buffers that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; + + /** + * protects static initialization + */ + static Mutex sStaticInitLock; + + /** + * mReleasedTexImageBuffer is a dummy buffer used when in single buffer + * mode and releaseTexImage() has been called + */ + static sp sReleasedTexImageBuffer; + sp mReleasedTexImage; +}; + +}; // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp new file mode 100644 index 000000000000..9ffccfb4d340 --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 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 "ImageConsumer.h" +#include +#include "Properties.h" +#include "SurfaceTexture.h" +#include "renderstate/RenderState.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "renderthread/VulkanManager.h" + +// Macro for including the SurfaceTexture name in log messages +#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +namespace android { + +void ImageConsumer::onFreeBufferLocked(int slotIndex) { + mImageSlots[slotIndex].mImage.reset(); +} + +void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. + if (item->mGraphicBuffer != nullptr) { + mImageSlots[item->mSlot].mImage.reset(); + } +} + +void ImageConsumer::onReleaseBufferLocked(int buf) { + mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer) { + if (!mImage.get()) { + mImage = graphicBuffer.get() + ? SkImage::MakeFromAHardwareBuffer( + reinterpret_cast(graphicBuffer.get()), + kPremul_SkAlphaType) + : nullptr; + } +} + +sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, + uirenderer::RenderState& renderState) { + BufferItem item; + status_t err; + err = st.acquireBufferLocked(&item, 0); + if (err != OK) { + if (err != BufferQueue::NO_BUFFER_AVAILABLE) { + IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } else { + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + *queueEmpty = true; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; + } + } + return nullptr; + } + + int slot = item.mSlot; + if (item.mFence->isValid()) { + // Wait on the producer fence for the buffer to be ready. + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); + } else { + err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence); + } + if (err != OK) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Release old buffer. + if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { + // If needed, set the released slot's fence to guard against a producer accessing the + // buffer before the outstanding accesses have completed. + sp releaseFence; + EGLDisplay display = EGL_NO_DISPLAY; + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + auto& eglManager = renderState.getRenderThread().eglManager(); + display = eglManager.eglDisplay(); + err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence, + releaseFence); + } else { + err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence); + } + if (OK != err) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + + if (releaseFence.get()) { + status_t err = st.addReleaseFenceLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); + if (err != OK) { + IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Finally release the old buffer. + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, + mImageSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); + err = status; + // Keep going, with error raised. + } + } + + // Update the state. + st.mCurrentTexture = slot; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + st.computeCurrentTransformMatrixLocked(); + + *queueEmpty = false; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; +} + +} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h new file mode 100644 index 000000000000..31ee8db52874 --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + +namespace android { + +namespace uirenderer { +class RenderState; +} + +class SurfaceTexture; + +/* + * ImageConsumer implements the parts of SurfaceTexture that deal with + * images consumed by HWUI view system. + */ +class ImageConsumer { +public: + sk_sp dequeueImage(bool* queueEmpty, SurfaceTexture& cb, + uirenderer::RenderState& renderState); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the SkImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + +private: + /** + * ImageSlot contains the information and object references that + * ImageConsumer maintains about a BufferQueue buffer slot. + */ + struct ImageSlot { + ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + // mImage is the SkImage created from mGraphicBuffer. + sk_sp mImage; + + /** + * mEglFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. + */ + EGLSyncKHR mEglFence; + + void createIfNeeded(sp graphicBuffer); + }; + + /** + * ImageConsumer stores the SkImages that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; +}; + +}; /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp new file mode 100644 index 000000000000..4bff715822e8 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2018 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 +#include +#include +#include + +#include + +#include "Matrix.h" +#include "SurfaceTexture.h" + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) + +static const mat4 mtxIdentity; + +SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t tex, + uint32_t texTarget, bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(tex) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::attachedToGL) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +SurfaceTexture::SurfaceTexture(const sp& bq, uint32_t texTarget, + bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(0) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::detached) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); + return NO_INIT; + } + mDefaultWidth = w; + mDefaultHeight = h; + return mConsumer->setDefaultBufferSize(w, h); +} + +status_t SurfaceTexture::updateTexImage() { + ATRACE_CALL(); + SFT_LOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.updateTexImage(*this); +} + +status_t SurfaceTexture::releaseTexImage() { + // releaseTexImage can be invoked even when not attached to a GL context. + ATRACE_CALL(); + SFT_LOGV("releaseTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.releaseTexImage(*this); +} + +status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber) { + status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); + if (err != NO_ERROR) { + return err; + } + + switch (mOpMode) { + case OpMode::attachedToView: + mImageConsumer.onAcquireBufferLocked(item); + break; + case OpMode::attachedToGL: + mEGLConsumer.onAcquireBufferLocked(item, *this); + break; + case OpMode::detached: + break; + } + + return NO_ERROR; +} + +status_t SurfaceTexture::releaseBufferLocked(int buf, sp graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); + // We could be releasing an EGL buffer, even if not currently attached to a GL context. + mImageConsumer.onReleaseBufferLocked(buf); + mEGLConsumer.onReleaseBufferLocked(buf); + return err; +} + +status_t SurfaceTexture::detachFromContext() { + ATRACE_CALL(); + SFT_LOGV("detachFromContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::attachedToGL) { + SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); + return INVALID_OPERATION; + } + + status_t err = mEGLConsumer.detachFromContext(*this); + if (err == OK) { + mOpMode = OpMode::detached; + } + + return err; +} + +status_t SurfaceTexture::attachToContext(uint32_t tex) { + ATRACE_CALL(); + SFT_LOGV("attachToContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("attachToContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::detached) { + SFT_LOGE( + "attachToContext: SurfaceTexture is already attached to a " + "context"); + return INVALID_OPERATION; + } + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible ImageConsumer cache + mImageConsumer.onFreeBufferLocked(mCurrentTexture); + } + + return mEGLConsumer.attachToContext(tex, *this); +} + +void SurfaceTexture::attachToView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + if (mAbandoned) { + SFT_LOGE("attachToView: abandoned SurfaceTexture"); + return; + } + if (mOpMode == OpMode::detached) { + mOpMode = OpMode::attachedToView; + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible EGLConsumer texture cache + mEGLConsumer.onFreeBufferLocked(mCurrentTexture); + mEGLConsumer.onAbandonLocked(); + } + } else { + SFT_LOGE("attachToView: already attached"); + } +} + +void SurfaceTexture::detachFromView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromView: abandoned SurfaceTexture"); + return; + } + + if (mOpMode == OpMode::attachedToView) { + mOpMode = OpMode::detached; + } else { + SFT_LOGE("detachFromView: not attached to View"); + } +} + +uint32_t SurfaceTexture::getCurrentTextureTarget() const { + return mTexTarget; +} + +void SurfaceTexture::getTransformMatrix(float mtx[16]) { + Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void SurfaceTexture::setFilteringEnabled(bool enabled) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); + return; + } + bool needsRecompute = mFilteringEnabled != enabled; + mFilteringEnabled = enabled; + + if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { + SFT_LOGD("setFilteringEnabled called with no current item"); + } + + if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + computeCurrentTransformMatrixLocked(); + } +} + +void SurfaceTexture::computeCurrentTransformMatrixLocked() { + SFT_LOGV("computeCurrentTransformMatrixLocked"); + sp buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) + ? nullptr + : mSlots[mCurrentTexture].mGraphicBuffer; + if (buf == nullptr) { + SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); + } + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, + mFilteringEnabled); +} + +void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp& buf, + const Rect& cropRect, uint32_t transform, + bool filtering) { + // Transform matrices + static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); + static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + + mat4 xform; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + xform *= mtxFlipH; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + xform *= mtxFlipV; + } + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + xform *= mtxRot90; + } + + if (!cropRect.isEmpty() && buf.get()) { + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + float shrinkAmount = 0.0f; + if (filtering) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; + + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; + } + + mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); + xform = crop * xform; + } + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so SurfaceTexture must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + xform = mtxFlipV * xform; + + memcpy(outTransform, xform.asArray(), sizeof(xform)); +} + +Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { + Rect outCrop = crop; + + uint32_t newWidth = static_cast(crop.width()); + uint32_t newHeight = static_cast(crop.height()); + + if (newWidth * bufferHeight > newHeight * bufferWidth) { + newWidth = newHeight * bufferWidth / bufferHeight; + ALOGV("too wide: newWidth = %d", newWidth); + } else if (newWidth * bufferHeight < newHeight * bufferWidth) { + newHeight = newWidth * bufferHeight / bufferWidth; + ALOGV("too tall: newHeight = %d", newHeight); + } + + uint32_t currentWidth = static_cast(crop.width()); + uint32_t currentHeight = static_cast(crop.height()); + + // The crop is too wide + if (newWidth < currentWidth) { + uint32_t dw = currentWidth - newWidth; + auto halfdw = dw / 2; + outCrop.left += halfdw; + // Not halfdw because it would subtract 1 too few when dw is odd + outCrop.right -= (dw - halfdw); + // The crop is too tall + } else if (newHeight < currentHeight) { + uint32_t dh = currentHeight - newHeight; + auto halfdh = dh / 2; + outCrop.top += halfdh; + // Not halfdh because it would subtract 1 too few when dh is odd + outCrop.bottom -= (dh - halfdh); + } + + ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, + outCrop.bottom); + + return outCrop; +} + +nsecs_t SurfaceTexture::getTimestamp() { + SFT_LOGV("getTimestamp"); + Mutex::Autolock lock(mMutex); + return mCurrentTimestamp; +} + +android_dataspace SurfaceTexture::getCurrentDataSpace() { + SFT_LOGV("getCurrentDataSpace"); + Mutex::Autolock lock(mMutex); + return mCurrentDataSpace; +} + +uint64_t SurfaceTexture::getFrameNumber() { + SFT_LOGV("getFrameNumber"); + Mutex::Autolock lock(mMutex); + return mCurrentFrameNumber; +} + +Rect SurfaceTexture::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) + ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) + : mCurrentCrop; +} + +uint32_t SurfaceTexture::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + +uint32_t SurfaceTexture::getCurrentScalingMode() const { + Mutex::Autolock lock(mMutex); + return mCurrentScalingMode; +} + +sp SurfaceTexture::getCurrentFence() const { + Mutex::Autolock lock(mMutex); + return mCurrentFence; +} + +std::shared_ptr SurfaceTexture::getCurrentFenceTime() const { + Mutex::Autolock lock(mMutex); + return mCurrentFenceTime; +} + +void SurfaceTexture::freeBufferLocked(int slotIndex) { + SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + if (slotIndex == mCurrentTexture) { + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + } + // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. + // Buffers can be freed after SurfaceTexture has detached from GL context or View. + mImageConsumer.onFreeBufferLocked(slotIndex); + mEGLConsumer.onFreeBufferLocked(slotIndex); + ConsumerBase::freeBufferLocked(slotIndex); +} + +void SurfaceTexture::abandonLocked() { + SFT_LOGV("abandonLocked"); + mEGLConsumer.onAbandonLocked(); + ConsumerBase::abandonLocked(); +} + +status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { + return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); +} + +void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat( + "%smTexName=%d mCurrentTexture=%d\n" + "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", + prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, + mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); + + ConsumerBase::dumpLocked(result, prefix); +} + +sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, + uirenderer::RenderState& renderState) { + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); + return nullptr; + } + + if (mOpMode != OpMode::attachedToView) { + SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); + return nullptr; + } + + auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); + if (image.get()) { + uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); + dataSpace = mCurrentDataSpace; + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h new file mode 100644 index 000000000000..db392a9f8476 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.h @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include +#include + +#include +#include + +#include +#include + +#include "EGLConsumer.h" +#include "ImageConsumer.h" + +namespace android { + +namespace uirenderer { +class RenderState; +} + +/* + * SurfaceTexture consumes buffers of graphics data from a BufferQueue, + * and makes them available to HWUI render thread as a SkImage and to + * an application GL render thread as an OpenGL texture. + * + * When attached to an application GL render thread, a typical usage + * pattern is to set up the SurfaceTexture with the + * desired options, and call updateTexImage() when a new frame is desired. + * If a new frame is available, the texture will be updated. If not, + * the previous contents are retained. + * + * When attached to a HWUI render thread, the TextureView implementation + * calls dequeueImage, which either pulls a new SkImage or returns the + * last cached SkImage if BufferQueue is empty. + * When attached to HWUI render thread, SurfaceTexture is compatible to + * both Vulkan and GL drawing pipelines. + */ +class ANDROID_API SurfaceTexture : public ConsumerBase { +public: + enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES + typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; + + /** + * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with + * the tex parameter is used, tex indicates the name of the OpenGL ES + * texture to which images are to be streamed. texTarget specifies the + * OpenGL ES texture target to which the texture will be bound in + * updateTexImage. useFenceSync specifies whether fences should be used to + * synchronize access to buffers if that behavior is enabled at + * compile-time. + * + * A SurfaceTexture may be detached from one OpenGL ES context and then + * attached to a different context using the detachFromContext and + * attachToContext methods, respectively. The intention of these methods is + * purely to allow a SurfaceTexture to be transferred from one consumer + * context to another. If such a transfer is not needed there is no + * requirement that either of these methods be called. + * + * If the constructor with the tex parameter is used, the SurfaceTexture is + * created in a state where it is considered attached to an OpenGL ES + * context for the purposes of the attachToContext and detachFromContext + * methods. However, despite being considered "attached" to a context, the + * specific OpenGL ES context doesn't get latched until the first call to + * updateTexImage. After that point, all calls to updateTexImage must be + * made with the same OpenGL ES context current. + * + * If the constructor without the tex parameter is used, the SurfaceTexture is + * created in a detached state, and attachToContext must be called before + * calls to updateTexImage. + */ + SurfaceTexture(const sp& bq, uint32_t tex, uint32_t texureTarget, + bool useFenceSync, bool isControlledByApp); + + SurfaceTexture(const sp& bq, uint32_t texureTarget, bool useFenceSync, + bool isControlledByApp); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(); + + /** + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(); + + /** + * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix + * associated with the texture image set by the most recent call to + * updateTexImage. + * + * This transform matrix maps 2D homogeneous texture coordinates of the form + * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture + * coordinate that should be used to sample that location from the texture. + * Sampling the texture outside of the range of this transform is undefined. + * + * This transform is necessary to compensate for transforms that the stream + * content producer may implicitly apply to the content. By forcing users of + * a SurfaceTexture to apply this transform we avoid performing an extra + * copy of the data that would be needed to hide the transform from the + * user. + * + * The matrix is stored in column-major order so that it may be passed + * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv + * functions. + */ + void getTransformMatrix(float mtx[16]); + + /** + * Computes the transform matrix documented by getTransformMatrix + * from the BufferItem sub parts. + */ + static void computeTransformMatrix(float outTransform[16], const sp& buf, + const Rect& cropRect, uint32_t transform, bool filtering); + + /** + * Scale the crop down horizontally or vertically such that it has the + * same aspect ratio as the buffer does. + */ + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + + /** + * getTimestamp retrieves the timestamp associated with the texture image + * set by the most recent call to updateTexImage. + * + * The timestamp is in nanoseconds, and is monotonically increasing. Its + * other semantics (zero point, etc) are source-dependent and should be + * documented by the source. + */ + int64_t getTimestamp(); + + /** + * getDataSpace retrieves the DataSpace associated with the texture image + * set by the most recent call to updateTexImage. + */ + android_dataspace getCurrentDataSpace(); + + /** + * getFrameNumber retrieves the frame number associated with the texture + * image set by the most recent call to updateTexImage. + * + * The frame number is an incrementing counter set to 0 at the creation of + * the BufferQueue associated with this consumer. + */ + uint64_t getFrameNumber(); + + /** + * setDefaultBufferSize is used to set the size of buffers returned by + * requestBuffers when a with and height of zero is requested. + * A call to setDefaultBufferSize() may trigger requestBuffers() to + * be called from the client. + * The width and height parameters must be no greater than the minimum of + * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + * An error due to invalid dimensions might not be reported until + * updateTexImage() is called. + */ + status_t setDefaultBufferSize(uint32_t width, uint32_t height); + + /** + * setFilteringEnabled sets whether the transform matrix should be computed + * for use with bilinear filtering. + */ + void setFilteringEnabled(bool enabled); + + /** + * getCurrentTextureTarget returns the texture target of the current + * texture as returned by updateTexImage(). + */ + uint32_t getCurrentTextureTarget() const; + + /** + * getCurrentCrop returns the cropping rectangle of the current buffer. + */ + Rect getCurrentCrop() const; + + /** + * getCurrentTransform returns the transform of the current buffer. + */ + uint32_t getCurrentTransform() const; + + /** + * getCurrentScalingMode returns the scaling mode of the current buffer. + */ + uint32_t getCurrentScalingMode() const; + + /** + * getCurrentFence returns the fence indicating when the current buffer is + * ready to be read from. + */ + sp getCurrentFence() const; + + /** + * getCurrentFence returns the FenceTime indicating when the current + * buffer is ready to be read from. + */ + std::shared_ptr getCurrentFenceTime() const; + + /** + * setConsumerUsageBits overrides the ConsumerBase method to OR + * DEFAULT_USAGE_FLAGS to usage. + */ + status_t setConsumerUsageBits(uint64_t usage); + + /** + * detachFromContext detaches the SurfaceTexture from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a SurfaceTexture from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a SurfaceTexture has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the SurfaceTexture is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(); + + /** + * attachToContext attaches a SurfaceTexture that is currently in the + * 'detached' state to the current OpenGL ES context. A SurfaceTexture is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * SurfaceTexture that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex); + + sk_sp dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, uirenderer::RenderState& renderState); + + /** + * attachToView attaches a SurfaceTexture that is currently in the + * 'detached' state to HWUI View system. + */ + void attachToView(); + + /** + * detachFromView detaches a SurfaceTexture from HWUI View system. + */ + void detachFromView(); + +protected: + /** + * abandonLocked overrides the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + virtual void abandonLocked(); + + /** + * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- + * specific info in addition to the ConsumerBase behavior. + */ + virtual void dumpLocked(String8& result, const char* prefix) const override; + + /** + * acquireBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) override; + + /** + * releaseBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) override; + + /** + * freeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + * + * This method must be called with mMutex locked. + */ + virtual void freeBufferLocked(int slotIndex); + + /** + * computeCurrentTransformMatrixLocked computes the transform matrix for the + * current texture. It uses mCurrentTransform and the current GraphicBuffer + * to compute this matrix and stores it in mCurrentTransformMatrix. + * mCurrentTextureImage must not be NULL. + */ + void computeCurrentTransformMatrixLocked(); + + /** + * The default consumer usage flags that SurfaceTexture always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the SurfaceTexture user. In particular, SurfaceTexture will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentCrop is the crop rectangle that applies to the current texture. + * It gets set each time updateTexImage is called. + */ + Rect mCurrentCrop; + + /** + * mCurrentTransform is the transform identifier for the current texture. It + * gets set each time updateTexImage is called. + */ + uint32_t mCurrentTransform; + + /** + * mCurrentScalingMode is the scaling mode for the current texture. It gets + * set each time updateTexImage is called. + */ + uint32_t mCurrentScalingMode; + + /** + * mCurrentFence is the fence received from BufferQueue in updateTexImage. + */ + sp mCurrentFence; + + /** + * The FenceTime wrapper around mCurrentFence. + */ + std::shared_ptr mCurrentFenceTime{FenceTime::NO_FENCE}; + + /** + * mCurrentTransformMatrix is the transform matrix for the current texture. + * It gets computed by computeTransformMatrix each time updateTexImage is + * called. + */ + float mCurrentTransformMatrix[16]; + + /** + * mCurrentTimestamp is the timestamp for the current texture. It + * gets set each time updateTexImage is called. + */ + int64_t mCurrentTimestamp; + + /** + * mCurrentDataSpace is the dataspace for the current texture. It + * gets set each time updateTexImage is called. + */ + android_dataspace mCurrentDataSpace; + + /** + * mCurrentFrameNumber is the frame counter for the current texture. + * It gets set each time updateTexImage is called. + */ + uint64_t mCurrentFrameNumber; + + uint32_t mDefaultWidth, mDefaultHeight; + + /** + * mFilteringEnabled indicates whether the transform matrix is computed for + * use with bilinear filtering. It defaults to true and is changed by + * setFilteringEnabled(). + */ + bool mFilteringEnabled; + + /** + * mTexName is the name of the OpenGL texture to which streamed images will + * be bound when updateTexImage is called. It is set at construction time + * and can be changed with a call to attachToContext. + */ + uint32_t mTexName; + + /** + * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync + * extension should be used to prevent buffers from being dequeued before + * it's safe for them to be written. It gets set at construction time and + * never changes. + */ + const bool mUseFenceSync; + + /** + * mTexTarget is the GL texture target with which the GL texture object is + * associated. It is set in the constructor and never changed. It is + * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android + * Browser. In that case it is set to GL_TEXTURE_2D to allow + * glCopyTexSubImage to read from the texture. This is a hack to work + * around a GL driver limitation on the number of FBO attachments, which the + * browser's tile cache exceeds. + */ + const uint32_t mTexTarget; + + /** + * mCurrentTexture is the buffer slot index of the buffer that is currently + * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + * indicating that no buffer slot is currently bound to the texture. Note, + * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + * that no buffer is bound to the texture. A call to setBufferCount will + * reset mCurrentTexture to INVALID_BUFFER_SLOT. + */ + int mCurrentTexture; + + enum class OpMode { detached, attachedToView, attachedToGL }; + /** + * mOpMode indicates whether the SurfaceTexture is currently attached to + * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, + * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to + * whatever GL context is current at the time of the first updateTexImage call. + * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by + * attachToContext. + * attachToView/detachFromView are used to attach/detach from HWUI view system. + */ + OpMode mOpMode; + + /** + * mEGLConsumer has SurfaceTexture logic used when attached to GL context. + */ + EGLConsumer mEGLConsumer; + + /** + * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. + */ + ImageConsumer mImageConsumer; + + friend class ImageConsumer; + friend class EGLConsumer; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp index 5b361548eeda..d2d37dcb34f2 100644 --- a/libs/hwui/tests/common/LeakChecker.cpp +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -16,7 +16,6 @@ #include "LeakChecker.h" -#include "Caches.h" #include "TestUtils.h" #include @@ -71,9 +70,6 @@ void LeakChecker::checkForLeaks() { // thread-local caches so some leaks will not be properly tagged as leaks UnreachableMemoryInfo rtMemInfo; TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { - if (Caches::hasInstance()) { - Caches::getInstance().tasks.stop(); - } // Check for leaks if (!GetUnreachableMemory(rtMemInfo)) { cerr << "Failed to get unreachable memory!" << endl; diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 69586345319e..66b9b85bdbe7 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -67,16 +67,14 @@ sp TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { sp layerUpdater = createTextureLayerUpdater(renderThread); - layerUpdater->backingLayer()->getTransform().load(transform); + layerUpdater->backingLayer()->getTransform() = transform; layerUpdater->setSize(width, height); layerUpdater->setTransform(&transform); // updateLayer so it's ready to draw - layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - static_cast(layerUpdater->backingLayer()) - ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix identity; + identity.setIdentity(); + layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr); return layerUpdater; } @@ -117,7 +115,6 @@ void TestUtils::TestTask::run() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { renderThread.vulkanManager().destroy(); } else { - renderThread.renderState().flush(Caches::FlushMode::Full); renderThread.destroyGlContext(); } } diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 743f8093bfa8..0e6582c59a36 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -18,7 +18,6 @@ #include #include -#include #include #include #include diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index f29830f0e34b..6c8775b1bdbb 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -15,12 +15,13 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "Properties.h" #include "tests/common/TestUtils.h" #include +#include +#include using namespace android; using namespace android::uirenderer; @@ -31,10 +32,6 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { layerUpdater->setBlend(true); // updates are deferred so the backing layer should still be in its default state - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget()); - } EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight()); EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter()); @@ -42,19 +39,13 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); // push the deferred updates to the layer - Matrix4 scaledMatrix; - scaledMatrix.loadScale(0.5, 0.5, 0.0); - layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5); + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + sk_sp layerImage = SkImage::MakeFromBitmap(bitmap); + layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage); // the backing layer should now have all the properties applied. - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget()); - } EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight()); EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter()); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 9e6d9a8c27de..aecceb3609f5 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -17,12 +17,13 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "Caches.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "Properties.h" #include "tests/common/LeakChecker.h" +#include "thread/TaskProcessor.h" +#include "thread/Task.h" #include "thread/TaskManager.h" #include diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index f8e8a0a18284..ebf2343c5518 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -16,6 +16,7 @@ #ifndef PAINT_UTILS_H #define PAINT_UTILS_H +#include #include #include diff --git a/native/android/Android.bp b/native/android/Android.bp index 4fb5e748aaac..43847cc4ab06 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -64,6 +64,7 @@ cc_library_shared { "libsensor", "libandroid_runtime", "libnetd_client", + "libhwui", ], static_libs: [ diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp index b26688190ccd..ced2792775d4 100644 --- a/native/android/surface_texture.cpp +++ b/native/android/surface_texture.cpp @@ -21,15 +21,16 @@ #include -#include #include #include +#include "surfacetexture/SurfaceTexture.h" + using namespace android; struct ASurfaceTexture { - sp consumer; + sp consumer; sp producer; }; -- cgit v1.2.3-59-g8ed1b From 72903272aee873b02ac73469f4ffb8c727e71e0a Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Thu, 20 Sep 2018 14:56:27 -0400 Subject: Remove unused classes from HWUI. Test: compiles and hwui_unit_tests Change-Id: I42e279a497ea17ad06a857c9914d5aca5df9f6df --- core/jni/android/graphics/NinePatch.cpp | 9 +- libs/hwui/Android.bp | 7 - libs/hwui/CanvasState.cpp | 284 ----------- libs/hwui/CanvasState.h | 195 -------- libs/hwui/ClipArea.cpp | 534 --------------------- libs/hwui/ClipArea.h | 212 -------- libs/hwui/FloatColor.h | 71 --- libs/hwui/ResourceCache.cpp | 145 ------ libs/hwui/ResourceCache.h | 103 ---- libs/hwui/Snapshot.cpp | 209 -------- libs/hwui/Snapshot.h | 279 ----------- libs/hwui/Vertex.h | 1 - libs/hwui/tests/common/TestUtils.h | 9 - .../tests/microbench/DisplayListCanvasBench.cpp | 47 -- libs/hwui/tests/unit/CanvasStateTests.cpp | 160 ------ libs/hwui/tests/unit/ClipAreaTests.cpp | 353 -------------- libs/hwui/tests/unit/RenderNodeDrawableTests.cpp | 1 + libs/hwui/tests/unit/SnapshotTests.cpp | 74 --- 18 files changed, 2 insertions(+), 2691 deletions(-) delete mode 100644 libs/hwui/CanvasState.cpp delete mode 100644 libs/hwui/CanvasState.h delete mode 100644 libs/hwui/ClipArea.cpp delete mode 100644 libs/hwui/ClipArea.h delete mode 100644 libs/hwui/FloatColor.h delete mode 100644 libs/hwui/ResourceCache.cpp delete mode 100644 libs/hwui/ResourceCache.h delete mode 100644 libs/hwui/Snapshot.cpp delete mode 100644 libs/hwui/Snapshot.h delete mode 100644 libs/hwui/tests/unit/CanvasStateTests.cpp delete mode 100644 libs/hwui/tests/unit/ClipAreaTests.cpp delete mode 100644 libs/hwui/tests/unit/SnapshotTests.cpp (limited to 'libs/hwui/ResourceCache.cpp') diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 261910727db9..bb291e74ce0d 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -23,8 +23,6 @@ #include #include -#include - #include "SkCanvas.h" #include "SkLatticeIter.h" #include "SkRegion.h" @@ -83,12 +81,7 @@ public: static void finalize(JNIEnv* env, jobject, jlong patchHandle) { int8_t* patch = reinterpret_cast(patchHandle); - if (android::uirenderer::ResourceCache::hasInstance()) { - Res_png_9patch* p = (Res_png_9patch*) patch; - android::uirenderer::ResourceCache::getInstance().destructor(p); - } else { - delete[] patch; - } + delete[] patch; } static jlong getTransparentRegion(JNIEnv* env, jobject, jobject jbitmap, diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 494e5135d5bb..449e587ae007 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -201,9 +201,7 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", - "CanvasState.cpp", "CanvasTransform.cpp", - "ClipArea.cpp", "DamageAccumulator.cpp", "DeferredLayerUpdater.cpp", "DeviceInfo.cpp", @@ -226,9 +224,7 @@ cc_defaults { "RecordingCanvas.cpp", "RenderNode.cpp", "RenderProperties.cpp", - "ResourceCache.cpp", "SkiaCanvas.cpp", - "Snapshot.cpp", "TreeInfo.cpp", "VectorDrawable.cpp", "protos/graphicsstats.proto", @@ -307,8 +303,6 @@ cc_test { "tests/unit/main.cpp", "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", - "tests/unit/CanvasStateTests.cpp", - "tests/unit/ClipAreaTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", "tests/unit/FatVectorTests.cpp", @@ -327,7 +321,6 @@ cc_test { "tests/unit/SkiaPipelineTests.cpp", "tests/unit/SkiaRenderPropertiesTests.cpp", "tests/unit/SkiaCanvasTests.cpp", - "tests/unit/SnapshotTests.cpp", "tests/unit/StringUtilsTests.cpp", "tests/unit/TestUtilsTests.cpp", "tests/unit/ThreadBaseTests.cpp", diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp deleted file mode 100644 index d18c4abde7f2..000000000000 --- a/libs/hwui/CanvasState.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2014 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 "CanvasState.h" -#include "hwui/Canvas.h" -#include "utils/MathUtils.h" - -namespace android { -namespace uirenderer { - -CanvasState::CanvasState(CanvasStateClient& renderer) - : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {} - -CanvasState::~CanvasState() { - // First call freeSnapshot on all but mFirstSnapshot - // to invoke all the dtors - freeAllSnapshots(); - - // Now actually release the memory - while (mSnapshotPool) { - void* temp = mSnapshotPool; - mSnapshotPool = mSnapshotPool->previous; - free(temp); - } -} - -void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) { - if (mWidth != viewportWidth || mHeight != viewportHeight) { - mWidth = viewportWidth; - mHeight = viewportHeight; - mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); - mCanvas.onViewportInitialized(); - } - - freeAllSnapshots(); - mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); - mSnapshot->setRelativeLightCenter(Vector3()); - mSaveCount = 1; -} - -void CanvasState::initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, - float clipTop, float clipRight, float clipBottom, - const Vector3& lightCenter) { - if (mWidth != viewportWidth || mHeight != viewportHeight) { - mWidth = viewportWidth; - mHeight = viewportHeight; - mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); - mCanvas.onViewportInitialized(); - } - - freeAllSnapshots(); - mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip); - mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); - mSnapshot->fbo = mCanvas.getTargetFbo(); - mSnapshot->setRelativeLightCenter(lightCenter); - mSaveCount = 1; -} - -Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) { - void* memory; - if (mSnapshotPool) { - memory = mSnapshotPool; - mSnapshotPool = mSnapshotPool->previous; - mSnapshotPoolCount--; - } else { - memory = malloc(sizeof(Snapshot)); - } - return new (memory) Snapshot(previous, savecount); -} - -void CanvasState::freeSnapshot(Snapshot* snapshot) { - snapshot->~Snapshot(); - // Arbitrary number, just don't let this grown unbounded - if (mSnapshotPoolCount > 10) { - free((void*)snapshot); - } else { - snapshot->previous = mSnapshotPool; - mSnapshotPool = snapshot; - mSnapshotPoolCount++; - } -} - -void CanvasState::freeAllSnapshots() { - while (mSnapshot != &mFirstSnapshot) { - Snapshot* temp = mSnapshot; - mSnapshot = mSnapshot->previous; - freeSnapshot(temp); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Save (layer) -/////////////////////////////////////////////////////////////////////////////// - -/** - * Guaranteed to save without side-effects - * - * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save - * stack, and ensures restoreToCount() doesn't call back into subclass overrides. - */ -int CanvasState::saveSnapshot(int flags) { - mSnapshot = allocSnapshot(mSnapshot, flags); - return mSaveCount++; -} - -int CanvasState::save(int flags) { - return saveSnapshot(flags); -} - -/** - * Guaranteed to restore without side-effects. - */ -void CanvasState::restoreSnapshot() { - Snapshot* toRemove = mSnapshot; - Snapshot* toRestore = mSnapshot->previous; - - mSaveCount--; - mSnapshot = toRestore; - - // subclass handles restore implementation - mCanvas.onSnapshotRestored(*toRemove, *toRestore); - - freeSnapshot(toRemove); -} - -void CanvasState::restore() { - if (mSaveCount > 1) { - restoreSnapshot(); - } -} - -void CanvasState::restoreToCount(int saveCount) { - if (saveCount < 1) saveCount = 1; - - while (mSaveCount > saveCount) { - restoreSnapshot(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Matrix -/////////////////////////////////////////////////////////////////////////////// - -void CanvasState::getMatrix(SkMatrix* matrix) const { - mSnapshot->transform->copyTo(*matrix); -} - -void CanvasState::translate(float dx, float dy, float dz) { - mSnapshot->transform->translate(dx, dy, dz); -} - -void CanvasState::rotate(float degrees) { - mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); -} - -void CanvasState::scale(float sx, float sy) { - mSnapshot->transform->scale(sx, sy, 1.0f); -} - -void CanvasState::skew(float sx, float sy) { - mSnapshot->transform->skew(sx, sy); -} - -void CanvasState::setMatrix(const SkMatrix& matrix) { - mSnapshot->transform->load(matrix); -} - -void CanvasState::setMatrix(const Matrix4& matrix) { - *(mSnapshot->transform) = matrix; -} - -void CanvasState::concatMatrix(const SkMatrix& matrix) { - mat4 transform(matrix); - mSnapshot->transform->multiply(transform); -} - -void CanvasState::concatMatrix(const Matrix4& matrix) { - mSnapshot->transform->multiply(matrix); -} - -/////////////////////////////////////////////////////////////////////////////// -// Clip -/////////////////////////////////////////////////////////////////////////////// - -bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) { - mSnapshot->clip(Rect(left, top, right, bottom), op); - return !mSnapshot->clipIsEmpty(); -} - -bool CanvasState::clipPath(const SkPath* path, SkClipOp op) { - mSnapshot->clipPath(*path, op); - return !mSnapshot->clipIsEmpty(); -} - -void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { - Rect bounds; - float radius; - if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported - - bool outlineIsRounded = MathUtils::isPositive(radius); - if (!outlineIsRounded || currentTransform()->isSimple()) { - // TODO: consider storing this rect separately, so that this can't be replaced with clip ops - clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect); - } - if (outlineIsRounded) { - setClippingRoundRect(allocator, bounds, radius, false); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Quick Rejection -/////////////////////////////////////////////////////////////////////////////// - -/** - * Calculates whether content drawn within the passed bounds would be outside of, or intersect with - * the clipRect. Does not modify the scissor. - * - * @param clipRequired if not null, will be set to true if element intersects clip - * (and wasn't rejected) - * - * @param snapOut if set, the geometry will be treated as having an AA ramp. - * See Rect::snapGeometryToPixelBoundaries() - */ -bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool* roundRectClipRequired, - bool snapOut) const { - if (bottom <= top || right <= left) { - return true; - } - - Rect r(left, top, right, bottom); - currentTransform()->mapRect(r); - r.snapGeometryToPixelBoundaries(snapOut); - - Rect clipRect(currentRenderTargetClip()); - clipRect.snapToPixelBoundaries(); - - if (!clipRect.intersects(r)) return true; - - // clip is required if geometry intersects clip rect - if (clipRequired) { - *clipRequired = !clipRect.contains(r); - } - - // round rect clip is required if RR clip exists, and geometry intersects its corners - if (roundRectClipRequired) { - *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr && - mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); - } - return false; -} - -bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const { - if (bottom <= top || right <= left) { - return true; - } - - Rect r(left, top, right, bottom); - currentTransform()->mapRect(r); - r.roundOut(); // rounded out to be conservative - - Rect clipRect(currentRenderTargetClip()); - clipRect.snapToPixelBoundaries(); - - if (!clipRect.intersects(r)) return true; - - return false; -} - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h deleted file mode 100644 index 9ac35ff47dab..000000000000 --- a/libs/hwui/CanvasState.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2014 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. - */ - -#pragma once - -#include "Snapshot.h" - -#include -#include -#include -#include - -namespace android { -namespace uirenderer { - -/** - * Abstract base class for any class containing CanvasState. - * Defines three mandatory callbacks. - */ -class CanvasStateClient { -public: - CanvasStateClient() {} - virtual ~CanvasStateClient() {} - - /** - * Callback allowing embedder to take actions in the middle of a - * setViewport() call. - */ - virtual void onViewportInitialized() = 0; - - /** - * Callback allowing embedder to take actions in the middle of a - * restore() call. May be called several times sequentially. - */ - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) = 0; - - /** - * Allows subclasses to control what value is stored in snapshot's - * fbo field in * initializeSaveStack. - */ - virtual GLuint getTargetFbo() const = 0; - -}; // class CanvasStateClient - -/** - * Implements Canvas state methods on behalf of Renderers. - * - * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the - * Renderer interface. Drawing and recording classes that include a CanvasState will have - * different use cases: - * - * Drawing code maintaining canvas state (e.g. FrameBuilder) can query attributes (such as - * transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating - * the stack itself. - * - * Recording code maintaining canvas state (e.g. RecordingCanvas) can both record and pass - * through state operations to CanvasState, so that not only will querying operations work - * (getClip/Matrix), but so that quickRejection can also be used. - */ - -class CanvasState { -public: - explicit CanvasState(CanvasStateClient& renderer); - ~CanvasState(); - - /** - * Initializes the first snapshot, computing the projection matrix, - * and stores the dimensions of the render target. - */ - void initializeRecordingSaveStack(int viewportWidth, int viewportHeight); - - /** - * Initializes the first snapshot, computing the projection matrix, - * and stores the dimensions of the render target. - */ - void initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, float clipTop, - float clipRight, float clipBottom, const Vector3& lightCenter); - - bool hasRectToRectTransform() const { return CC_LIKELY(currentTransform()->rectToRect()); } - - // Save (layer) - int getSaveCount() const { return mSaveCount; } - int save(int flags); - void restore(); - void restoreToCount(int saveCount); - - // Save/Restore without side-effects - int saveSnapshot(int flags); - void restoreSnapshot(); - - // Matrix - void getMatrix(SkMatrix* outMatrix) const; - void translate(float dx, float dy, float dz = 0.0f); - void rotate(float degrees); - void scale(float sx, float sy); - void skew(float sx, float sy); - - void setMatrix(const SkMatrix& matrix); - void setMatrix(const Matrix4& matrix); // internal only convenience method - void concatMatrix(const SkMatrix& matrix); - void concatMatrix(const Matrix4& matrix); // internal only convenience method - - // Clip - const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); } - const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); } - - bool quickRejectConservative(float left, float top, float right, float bottom) const; - - bool clipRect(float left, float top, float right, float bottom, SkClipOp op); - bool clipPath(const SkPath* path, SkClipOp op); - - /** - * Sets a "clipping outline", which is independent from the regular clip. - * Currently only supports rectangles or rounded rectangles; passing in a - * more complicated outline fails silently. Replaces any previous clipping - * outline. - */ - void setClippingOutline(LinearAllocator& allocator, const Outline* outline); - void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius, - bool highPriority = true) { - mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority); - } - void setProjectionPathMask(const SkPath* path) { mSnapshot->setProjectionPathMask(path); } - - /** - * Returns true if drawing in the rectangle (left, top, right, bottom) - * will be clipped out. Is conservative: might return false when subpixel- - * perfect tests would return true. - */ - bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool* roundRectClipRequired, - bool snapOut) const; - - void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; } - - inline const mat4* currentTransform() const { return currentSnapshot()->transform; } - inline const Rect& currentRenderTargetClip() const { - return currentSnapshot()->getRenderTargetClip(); - } - inline int currentFlags() const { return currentSnapshot()->flags; } - const Vector3& currentLightCenter() const { - return currentSnapshot()->getRelativeLightCenter(); - } - int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); } - int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); } - int getWidth() const { return mWidth; } - int getHeight() const { return mHeight; } - bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); } - - inline const Snapshot* currentSnapshot() const { return mSnapshot; } - inline Snapshot* writableSnapshot() { return mSnapshot; } - inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; } - -private: - Snapshot* allocSnapshot(Snapshot* previous, int savecount); - void freeSnapshot(Snapshot* snapshot); - void freeAllSnapshots(); - - /// Dimensions of the drawing surface - int mWidth, mHeight; - - /// Number of saved states - int mSaveCount; - - /// Base state - Snapshot mFirstSnapshot; - - /// Host providing callbacks - CanvasStateClient& mCanvas; - - /// Current state - Snapshot* mSnapshot; - - // Pool of allocated snapshots to re-use - // NOTE: The dtors have already been invoked! - Snapshot* mSnapshotPool = nullptr; - int mSnapshotPoolCount = 0; - -}; // class CanvasState - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp deleted file mode 100644 index 27d93cfa0391..000000000000 --- a/libs/hwui/ClipArea.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright (C) 2015 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 "ClipArea.h" - -#include "utils/LinearAllocator.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) { - Vertex v = {x, y}; - transform.mapPoint(v.x, v.y); - transformedBounds.expandToCover(v.x, v.y); -} - -Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) { - const float kMinFloat = std::numeric_limits::lowest(); - const float kMaxFloat = std::numeric_limits::max(); - Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat}; - handlePoint(transformedBounds, transform, r.left, r.top); - handlePoint(transformedBounds, transform, r.right, r.top); - handlePoint(transformedBounds, transform, r.left, r.bottom); - handlePoint(transformedBounds, transform, r.right, r.bottom); - return transformedBounds; -} - -void ClipBase::dump() const { - ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect)); -} - -/* - * TransformedRectangle - */ - -TransformedRectangle::TransformedRectangle() {} - -TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform) - : mBounds(bounds), mTransform(transform) {} - -bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const { - return mTransform == other.mTransform; -} - -void TransformedRectangle::intersectWith(const TransformedRectangle& other) { - mBounds.doIntersect(other.mBounds); -} - -bool TransformedRectangle::isEmpty() const { - return mBounds.isEmpty(); -} - -/* - * RectangleList - */ - -RectangleList::RectangleList() : mTransformedRectanglesCount(0) {} - -bool RectangleList::isEmpty() const { - if (mTransformedRectanglesCount < 1) { - return true; - } - - for (int i = 0; i < mTransformedRectanglesCount; i++) { - if (mTransformedRectangles[i].isEmpty()) { - return true; - } - } - return false; -} - -int RectangleList::getTransformedRectanglesCount() const { - return mTransformedRectanglesCount; -} - -const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const { - return mTransformedRectangles[i]; -} - -void RectangleList::setEmpty() { - mTransformedRectanglesCount = 0; -} - -void RectangleList::set(const Rect& bounds, const Matrix4& transform) { - mTransformedRectanglesCount = 1; - mTransformedRectangles[0] = TransformedRectangle(bounds, transform); -} - -bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) { - TransformedRectangle newRectangle(bounds, transform); - - // Try to find a rectangle with a compatible transformation - int index = 0; - for (; index < mTransformedRectanglesCount; index++) { - TransformedRectangle& tr(mTransformedRectangles[index]); - if (tr.canSimplyIntersectWith(newRectangle)) { - tr.intersectWith(newRectangle); - return true; - } - } - - // Add it to the list if there is room - if (index < kMaxTransformedRectangles) { - mTransformedRectangles[index] = newRectangle; - mTransformedRectanglesCount += 1; - return true; - } - - // This rectangle list is full - return false; -} - -Rect RectangleList::calculateBounds() const { - Rect bounds; - for (int index = 0; index < mTransformedRectanglesCount; index++) { - const TransformedRectangle& tr(mTransformedRectangles[index]); - if (index == 0) { - bounds = tr.transformedBounds(); - } else { - bounds.doIntersect(tr.transformedBounds()); - } - } - return bounds; -} - -static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) { - SkPath rectPath; - SkPath rectPathTransformed; - rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom); - SkMatrix skTransform; - transform.copyTo(skTransform); - rectPath.transform(skTransform, &rectPathTransformed); - return rectPathTransformed; -} - -SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { - SkRegion rectangleListAsRegion; - for (int index = 0; index < mTransformedRectanglesCount; index++) { - const TransformedRectangle& tr(mTransformedRectangles[index]); - SkPath rectPathTransformed = - pathFromTransformedRectangle(tr.getBounds(), tr.getTransform()); - if (index == 0) { - rectangleListAsRegion.setPath(rectPathTransformed, clip); - } else { - SkRegion rectRegion; - rectRegion.setPath(rectPathTransformed, clip); - rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op); - } - } - return rectangleListAsRegion; -} - -void RectangleList::transform(const Matrix4& transform) { - for (int index = 0; index < mTransformedRectanglesCount; index++) { - mTransformedRectangles[index].transform(transform); - } -} - -/* - * ClipArea - */ - -ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {} - -/* - * Interface - */ - -void ClipArea::setViewportDimensions(int width, int height) { - mPostViewportClipObserved = false; - mViewportBounds.set(0, 0, width, height); - mClipRect = mViewportBounds; -} - -void ClipArea::setEmpty() { - onClipUpdated(); - mMode = ClipMode::Rectangle; - mClipRect.setEmpty(); - mClipRegion.setEmpty(); - mRectangleList.setEmpty(); -} - -void ClipArea::setClip(float left, float top, float right, float bottom) { - onClipUpdated(); - mMode = ClipMode::Rectangle; - mClipRect.set(left, top, right, bottom); - mClipRegion.setEmpty(); -} - -void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - switch (mMode) { - case ClipMode::Rectangle: - rectangleModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::RectangleList: - rectangleListModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::Region: - regionModeClipRectWithTransform(r, transform, op); - break; - } -} - -void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - enterRegionMode(); - mClipRegion.op(region, op); - onClipRegionUpdated(); -} - -void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - SkMatrix skTransform; - transform->copyTo(skTransform); - SkPath transformed; - path.transform(skTransform, &transformed); - SkRegion region; - regionFromPath(transformed, region); - enterRegionMode(); - mClipRegion.op(region, op); - onClipRegionUpdated(); -} - -/* - * Rectangle mode - */ - -void ClipArea::enterRectangleMode() { - // Entering rectangle mode discards any - // existing clipping information from the other modes. - // The only way this occurs is by a clip setting operation. - mMode = ClipMode::Rectangle; -} - -void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - if (op == SkRegion::kReplace_Op && transform->rectToRect()) { - mClipRect = r; - transform->mapRect(mClipRect); - return; - } else if (op != SkRegion::kIntersect_Op) { - enterRegionMode(); - regionModeClipRectWithTransform(r, transform, op); - return; - } - - if (transform->rectToRect()) { - Rect transformed(r); - transform->mapRect(transformed); - mClipRect.doIntersect(transformed); - return; - } - - enterRectangleListMode(); - rectangleListModeClipRectWithTransform(r, transform, op); -} - -/* - * RectangleList mode implementation - */ - -void ClipArea::enterRectangleListMode() { - // Is is only legal to enter rectangle list mode from - // rectangle mode, since rectangle list mode cannot represent - // all clip areas that can be represented by a region. - ALOG_ASSERT(mMode == ClipMode::Rectangle); - mMode = ClipMode::RectangleList; - mRectangleList.set(mClipRect, Matrix4::identity()); -} - -void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) { - enterRegionMode(); - regionModeClipRectWithTransform(r, transform, op); - } -} - -/* - * Region mode implementation - */ - -void ClipArea::enterRegionMode() { - ClipMode oldMode = mMode; - mMode = ClipMode::Region; - if (oldMode != ClipMode::Region) { - if (oldMode == ClipMode::Rectangle) { - mClipRegion.setRect(mClipRect.toSkIRect()); - } else { - mClipRegion = mRectangleList.convertToRegion(createViewportRegion()); - onClipRegionUpdated(); - } - } -} - -void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - SkPath transformedRect = pathFromTransformedRectangle(r, *transform); - SkRegion transformedRectRegion; - regionFromPath(transformedRect, transformedRectRegion); - mClipRegion.op(transformedRectRegion, op); - onClipRegionUpdated(); -} - -void ClipArea::onClipRegionUpdated() { - if (!mClipRegion.isEmpty()) { - mClipRect.set(mClipRegion.getBounds()); - - if (mClipRegion.isRect()) { - mClipRegion.setEmpty(); - enterRectangleMode(); - } - } else { - mClipRect.setEmpty(); - } -} - -/** - * Clip serialization - */ - -const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) { - if (!mPostViewportClipObserved) { - // Only initial clip-to-viewport observed, so no serialization of clip necessary - return nullptr; - } - - static_assert(std::is_trivially_destructible::value, - "expect Rect to be trivially destructible"); - static_assert(std::is_trivially_destructible::value, - "expect RectangleList to be trivially destructible"); - - if (mLastSerialization == nullptr) { - ClipBase* serialization = nullptr; - switch (mMode) { - case ClipMode::Rectangle: - serialization = allocator.create(mClipRect); - break; - case ClipMode::RectangleList: - serialization = allocator.create(mRectangleList); - serialization->rect = mRectangleList.calculateBounds(); - break; - case ClipMode::Region: - serialization = allocator.create(mClipRegion); - serialization->rect.set(mClipRegion.getBounds()); - break; - } - serialization->intersectWithRoot = mReplaceOpObserved; - // TODO: this is only done for draw time, should eventually avoid for record time - serialization->rect.snapToPixelBoundaries(); - mLastSerialization = serialization; - } - return mLastSerialization; -} - -inline static const RectangleList& getRectList(const ClipBase* scb) { - return reinterpret_cast(scb)->rectList; -} - -inline static const SkRegion& getRegion(const ClipBase* scb) { - return reinterpret_cast(scb)->region; -} - -// Conservative check for too many rectangles to fit in rectangle list. -// For simplicity, doesn't account for rect merging -static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) { - int currentRectCount = clipArea.isRectangleList() - ? clipArea.getRectangleList().getTransformedRectanglesCount() - : 1; - int recordedRectCount = (scb->mode == ClipMode::RectangleList) - ? getRectList(scb).getTransformedRectanglesCount() - : 1; - return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles; -} - -static const ClipRect sEmptyClipRect(Rect(0, 0)); - -const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, - const Matrix4& recordedClipTransform) { - // if no recordedClip passed, just serialize current state - if (!recordedClip) return serializeClip(allocator); - - // if either is empty, clip is empty - if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect; - - if (!mLastResolutionResult || recordedClip != mLastResolutionClip || - recordedClipTransform != mLastResolutionTransform) { - mLastResolutionClip = recordedClip; - mLastResolutionTransform = recordedClipTransform; - - if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle && - recordedClipTransform.rectToRect())) { - // common case - result is a single rectangle - auto rectClip = allocator.create(recordedClip->rect); - recordedClipTransform.mapRect(rectClip->rect); - rectClip->rect.doIntersect(mClipRect); - rectClip->rect.snapToPixelBoundaries(); - mLastResolutionResult = rectClip; - } else if (CC_UNLIKELY(mMode == ClipMode::Region || - recordedClip->mode == ClipMode::Region || - cannotFitInRectangleList(*this, recordedClip))) { - // region case - SkRegion other; - switch (recordedClip->mode) { - case ClipMode::Rectangle: - if (CC_LIKELY(recordedClipTransform.rectToRect())) { - // simple transform, skip creating SkPath - Rect resultClip(recordedClip->rect); - recordedClipTransform.mapRect(resultClip); - other.setRect(resultClip.toSkIRect()); - } else { - SkPath transformedRect = pathFromTransformedRectangle( - recordedClip->rect, recordedClipTransform); - other.setPath(transformedRect, createViewportRegion()); - } - break; - case ClipMode::RectangleList: { - RectangleList transformedList(getRectList(recordedClip)); - transformedList.transform(recordedClipTransform); - other = transformedList.convertToRegion(createViewportRegion()); - break; - } - case ClipMode::Region: - other = getRegion(recordedClip); - applyTransformToRegion(recordedClipTransform, &other); - } - - ClipRegion* regionClip = allocator.create(); - switch (mMode) { - case ClipMode::Rectangle: - regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op); - break; - case ClipMode::RectangleList: - regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()), - other, SkRegion::kIntersect_Op); - break; - case ClipMode::Region: - regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op); - break; - } - // Don't need to snap, since region's in int bounds - regionClip->rect.set(regionClip->region.getBounds()); - mLastResolutionResult = regionClip; - } else { - auto rectListClip = allocator.create(mRectangleList); - auto&& rectList = rectListClip->rectList; - if (mMode == ClipMode::Rectangle) { - rectList.set(mClipRect, Matrix4::identity()); - } - - if (recordedClip->mode == ClipMode::Rectangle) { - rectList.intersectWith(recordedClip->rect, recordedClipTransform); - } else { - const RectangleList& other = getRectList(recordedClip); - for (int i = 0; i < other.getTransformedRectanglesCount(); i++) { - auto&& tr = other.getTransformedRectangle(i); - Matrix4 totalTransform(recordedClipTransform); - totalTransform.multiply(tr.getTransform()); - rectList.intersectWith(tr.getBounds(), totalTransform); - } - } - rectListClip->rect = rectList.calculateBounds(); - rectListClip->rect.snapToPixelBoundaries(); - mLastResolutionResult = rectListClip; - } - } - return mLastResolutionResult; -} - -void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) { - if (!clip) return; // nothing to do - - if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) { - clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op); - } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) { - auto&& rectList = getRectList(clip); - for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) { - auto&& tr = rectList.getTransformedRectangle(i); - Matrix4 totalTransform(transform); - totalTransform.multiply(tr.getTransform()); - clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op); - } - } else { - SkRegion region(getRegion(clip)); - applyTransformToRegion(transform, ®ion); - clipRegion(region, SkRegion::kIntersect_Op); - } -} - -void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) { - if (transform.rectToRect() && !transform.isPureTranslate()) { - // handle matrices with scale manually by mapping each rect - SkRegion other; - SkRegion::Iterator it(*region); - while (!it.done()) { - Rect rect(it.rect()); - transform.mapRect(rect); - rect.snapGeometryToPixelBoundaries(true); - other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op); - it.next(); - } - region->swap(other); - } else { - // TODO: handle non-translate transforms properly! - region->translate(transform.getTranslateX(), transform.getTranslateY()); - } -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h deleted file mode 100644 index a7a11801cfe2..000000000000 --- a/libs/hwui/ClipArea.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2015 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 CLIPAREA_H -#define CLIPAREA_H - -#include "Matrix.h" -#include "Rect.h" -#include "utils/Pair.h" - -#include - -namespace android { -namespace uirenderer { - -class LinearAllocator; - -Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform); - -class TransformedRectangle { -public: - TransformedRectangle(); - TransformedRectangle(const Rect& bounds, const Matrix4& transform); - - bool canSimplyIntersectWith(const TransformedRectangle& other) const; - void intersectWith(const TransformedRectangle& other); - - bool isEmpty() const; - - const Rect& getBounds() const { return mBounds; } - - Rect transformedBounds() const { - Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform)); - return transformedBounds; - } - - const Matrix4& getTransform() const { return mTransform; } - - void transform(const Matrix4& transform) { - Matrix4 t; - t.loadMultiply(transform, mTransform); - mTransform = t; - } - -private: - Rect mBounds; - Matrix4 mTransform; -}; - -class RectangleList { -public: - RectangleList(); - - bool isEmpty() const; - int getTransformedRectanglesCount() const; - const TransformedRectangle& getTransformedRectangle(int i) const; - - void setEmpty(); - void set(const Rect& bounds, const Matrix4& transform); - bool intersectWith(const Rect& bounds, const Matrix4& transform); - void transform(const Matrix4& transform); - - SkRegion convertToRegion(const SkRegion& clip) const; - Rect calculateBounds() const; - - enum { kMaxTransformedRectangles = 5 }; - -private: - int mTransformedRectanglesCount; - TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles]; -}; - -enum class ClipMode { - Rectangle, - RectangleList, - - // region and path - intersected. if either is empty, don't use - Region -}; - -struct ClipBase { - explicit ClipBase(ClipMode mode) : mode(mode) {} - explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {} - const ClipMode mode; - bool intersectWithRoot = false; - // Bounds of the clipping area, used to define the scissor, and define which - // portion of the stencil is updated/used - Rect rect; - - void dump() const; -}; - -struct ClipRect : ClipBase { - explicit ClipRect(const Rect& rect) : ClipBase(rect) {} -}; - -struct ClipRectList : ClipBase { - explicit ClipRectList(const RectangleList& rectList) - : ClipBase(ClipMode::RectangleList), rectList(rectList) {} - RectangleList rectList; -}; - -struct ClipRegion : ClipBase { - explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {} - ClipRegion() : ClipBase(ClipMode::Region) {} - SkRegion region; -}; - -class ClipArea { -public: - ClipArea(); - - void setViewportDimensions(int width, int height); - - bool isEmpty() const { return mClipRect.isEmpty(); } - - void setEmpty(); - void setClip(float left, float top, float right, float bottom); - void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op); - - const Rect& getClipRect() const { return mClipRect; } - - const SkRegion& getClipRegion() const { return mClipRegion; } - - const RectangleList& getRectangleList() const { return mRectangleList; } - - bool isRegion() const { return ClipMode::Region == mMode; } - - bool isSimple() const { return mMode == ClipMode::Rectangle; } - - bool isRectangleList() const { return mMode == ClipMode::RectangleList; } - - WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator); - WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip( - LinearAllocator& allocator, const ClipBase* recordedClip, - const Matrix4& recordedClipTransform); - void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform); - - static void applyTransformToRegion(const Matrix4& transform, SkRegion* region); - -private: - void enterRectangleMode(); - void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - - void enterRectangleListMode(); - void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op); - - void enterRegionModeFromRectangleMode(); - void enterRegionModeFromRectangleListMode(); - void enterRegionMode(); - void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - - void clipRegion(const SkRegion& region, SkRegion::Op op); - void ensureClipRegion(); - void onClipRegionUpdated(); - - // Called by every state modifying public method. - void onClipUpdated() { - mPostViewportClipObserved = true; - mLastSerialization = nullptr; - mLastResolutionResult = nullptr; - } - - SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); } - - void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) { - // TODO: this should not mask every path to the viewport - this makes it impossible to use - // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op) - pathAsRegion.setPath(path, createViewportRegion()); - } - - ClipMode mMode; - bool mPostViewportClipObserved = false; - bool mReplaceOpObserved = false; - - /** - * If mLastSerialization is non-null, it represents an already serialized copy - * of the current clip state. If null, it has not been computed. - */ - const ClipBase* mLastSerialization = nullptr; - - /** - * This pair of pointers is a single entry cache of most recently seen - */ - const ClipBase* mLastResolutionResult = nullptr; - const ClipBase* mLastResolutionClip = nullptr; - Matrix4 mLastResolutionTransform; - - Rect mViewportBounds; - Rect mClipRect; - SkRegion mClipRegion; - RectangleList mRectangleList; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif /* CLIPAREA_H_ */ diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h deleted file mode 100644 index b424f97a5004..000000000000 --- a/libs/hwui/FloatColor.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2015 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 FLOATCOLOR_H -#define FLOATCOLOR_H - -#include "utils/Color.h" -#include "utils/Macros.h" -#include "utils/MathUtils.h" - -#include - -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 - // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied - // gamma-encoded sRGB color - void set(uint32_t color) { - a = ((color >> 24) & 0xff) / 255.0f; - r = a * EOCF(((color >> 16) & 0xff) / 255.0f); - g = a * EOCF(((color >> 8) & 0xff) / 255.0f); - b = a * EOCF(((color)&0xff) / 255.0f); - } - - // "color" is a gamma-encoded sRGB color - // After calling this method, the color is stored as a un-premultiplied linear color - // if linear blending is enabled. Otherwise, the color is stored as a un-premultiplied - // gamma-encoded sRGB color - void setUnPreMultiplied(uint32_t color) { - a = ((color >> 24) & 0xff) / 255.0f; - r = EOCF(((color >> 16) & 0xff) / 255.0f); - g = EOCF(((color >> 8) & 0xff) / 255.0f); - b = EOCF(((color)&0xff) / 255.0f); - } - - bool isNotBlack() { return a < 1.0f || r > 0.0f || g > 0.0f || b > 0.0f; } - - bool operator==(const FloatColor& other) const { - return MathUtils::areEqual(r, other.r) && MathUtils::areEqual(g, other.g) && - MathUtils::areEqual(b, other.b) && MathUtils::areEqual(a, other.a); - } - - bool operator!=(const FloatColor& other) const { return !(*this == other); } - - float r; - float g; - float b; - float a; -}; - -REQUIRE_COMPATIBLE_LAYOUT(FloatColor); - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif /* FLOATCOLOR_H */ diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp deleted file mode 100644 index 65bee476f14d..000000000000 --- a/libs/hwui/ResourceCache.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2010 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 "ResourceCache.h" - -namespace android { - -using namespace uirenderer; -ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache); - -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Resource cache -/////////////////////////////////////////////////////////////////////////////// - -void ResourceCache::logCache() { - ALOGD("ResourceCache: cacheReport:"); - for (size_t i = 0; i < mCache->size(); ++i) { - ResourceReference* ref = mCache->valueAt(i); - ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", i, mCache->keyAt(i), - mCache->valueAt(i)); - ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", i, - ref->refCount, ref->destroyed, ref->resourceType); - } -} - -ResourceCache::ResourceCache() { - Mutex::Autolock _l(mLock); - mCache = new KeyedVector(); -} - -ResourceCache::~ResourceCache() { - Mutex::Autolock _l(mLock); - delete mCache; -} - -void ResourceCache::lock() { - mLock.lock(); -} - -void ResourceCache::unlock() { - mLock.unlock(); -} - -void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) { - Mutex::Autolock _l(mLock); - incrementRefcountLocked(resource, resourceType); -} - -void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { - incrementRefcount((void*)patchResource, kNinePatch); -} - -void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; - if (ref == nullptr || mCache->size() == 0) { - ref = new ResourceReference(resourceType); - mCache->add(resource, ref); - } - ref->refCount++; -} - -void ResourceCache::decrementRefcount(void* resource) { - Mutex::Autolock _l(mLock); - decrementRefcountLocked(resource); -} - -void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { - decrementRefcount((void*)patchResource); -} - -void ResourceCache::decrementRefcountLocked(void* resource) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; - if (ref == nullptr) { - // Should not get here - shouldn't get a call to decrement if we're not yet tracking it - return; - } - ref->refCount--; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - } -} - -void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) { - decrementRefcountLocked((void*)patchResource); -} - -void ResourceCache::destructor(Res_png_9patch* resource) { - Mutex::Autolock _l(mLock); - destructorLocked(resource); -} - -void ResourceCache::destructorLocked(Res_png_9patch* resource) { - ssize_t index = mCache->indexOfKey(resource); - ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; - if (ref == nullptr) { - // If we're not tracking this resource, just delete it - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; - return; - } - ref->destroyed = true; - if (ref->refCount == 0) { - deleteResourceReferenceLocked(resource, ref); - } -} - -/** - * This method should only be called while the mLock mutex is held (that mutex is grabbed - * by the various destructor() and recycle() methods which call this method). - */ -void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) { - if (ref->destroyed) { - switch (ref->resourceType) { - case kNinePatch: { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; - } break; - } - } - mCache->removeItem(resource); - delete ref; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h deleted file mode 100644 index fd3f9fd05d58..000000000000 --- a/libs/hwui/ResourceCache.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 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_RESOURCE_CACHE_H -#define ANDROID_HWUI_RESOURCE_CACHE_H - -#include - -#include -#include - -#include -#include - -#include - -namespace android { -namespace uirenderer { - -class Layer; - -/** - * Type of Resource being cached - */ -enum ResourceType { - kNinePatch, -}; - -class ResourceReference { -public: - explicit ResourceReference(ResourceType type) { - refCount = 0; - destroyed = false; - resourceType = type; - } - - int refCount; - bool destroyed; - ResourceType resourceType; -}; - -class ANDROID_API ResourceCache : public Singleton { - ResourceCache(); - ~ResourceCache(); - - friend class Singleton; - -public: - /** - * When using these two methods, make sure to only invoke the *Locked() - * variants of increment/decrementRefcount(), recyle() and destructor() - */ - void lock(); - void unlock(); - - void incrementRefcount(const Res_png_9patch* resource); - - void decrementRefcount(const Res_png_9patch* resource); - - void decrementRefcountLocked(const Res_png_9patch* resource); - - void destructor(Res_png_9patch* resource); - - void destructorLocked(Res_png_9patch* resource); - -private: - void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref); - - void incrementRefcount(void* resource, ResourceType resourceType); - void incrementRefcountLocked(void* resource, ResourceType resourceType); - - void decrementRefcount(void* resource); - void decrementRefcountLocked(void* resource); - - void logCache(); - - /** - * Used to increment, decrement, and destroy. Incrementing is generally accessed on the UI - * thread, but destroying resources may be called from the GC thread, the finalizer thread, - * or a reference queue finalization thread. - */ - mutable Mutex mLock; - - KeyedVector* mCache; -}; - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_RESOURCE_CACHE_H diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp deleted file mode 100644 index f1a1bef7c94e..000000000000 --- a/libs/hwui/Snapshot.cpp +++ /dev/null @@ -1,209 +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 "Snapshot.h" - -#include "hwui/Canvas.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Constructors -/////////////////////////////////////////////////////////////////////////////// - -Snapshot::Snapshot() - : flags(0) - , previous(nullptr) - , layer(nullptr) - , fbo(0) - , alpha(1.0f) - , roundRectClipState(nullptr) - , projectionPathMask(nullptr) - , mClipArea(&mClipAreaRoot) { - transform = &mTransformRoot; - mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0; -} - -/** - * Copies the specified snapshot/ The specified snapshot is stored as - * the previous snapshot. - */ -Snapshot::Snapshot(Snapshot* s, int saveFlags) - : flags(0) - , previous(s) - , layer(s->layer) - , fbo(s->fbo) - , alpha(s->alpha) - , roundRectClipState(s->roundRectClipState) - , projectionPathMask(s->projectionPathMask) - , mClipArea(nullptr) - , mViewportData(s->mViewportData) - , mRelativeLightCenter(s->mRelativeLightCenter) { - if (saveFlags & SaveFlags::Matrix) { - mTransformRoot = *s->transform; - transform = &mTransformRoot; - } else { - transform = s->transform; - } - - if (saveFlags & SaveFlags::Clip) { - mClipAreaRoot = s->getClipArea(); - mClipArea = &mClipAreaRoot; - } else { - mClipArea = s->mClipArea; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Clipping -/////////////////////////////////////////////////////////////////////////////// - -void Snapshot::clip(const Rect& localClip, SkClipOp op) { - flags |= Snapshot::kFlagClipSet; - mClipArea->clipRectWithTransform(localClip, transform, static_cast(op)); -} - -void Snapshot::clipPath(const SkPath& path, SkClipOp op) { - flags |= Snapshot::kFlagClipSet; - mClipArea->clipPathWithTransform(path, transform, static_cast(op)); -} - -void Snapshot::setClip(float left, float top, float right, float bottom) { - flags |= Snapshot::kFlagClipSet; - mClipArea->setClip(left, top, right, bottom); -} - -bool Snapshot::hasPerspectiveTransform() const { - return transform->isPerspective(); -} - -const Rect& Snapshot::getLocalClip() { - mat4 inverse; - inverse.loadInverse(*transform); - - mLocalClip.set(mClipArea->getClipRect()); - inverse.mapRect(mLocalClip); - - return mLocalClip; -} - -void Snapshot::resetClip(float left, float top, float right, float bottom) { - // TODO: This is incorrect, when we start rendering into a new layer, - // we may have to modify the previous snapshot's clip rect and clip - // region if the previous restore() call did not restore the clip - mClipArea = &mClipAreaRoot; - setClip(left, top, right, bottom); -} - -/////////////////////////////////////////////////////////////////////////////// -// Clipping round rect -/////////////////////////////////////////////////////////////////////////////// - -void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, - bool highPriority) { - if (bounds.isEmpty()) { - mClipArea->setEmpty(); - return; - } - - if (roundRectClipState && roundRectClipState->highPriority) { - // ignore, don't replace, already have a high priority clip - return; - } - - RoundRectClipState* state = new (allocator) RoundRectClipState; - - state->highPriority = highPriority; - - // store the inverse drawing matrix - Matrix4 roundRectDrawingMatrix = getOrthoMatrix(); - roundRectDrawingMatrix.multiply(*transform); - state->matrix.loadInverse(roundRectDrawingMatrix); - - // compute area under rounded corners - only draws overlapping these rects need to be clipped - for (int i = 0; i < 4; i++) { - state->dangerRects[i] = bounds; - } - state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius; - state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius; - state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius; - state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius; - for (int i = 0; i < 4; i++) { - transform->mapRect(state->dangerRects[i]); - - // round danger rects out as though they are AA geometry (since they essentially are) - state->dangerRects[i].snapGeometryToPixelBoundaries(true); - } - - // store RR area - state->innerRect = bounds; - state->innerRect.inset(radius); - state->radius = radius; - - // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info - roundRectClipState = state; -} - -void Snapshot::setProjectionPathMask(const SkPath* path) { - projectionPathMask = path; -} - -static Snapshot* getClipRoot(Snapshot* target) { - while (target->previous && target->previous->previous) { - target = target->previous; - } - return target; -} - -const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, - const Matrix4& recordedClipTransform) { - auto target = this; - if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { - // Clip must be intersected with root, instead of current clip. - target = getClipRoot(this); - } - - return target->mClipArea->serializeIntersectedClip(allocator, recordedClip, - recordedClipTransform); -} - -void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) { - if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) { - // current clip is being replaced, but must intersect with clip root - *mClipArea = *(getClipRoot(this)->mClipArea); - } - mClipArea->applyClip(recordedClip, transform); -} - -/////////////////////////////////////////////////////////////////////////////// -// Queries -/////////////////////////////////////////////////////////////////////////////// - -void Snapshot::dump() const { - ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", this, flags, previous, - getViewportHeight(), !mClipArea->isSimple()); - const Rect& clipRect(mClipArea->getClipRect()); - ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top, - clipRect.right, clipRect.bottom, mClipArea->isSimple()); - - ALOGD(" Transform (at %p):", transform); - transform->dump(); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h deleted file mode 100644 index 655f819ca41b..000000000000 --- a/libs/hwui/Snapshot.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#pragma once - -#include -#include - -#include -#include -#include - -#include -#include - -#include "ClipArea.h" -#include "Layer.h" -#include "Matrix.h" -#include "Outline.h" -#include "Rect.h" -#include "utils/Macros.h" - -namespace android { -namespace uirenderer { - -/** - * Temporary structure holding information for a single outline clip. - * - * These structures are treated as immutable once created, and only exist for a single frame, which - * is why they may only be allocated with a LinearAllocator. - */ -class RoundRectClipState { -public: - static void* operator new(size_t size) = delete; - static void* operator new(size_t size, LinearAllocator& allocator) { - return allocator.alloc(size); - } - - bool areaRequiresRoundRectClip(const Rect& rect) const { - return rect.intersects(dangerRects[0]) || rect.intersects(dangerRects[1]) || - rect.intersects(dangerRects[2]) || rect.intersects(dangerRects[3]); - } - - bool highPriority; - Matrix4 matrix; - Rect dangerRects[4]; - Rect innerRect; - float radius; -}; - -/** - * A snapshot holds information about the current state of the rendering - * surface. A snapshot is usually created whenever the user calls save() - * and discarded when the user calls restore(). Once a snapshot is created, - * it can hold information for deferred rendering. - * - * Each snapshot has a link to a previous snapshot, indicating the previous - * state of the renderer. - */ -class Snapshot { -public: - Snapshot(); - Snapshot(Snapshot* s, int saveFlags); - - /** - * Various flags set on ::flags. - */ - enum Flags { - /** - * Indicates that the clip region was modified. When this - * snapshot is restored so must the clip. - */ - kFlagClipSet = 0x1, - /** - * Indicates that this snapshot was created when saving - * a new layer. - */ - kFlagIsLayer = 0x2, - /** - * Indicates that this snapshot is a special type of layer - * backed by an FBO. This flag only makes sense when the - * flag kFlagIsLayer is also set. - * - * Viewport has been modified to fit the new Fbo, and must be - * restored when this snapshot is restored. - */ - kFlagIsFboLayer = 0x4, - }; - - /** - * Modifies the current clip with the new clip rectangle and - * the specified operation. The specified rectangle is transformed - * by this snapshot's trasnformation. - */ - void clip(const Rect& localClip, SkClipOp op); - - /** - * Modifies the current clip with the new clip rectangle and - * the specified operation. The specified rectangle is considered - * already transformed. - */ - void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect); - - /** - * Modifies the current clip with the specified path and operation. - */ - void clipPath(const SkPath& path, SkClipOp op); - - /** - * Sets the current clip. - */ - void setClip(float left, float top, float right, float bottom); - - /** - * Returns the current clip in local coordinates. The clip rect is - * transformed by the inverse transform matrix. - */ - ANDROID_API const Rect& getLocalClip(); - - /** - * Returns the current clip in render target coordinates. - */ - const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); } - - /* - * Accessor functions so that the clip area can stay private - */ - bool clipIsEmpty() const { return mClipArea->isEmpty(); } - const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); } - bool clipIsSimple() const { return mClipArea->isSimple(); } - const ClipArea& getClipArea() const { return *mClipArea; } - ClipArea& mutateClipArea() { return *mClipArea; } - - WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip( - LinearAllocator& allocator, const ClipBase* recordedClip, - const Matrix4& recordedClipTransform); - void applyClip(const ClipBase* clip, const Matrix4& transform); - - /** - * Resets the clip to the specified rect. - */ - void resetClip(float left, float top, float right, float bottom); - - void initializeViewport(int width, int height) { - mViewportData.initialize(width, height); - mClipAreaRoot.setViewportDimensions(width, height); - } - - int getViewportWidth() const { return mViewportData.mWidth; } - int getViewportHeight() const { return mViewportData.mHeight; } - const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; } - - const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; } - void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; } - - /** - * Sets (and replaces) the current clipping outline - * - * If the current round rect clip is high priority, the incoming clip is ignored. - */ - void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius, - bool highPriority); - - /** - * Sets (and replaces) the current projection mask - */ - void setProjectionPathMask(const SkPath* path); - - /** - * Indicates whether the current transform has perspective components. - */ - bool hasPerspectiveTransform() const; - - /** - * Dirty flags. - */ - int flags; - - /** - * Previous snapshot. - */ - Snapshot* previous; - - /** - * A pointer to the currently active layer. - * - * This snapshot does not own the layer, this pointer must not be freed. - */ - Layer* layer; - - /** - * Target FBO used for rendering. Set to 0 when rendering directly - * into the framebuffer. - */ - GLuint fbo; - - /** - * Local transformation. Holds the current translation, scale and - * rotation values. - * - * This is a reference to a matrix owned by this snapshot or another - * snapshot. This pointer must not be freed. See ::mTransformRoot. - */ - mat4* transform; - - /** - * Current alpha value. This value is 1 by default, but may be set by a DisplayList which - * has translucent rendering in a non-overlapping View. This value will be used by - * the renderer to set the alpha in the current color being used for ensuing drawing - * operations. The value is inherited by child snapshots because the same value should - * be applied to descendants of the current DisplayList (for example, a TextView contains - * the base alpha value which should be applied to the child DisplayLists used for drawing - * the actual text). - */ - float alpha; - - /** - * Current clipping round rect. - * - * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips, - * never modified. - */ - const RoundRectClipState* roundRectClipState; - - /** - * Current projection masking path - used exclusively to mask projected, tessellated circles. - */ - const SkPath* projectionPathMask; - - void dump() const; - -private: - struct ViewportData { - ViewportData() : mWidth(0), mHeight(0) {} - void initialize(int width, int height) { - mWidth = width; - mHeight = height; - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); - } - - /* - * Width and height of current viewport. - * - * The viewport is always defined to be (0, 0, width, height). - */ - int mWidth; - int mHeight; - /** - * Contains the current orthographic, projection matrix. - */ - mat4 mOrthoMatrix; - }; - - mat4 mTransformRoot; - - ClipArea mClipAreaRoot; - ClipArea* mClipArea; - Rect mLocalClip; - - ViewportData mViewportData; - Vector3 mRelativeLightCenter; - -}; // class Snapshot - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index a6aae55b2ee8..f0912777e3d8 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -19,7 +19,6 @@ #include "Vector.h" -#include "FloatColor.h" #include "utils/Macros.h" namespace android { diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index a00b8db3c617..c5db861d4f48 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -141,14 +140,6 @@ public: return true; } - static std::unique_ptr makeSnapshot(const Matrix4& transform, const Rect& clip) { - std::unique_ptr snapshot(new Snapshot()); - // store clip first, so it isn't transformed - snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom); - *(snapshot->transform) = transform; - return snapshot; - } - static sk_sp createBitmap(int width, int height, SkColorType colorType = kN32_SkColorType) { SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType); diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 9388c2062736..70423a70157b 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -16,7 +16,6 @@ #include -#include "CanvasState.h" #include "DisplayList.h" #include "hwui/Canvas.h" #include "pipeline/skia/SkiaDisplayList.h" @@ -116,52 +115,6 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) } BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView); -class NullClient : public CanvasStateClient { - void onViewportInitialized() override {} - void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} - GLuint getTargetFbo() const override { return 0; } -}; - -void BM_CanvasState_saverestore(benchmark::State& benchState) { - NullClient client; - CanvasState state(client); - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - - while (benchState.KeepRunning()) { - state.save(SaveFlags::MatrixClip); - state.save(SaveFlags::MatrixClip); - benchmark::DoNotOptimize(&state); - state.restore(); - state.restore(); - } -} -BENCHMARK(BM_CanvasState_saverestore); - -void BM_CanvasState_init(benchmark::State& benchState) { - NullClient client; - CanvasState state(client); - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - - while (benchState.KeepRunning()) { - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - benchmark::DoNotOptimize(&state); - } -} -BENCHMARK(BM_CanvasState_init); - -void BM_CanvasState_translate(benchmark::State& benchState) { - NullClient client; - CanvasState state(client); - state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3()); - - while (benchState.KeepRunning()) { - state.translate(5, 5, 0); - benchmark::DoNotOptimize(&state); - state.translate(-5, -5, 0); - } -} -BENCHMARK(BM_CanvasState_translate); - void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { sp child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp deleted file mode 100644 index 4c03811b0c96..000000000000 --- a/libs/hwui/tests/unit/CanvasStateTests.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2015 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 "CanvasState.h" - -#include "Matrix.h" -#include "Rect.h" -#include "hwui/Canvas.h" -#include "utils/LinearAllocator.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -class NullClient : public CanvasStateClient { - void onViewportInitialized() override {} - void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} - GLuint getTargetFbo() const override { return 0; } -}; - -static NullClient sNullClient; - -static bool approxEqual(const Matrix4& a, const Matrix4& b) { - for (int i = 0; i < 16; i++) { - if (!MathUtils::areEqual(a[i], b[i])) { - return false; - } - } - return true; -} - -TEST(CanvasState, gettersAndSetters) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - ASSERT_EQ(state.getWidth(), 200); - ASSERT_EQ(state.getHeight(), 200); - - Matrix4 simpleTranslate; - simpleTranslate.loadTranslate(10, 20, 0); - state.setMatrix(simpleTranslate); - - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); - ASSERT_EQ(state.getLocalClipBounds(), Rect(-10, -20, 190, 180)); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); - EXPECT_TRUE(state.clipIsSimple()); -} - -TEST(CanvasState, simpleClipping) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100)); - - state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100)); - - state.clipRect(50, 50, 150, 150, SkClipOp::kReplace_deprecated); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150)); -} - -TEST(CanvasState, complexClipping) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.save(SaveFlags::MatrixClip); - { - // rotated clip causes complex clip - state.rotate(10); - EXPECT_TRUE(state.clipIsSimple()); - state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect); - EXPECT_FALSE(state.clipIsSimple()); - } - state.restore(); - - state.save(SaveFlags::MatrixClip); - { - // subtracted clip causes complex clip - EXPECT_TRUE(state.clipIsSimple()); - state.clipRect(50, 50, 150, 150, SkClipOp::kDifference); - EXPECT_FALSE(state.clipIsSimple()); - } - state.restore(); - - state.save(SaveFlags::MatrixClip); - { - // complex path causes complex clip - SkPath path; - path.addOval(SkRect::MakeWH(200, 200)); - EXPECT_TRUE(state.clipIsSimple()); - state.clipPath(&path, SkClipOp::kDifference); - EXPECT_FALSE(state.clipIsSimple()); - } - state.restore(); -} - -TEST(CanvasState, saveAndRestore) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.save(SaveFlags::Clip); - { - state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); - } - state.restore(); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); // verify restore - - Matrix4 simpleTranslate; - simpleTranslate.loadTranslate(10, 10, 0); - state.save(SaveFlags::Matrix); - { - state.translate(10, 10, 0); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); - } - state.restore(); - EXPECT_FALSE(approxEqual(*state.currentTransform(), simpleTranslate)); -} - -TEST(CanvasState, saveAndRestoreButNotTooMuch) { - CanvasState state(sNullClient); - state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3()); - - state.save(SaveFlags::Matrix); // NOTE: clip not saved - { - state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); - } - state.restore(); - ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); // verify not restored - - Matrix4 simpleTranslate; - simpleTranslate.loadTranslate(10, 10, 0); - state.save(SaveFlags::Clip); // NOTE: matrix not saved - { - state.translate(10, 10, 0); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); - } - state.restore(); - EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); // verify not restored -} -} -} diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp deleted file mode 100644 index 450bb679a45f..000000000000 --- a/libs/hwui/tests/unit/ClipAreaTests.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2015 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 -#include -#include - -#include "ClipArea.h" - -#include "Matrix.h" -#include "Rect.h" -#include "utils/LinearAllocator.h" - -namespace android { -namespace uirenderer { - -static Rect kViewportBounds(2048, 2048); - -static ClipArea createClipArea() { - ClipArea area; - area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight()); - return area; -} - -TEST(TransformedRectangle, basics) { - Rect r(0, 0, 100, 100); - Matrix4 minus90; - minus90.loadRotate(-90); - minus90.mapRect(r); - Rect r2(20, 40, 120, 60); - - Matrix4 m90; - m90.loadRotate(90); - TransformedRectangle tr(r, m90); - EXPECT_TRUE(tr.canSimplyIntersectWith(tr)); - - Matrix4 m0; - TransformedRectangle tr0(r2, m0); - EXPECT_FALSE(tr.canSimplyIntersectWith(tr0)); - - Matrix4 m45; - m45.loadRotate(45); - TransformedRectangle tr2(r, m45); - EXPECT_FALSE(tr2.canSimplyIntersectWith(tr)); -} - -TEST(RectangleList, basics) { - RectangleList list; - EXPECT_TRUE(list.isEmpty()); - - Rect r(0, 0, 100, 100); - Matrix4 m45; - m45.loadRotate(45); - list.set(r, m45); - EXPECT_FALSE(list.isEmpty()); - - Rect r2(20, 20, 200, 200); - list.intersectWith(r2, m45); - EXPECT_FALSE(list.isEmpty()); - EXPECT_EQ(1, list.getTransformedRectanglesCount()); - - Rect r3(20, 20, 200, 200); - Matrix4 m30; - m30.loadRotate(30); - list.intersectWith(r2, m30); - EXPECT_FALSE(list.isEmpty()); - EXPECT_EQ(2, list.getTransformedRectanglesCount()); - - SkRegion clip; - clip.setRect(0, 0, 2000, 2000); - SkRegion rgn(list.convertToRegion(clip)); - EXPECT_FALSE(rgn.isEmpty()); -} - -TEST(ClipArea, basics) { - ClipArea area(createClipArea()); - EXPECT_FALSE(area.isEmpty()); -} - -TEST(ClipArea, paths) { - ClipArea area(createClipArea()); - SkPath path; - SkScalar r = 100; - path.addCircle(r, r, r); - area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op); - EXPECT_FALSE(area.isEmpty()); - EXPECT_FALSE(area.isSimple()); - EXPECT_FALSE(area.isRectangleList()); - - Rect clipRect(area.getClipRect()); - Rect expected(0, 0, r * 2, r * 2); - EXPECT_EQ(expected, clipRect); - SkRegion clipRegion(area.getClipRegion()); - auto skRect(clipRegion.getBounds()); - Rect regionBounds; - regionBounds.set(skRect); - EXPECT_EQ(expected, regionBounds); -} - -TEST(ClipArea, replaceNegative) { - ClipArea area(createClipArea()); - area.setClip(0, 0, 100, 100); - - Rect expected(-50, -50, 50, 50); - area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op); - EXPECT_EQ(expected, area.getClipRect()); -} - -TEST(ClipArea, serializeClip) { - ClipArea area(createClipArea()); - LinearAllocator allocator; - - // unset clip - EXPECT_EQ(nullptr, area.serializeClip(allocator)); - - // rect clip - area.setClip(0, 0, 200, 200); - { - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode); - ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; - EXPECT_EQ(Rect(200, 200), serializedClip->rect); - EXPECT_EQ(serializedClip, area.serializeClip(allocator)) - << "Requery of clip on unmodified ClipArea must return same pointer."; - } - - // rect list - Matrix4 rotate; - rotate.loadRotate(5.0f); - area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op); - { - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode); - ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; - auto clipRectList = reinterpret_cast(serializedClip); - EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); - EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect); - EXPECT_EQ(serializedClip, area.serializeClip(allocator)) - << "Requery of clip on unmodified ClipArea must return same pointer."; - } - - // region - SkPath circlePath; - circlePath.addCircle(100, 100, 100); - area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); - { - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - ASSERT_EQ(ClipMode::Region, serializedClip->mode); - ASSERT_TRUE(serializedClip->intersectWithRoot) << "Replace op, so expect intersectWithRoot"; - auto clipRegion = reinterpret_cast(serializedClip); - EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds()) - << "Clip region should be 200x200"; - EXPECT_EQ(Rect(200, 200), clipRegion->rect); - EXPECT_EQ(serializedClip, area.serializeClip(allocator)) - << "Requery of clip on unmodified ClipArea must return same pointer."; - } -} - -TEST(ClipArea, serializeClip_pathIntersectWithRoot) { - ClipArea area(createClipArea()); - LinearAllocator allocator; - SkPath circlePath; - circlePath.addCircle(100, 100, 100); - area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kIntersect_Op); - - auto serializedClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, serializedClip); - EXPECT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot"; -} - -TEST(ClipArea, serializeIntersectedClip) { - ClipArea area(createClipArea()); - LinearAllocator allocator; - - // simple state; - EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); - area.setClip(0, 0, 200, 200); - { - auto origRectClip = area.serializeClip(allocator); - ASSERT_NE(nullptr, origRectClip); - EXPECT_EQ(origRectClip, - area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); - } - - // rect - { - ClipRect recordedClip(Rect(100, 100)); - Matrix4 translateScale; - translateScale.loadTranslate(100, 100, 0); - translateScale.scale(2, 3, 1); - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); - ASSERT_NE(nullptr, resolvedClip); - ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode); - EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect); - - EXPECT_EQ(resolvedClip, - area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) - << "Must return previous serialization, since input is same"; - - ClipRect recordedClip2(Rect(100, 100)); - EXPECT_NE(resolvedClip, - area.serializeIntersectedClip(allocator, &recordedClip2, translateScale)) - << "Shouldn't return previous serialization, since matrix location is different"; - } - - // rect list - Matrix4 rotate; - rotate.loadRotate(2.0f); - area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op); - { - ClipRect recordedClip(Rect(100, 100)); - auto resolvedClip = - area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity()); - ASSERT_NE(nullptr, resolvedClip); - ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode); - auto clipRectList = reinterpret_cast(resolvedClip); - EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); - } - - // region - SkPath circlePath; - circlePath.addCircle(100, 100, 100); - area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); - { - SkPath ovalPath; - ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200)); - - ClipRegion recordedClip; - recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200))); - recordedClip.rect = Rect(200, 200); - - Matrix4 translate10x20; - translate10x20.loadTranslate(10, 20, 0); - auto resolvedClip = area.serializeIntersectedClip( - allocator, &recordedClip, - translate10x20); // Note: only translate for now, others not handled correctly - ASSERT_NE(nullptr, resolvedClip); - ASSERT_EQ(ClipMode::Region, resolvedClip->mode); - auto clipRegion = reinterpret_cast(resolvedClip); - EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds()); - } -} - -TEST(ClipArea, serializeIntersectedClip_snap) { - ClipArea area(createClipArea()); - area.setClip(100.2, 100.4, 500.6, 500.8); - LinearAllocator allocator; - - { - // no recorded clip case - auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()); - EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect); - } - { - // recorded clip case - ClipRect recordedClip(Rect(100.12, 100.74)); - Matrix4 translateScale; - translateScale.loadTranslate(100, 100, 0); - translateScale.scale(2, 3, - 1); // recorded clip will have non-int coords, even after transform - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); - ASSERT_NE(nullptr, resolvedClip); - EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode); - EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect); - } -} - -TEST(ClipArea, serializeIntersectedClip_scale) { - ClipArea area(createClipArea()); - area.setClip(0, 0, 400, 400); - LinearAllocator allocator; - - SkPath circlePath; - circlePath.addCircle(50, 50, 50); - - ClipRegion recordedClip; - recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100))); - recordedClip.rect = Rect(100, 100); - - Matrix4 translateScale; - translateScale.loadTranslate(100, 100, 0); - translateScale.scale(2, 2, 1); - auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); - - ASSERT_NE(nullptr, resolvedClip); - EXPECT_EQ(ClipMode::Region, resolvedClip->mode); - EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect); - auto clipRegion = reinterpret_cast(resolvedClip); - EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_identity) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - ClipArea::applyTransformToRegion(Matrix4::identity(), ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_translate) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.loadTranslate(10, 20, 0); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_scale) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.loadScale(2, 3, 1); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_translateScale) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.translate(10, 20); - transform.scale(2, 3, 1); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds()); -} - -TEST(ClipArea, applyTransformToRegion_rotate90) { - SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4)); - Matrix4 transform; - transform.loadRotate(90); - ClipArea::applyTransformToRegion(transform, ®ion); - EXPECT_TRUE(region.isRect()); - EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds()); -} - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 2926ef3a2d95..2c73940b9b9c 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -32,6 +32,7 @@ #include "pipeline/skia/SkiaRecordingCanvas.h" #include "renderthread/CanvasContext.h" #include "tests/common/TestUtils.h" +#include "utils/Color.h" using namespace android; using namespace android::uirenderer; diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp deleted file mode 100644 index 9d673c82d4d0..000000000000 --- a/libs/hwui/tests/unit/SnapshotTests.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2016 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 - -#include - -#include - -using namespace android::uirenderer; - -TEST(Snapshot, serializeIntersectedClip) { - auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100)); - auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90)); - auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); - root->previous = actualRoot.get(); - child->previous = root.get(); - - LinearAllocator allocator; - ClipRect rect(Rect(0, 0, 75, 75)); - { - auto intersectWithChild = - child->serializeIntersectedClip(allocator, &rect, Matrix4::identity()); - ASSERT_NE(nullptr, intersectWithChild); - EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child"; - } - - rect.intersectWithRoot = true; - { - auto intersectWithRoot = - child->serializeIntersectedClip(allocator, &rect, Matrix4::identity()); - ASSERT_NE(nullptr, intersectWithRoot); - EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root"; - } -} - -TEST(Snapshot, applyClip) { - auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100)); - auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90)); - root->previous = actualRoot.get(); - - ClipRect rect(Rect(0, 0, 75, 75)); - { - auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); - child->previous = root.get(); - child->applyClip(&rect, Matrix4::identity()); - - EXPECT_TRUE(child->getClipArea().isSimple()); - EXPECT_EQ(Rect(50, 50, 75, 75), child->getRenderTargetClip()); - } - - { - rect.intersectWithRoot = true; - auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90)); - child->previous = root.get(); - child->applyClip(&rect, Matrix4::identity()); - - EXPECT_TRUE(child->getClipArea().isSimple()); - EXPECT_EQ(Rect(10, 10, 75, 75), child->getRenderTargetClip()); - } -} -- cgit v1.2.3-59-g8ed1b