From be3876c434864d6c05212158b25d778d29b07eb0 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Fri, 20 Apr 2018 16:13:31 -0400 Subject: Respect a Layer's (e.g. SurfaceTexture) colorSpace when compositing This CL extracts the android_dataspace from the GLConsumer and converts it to a SkColorSpace. HWUI always expects to composite into an sRGB destination so when we draw the layer we run the draw through a colorFilter that converts the input colorSpace into that of the destination. Test: CtsViewTestCases Bug: 78016220 Change-Id: Ic0446a0d861e86a5a9d0382346b57fcc45c8a61b --- libs/hwui/DeferredLayerUpdater.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'libs/hwui/DeferredLayerUpdater.cpp') diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index be7d663b412f..569de76f294e 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -40,7 +40,6 @@ DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayer } DeferredLayerUpdater::~DeferredLayerUpdater() { - SkSafeUnref(mColorFilter); setTransform(nullptr); mRenderState.unregisterDeferredLayerUpdater(this); destroyLayer(); @@ -67,8 +66,11 @@ void DeferredLayerUpdater::destroyLayer() { void DeferredLayerUpdater::setPaint(const SkPaint* paint) { mAlpha = PaintUtils::getAlphaDirect(paint); mMode = PaintUtils::getBlendModeDirect(paint); - SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr; - SkRefCnt_SafeAssign(mColorFilter, colorFilter); + if (paint) { + mColorFilter = paint->refColorFilter(); + } else { + mColorFilter.reset(); + } } void DeferredLayerUpdater::apply() { @@ -143,7 +145,7 @@ void DeferredLayerUpdater::doUpdateTexImage() { #endif mSurfaceTexture->getTransformMatrix(transform); - updateLayer(forceFilter, transform); + updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace()); } } @@ -153,17 +155,19 @@ void DeferredLayerUpdater::doUpdateVkTexImage() { Layer::Api::OpenGL, Layer::Api::Vulkan); static const mat4 identityMatrix; - updateLayer(false, identityMatrix.data); + updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN); VkLayer* vkLayer = static_cast(mLayer); vkLayer->updateTexture(); } -void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) { +void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform, + android_dataspace dataspace) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); mLayer->getTexTransform().load(textureTransform); + mLayer->setDataSpace(dataspace); } void DeferredLayerUpdater::detachSurfaceTexture() { -- 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/DeferredLayerUpdater.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/DeferredLayerUpdater.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/DeferredLayerUpdater.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/DeferredLayerUpdater.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/DeferredLayerUpdater.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 1a025a7163daa89b6cb2043c56fa0ddc0548db5c Mon Sep 17 00:00:00 2001 From: Stan Iliev Date: Wed, 5 Sep 2018 16:35:11 -0400 Subject: Refactor HWUI readback code to be backend independent Implement readback from Surface, TextureView and HW Bitmap for Vulkan pipeline by wrapping the graphics buffer in an SkImage. Refactor both Vulkan and GL readback to use common code. TextureView readback is moved from IRenderPipeline interface to Readback class. Refactor all 3 readback flows to use common implementation. Test: Passed all view, uirendering and graphics CTS tests with GL Test: Passed many CTS test with Vulkan, that require readback Bug: 113673613 Change-Id: Ifbfd8170a5401f87a709b4b1b9fa058e8e11768d --- core/jni/android/graphics/Bitmap.cpp | 4 + core/jni/android_view_ThreadedRenderer.cpp | 3 + libs/hwui/Android.bp | 3 +- libs/hwui/DeferredLayerUpdater.cpp | 1 + libs/hwui/EglReadback.cpp | 95 -------- libs/hwui/EglReadback.h | 50 ----- libs/hwui/Layer.cpp | 3 +- libs/hwui/Layer.h | 13 +- libs/hwui/Readback.cpp | 287 +++++++++++++++++++++++++ libs/hwui/Readback.h | 22 +- libs/hwui/hwui/Bitmap.cpp | 2 +- libs/hwui/pipeline/skia/LayerDrawable.cpp | 33 +-- libs/hwui/pipeline/skia/LayerDrawable.h | 2 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 59 ----- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h | 1 - libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp | 147 ------------- libs/hwui/pipeline/skia/SkiaOpenGLReadback.h | 37 ---- libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 5 - libs/hwui/pipeline/skia/SkiaVulkanPipeline.h | 1 - libs/hwui/pipeline/skia/SkiaVulkanReadback.h | 44 ---- libs/hwui/renderthread/CanvasContext.cpp | 4 - libs/hwui/renderthread/CanvasContext.h | 1 - libs/hwui/renderthread/IRenderPipeline.h | 1 - libs/hwui/renderthread/RenderProxy.cpp | 12 +- libs/hwui/renderthread/RenderProxy.h | 2 +- libs/hwui/renderthread/RenderThread.cpp | 16 +- 26 files changed, 353 insertions(+), 495 deletions(-) delete mode 100644 libs/hwui/EglReadback.cpp delete mode 100644 libs/hwui/EglReadback.h create mode 100644 libs/hwui/Readback.cpp delete mode 100644 libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp delete mode 100644 libs/hwui/pipeline/skia/SkiaOpenGLReadback.h delete mode 100644 libs/hwui/pipeline/skia/SkiaVulkanReadback.h (limited to 'libs/hwui/DeferredLayerUpdater.cpp') diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 8083a6a27da7..0a252716b69e 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1198,6 +1198,10 @@ static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) { sp buffer(graphicBufferForJavaObject(env, graphicBuffer)); + // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 + // format and SRGB color space. + // To support any color space, we need to pass an additional ColorSpace argument to + // java Bitmap.createHardwareBitmap. sk_sp bitmap = Bitmap::createFrom(buffer); if (!bitmap.get()) { ALOGW("failed to create hardware bitmap from graphic buffer"); diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 4a1774279d28..3c59bd1e3856 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -1024,6 +1024,9 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode( // Continue I guess? } sk_sp bitmap = Bitmap::createFrom(buffer); + // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 + // format and SRGB color space. + // To support any color space, we could extract it from BufferItem and pass it to Bitmap. return bitmap::createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Premultiplied); } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 2baacbf541dc..e063e0bd9c3e 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -170,7 +170,6 @@ cc_defaults { "pipeline/skia/SkiaDisplayList.cpp", "pipeline/skia/SkiaMemoryTracer.cpp", "pipeline/skia/SkiaOpenGLPipeline.cpp", - "pipeline/skia/SkiaOpenGLReadback.cpp", "pipeline/skia/SkiaPipeline.cpp", "pipeline/skia/SkiaProfileRenderer.cpp", "pipeline/skia/SkiaRecordingCanvas.cpp", @@ -217,13 +216,13 @@ cc_defaults { "Layer.cpp", "LayerUpdateQueue.cpp", "Matrix.cpp", - "EglReadback.cpp", "PathParser.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Properties.cpp", "PropertyValuesAnimatorSet.cpp", "PropertyValuesHolder.cpp", + "Readback.cpp", "RecordingCanvas.cpp", "RenderNode.cpp", "RenderProperties.cpp", diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 00916559a9c2..837d5461d2a8 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -49,6 +49,7 @@ void DeferredLayerUpdater::destroyLayer() { } mLayer->postDecStrong(); + mLayer = nullptr; } diff --git a/libs/hwui/EglReadback.cpp b/libs/hwui/EglReadback.cpp deleted file mode 100644 index 65becf88b930..000000000000 --- a/libs/hwui/EglReadback.cpp +++ /dev/null @@ -1,95 +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 "EglReadback.h" - -#include "renderthread/EglManager.h" - -#include -#include -#include - -namespace android { -namespace uirenderer { - -CopyResult EglReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { - ATRACE_CALL(); - // Setup the source - sp sourceBuffer; - sp sourceFence; - Matrix4 texTransform; - status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data); - texTransform.invalidateType(); - if (err != NO_ERROR) { - ALOGW("Failed to get last queued buffer, error = %d", err); - return CopyResult::UnknownError; - } - if (!sourceBuffer.get()) { - ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); - return CopyResult::SourceEmpty; - } - if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { - ALOGW("Surface is protected, unable to copy from it"); - return CopyResult::SourceInvalid; - } - err = sourceFence->wait(500 /* ms */); - if (err != NO_ERROR) { - ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); - return CopyResult::Timeout; - } - - return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap); -} - -CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform, - const Rect& srcRect, SkBitmap* bitmap) { - mRenderThread.requireGlContext(); - // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via - // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES - // to be able to properly sample from the buffer. - - // Create the EGLImage object that maps the GraphicBuffer - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); - EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; - - EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - clientBuffer, attrs); - - if (sourceImage == EGL_NO_IMAGE_KHR) { - ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); - return CopyResult::UnknownError; - } - - uint32_t width = graphicBuffer->getWidth(); - uint32_t height = graphicBuffer->getHeight(); - CopyResult copyResult = - copyImageInto(sourceImage, texTransform, width, height, srcRect, bitmap); - - eglDestroyImageKHR(display, sourceImage); - return copyResult; -} - -CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) { - Rect srcRect; - Matrix4 transform; - transform.loadScale(1, -1, 1); - transform.translate(0, -1); - return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap); -} - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/EglReadback.h b/libs/hwui/EglReadback.h deleted file mode 100644 index e723169ad795..000000000000 --- a/libs/hwui/EglReadback.h +++ /dev/null @@ -1,50 +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. - */ - -#pragma once - -#include "Readback.h" - -#include "Matrix.h" - -#include -#include - -namespace android { -namespace uirenderer { - -class EglReadback : public Readback { -public: - virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, - SkBitmap* bitmap) override; - virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, - SkBitmap* bitmap) override; - -protected: - explicit EglReadback(renderthread::RenderThread& thread) : Readback(thread) {} - virtual ~EglReadback() {} - - virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, - int imgWidth, int imgHeight, const Rect& srcRect, - SkBitmap* bitmap) = 0; - -private: - CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform, - const Rect& srcRect, SkBitmap* bitmap); -}; - -} // namespace uirenderer -} // namespace android diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index f59a2e6ee5c1..cc95051fc952 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -26,8 +26,7 @@ namespace uirenderer { Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alpha, SkBlendMode mode) - : GpuMemoryTracker(GpuObjectType::Layer) - , mRenderState(renderState) + : mRenderState(renderState) , mColorFilter(colorFilter) , alpha(alpha) , mode(mode) { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index c4e4c1c96ba6..6f07a43ceb58 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -16,7 +16,6 @@ #pragma once -#include #include #include @@ -39,21 +38,21 @@ class RenderState; /** * A layer has dimensions and is backed by a backend specific texture or framebuffer. */ -class Layer : public VirtualLightRefBase, GpuMemoryTracker { +class Layer : public VirtualLightRefBase { public: Layer(RenderState& renderState, sk_sp, int alpha, SkBlendMode mode); ~Layer(); - virtual uint32_t getWidth() const { return mWidth; } + uint32_t getWidth() const { return mWidth; } - virtual uint32_t getHeight() const { return mHeight; } + uint32_t getHeight() const { return mHeight; } - virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } + void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } - virtual void setBlend(bool blend) { mBlend = blend; } + void setBlend(bool blend) { mBlend = blend; } - virtual bool isBlend() const { return mBlend; } + bool isBlend() const { return mBlend; } inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp new file mode 100644 index 000000000000..80f2b5714659 --- /dev/null +++ b/libs/hwui/Readback.cpp @@ -0,0 +1,287 @@ +/* + * 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 "Readback.h" + +#include "pipeline/skia/LayerDrawable.h" +#include "renderthread/EglManager.h" +#include "renderthread/VulkanManager.h" + +#include +#include +#include +#include +#include "DeferredLayerUpdater.h" +#include "Properties.h" +#include "hwui/Bitmap.h" +#include "utils/Color.h" +#include "utils/MathUtils.h" + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { + +CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { + ATRACE_CALL(); + // Setup the source + sp sourceBuffer; + sp sourceFence; + Matrix4 texTransform; + status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data); + texTransform.invalidateType(); + if (err != NO_ERROR) { + ALOGW("Failed to get last queued buffer, error = %d", err); + return CopyResult::UnknownError; + } + if (!sourceBuffer.get()) { + ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); + return CopyResult::SourceEmpty; + } + if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { + ALOGW("Surface is protected, unable to copy from it"); + return CopyResult::SourceInvalid; + } + err = sourceFence->wait(500 /* ms */); + if (err != NO_ERROR) { + ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); + return CopyResult::Timeout; + } + if (!sourceBuffer.get()) { + return CopyResult::UnknownError; + } + + sk_sp colorSpace = + DataSpaceToColorSpace(static_cast(surface.getBuffersDataSpace())); + sk_sp colorSpaceFilter; + if (colorSpace && !colorSpace->isSRGB()) { + colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace); + } + sk_sp image = SkImage::MakeFromAHardwareBuffer( + reinterpret_cast(sourceBuffer.get()), kPremul_SkAlphaType); + return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap); +} + +CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) { + LOG_ALWAYS_FATAL_IF(!hwBitmap->isHardware()); + + Rect srcRect; + Matrix4 transform; + transform.loadScale(1, -1, 1); + transform.translate(0, -1); + + // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage". + // TODO: When this was attempted, it resulted in instability. + sk_sp colorSpaceFilter; + sk_sp colorSpace = hwBitmap->info().refColorSpace(); + if (colorSpace && !colorSpace->isSRGB()) { + colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace); + } + sk_sp image = SkImage::MakeFromAHardwareBuffer( + reinterpret_cast(hwBitmap->graphicBuffer()), kPremul_SkAlphaType); + + // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format + // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for + // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test. + return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap); +} + +CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) { + if (!mRenderThread.getGrContext()) { + return CopyResult::UnknownError; + } + + // acquire most recent buffer for drawing + deferredLayer->updateTexImage(); + deferredLayer->apply(); + const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height()); + CopyResult copyResult = CopyResult::UnknownError; + Layer* layer = deferredLayer->backingLayer(); + if (layer) { + if (copyLayerInto(layer, nullptr, &dstRect, bitmap)) { + copyResult = CopyResult::Success; + } + } + return copyResult; +} + +CopyResult Readback::copyImageInto(const sk_sp& image, + sk_sp& colorSpaceFilter, Matrix4& texTransform, + const Rect& srcRect, SkBitmap* bitmap) { + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { + mRenderThread.requireGlContext(); + } else { + mRenderThread.vulkanManager().initialize(); + } + if (!image.get()) { + return CopyResult::UnknownError; + } + int imgWidth = image->width(); + int imgHeight = image->height(); + sk_sp grContext = sk_ref_sp(mRenderThread.getGrContext()); + + if (bitmap->colorType() == kRGBA_F16_SkColorType && + !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) { + ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); + return CopyResult::DestinationInvalid; + } + + CopyResult copyResult = CopyResult::UnknownError; + + int displayedWidth = imgWidth, displayedHeight = imgHeight; + // If this is a 90 or 270 degree rotation we need to swap width/height to get the device + // size. + if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) { + std::swap(displayedWidth, displayedHeight); + } + SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height()); + SkRect skiaSrcRect = srcRect.toSkRect(); + if (skiaSrcRect.isEmpty()) { + skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight); + } + bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight)); + if (!srcNotEmpty) { + return copyResult; + } + + // See Readback::copyLayerInto for an overview of color space conversion. + // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder). + // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion. + // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally. + Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc); + bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) && + MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height()); + layer.setForceFilter(!disableFilter); + layer.setSize(displayedWidth, displayedHeight); + texTransform.copyTo(layer.getTexTransform()); + layer.setImage(image); + if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) { + copyResult = CopyResult::Success; + } + + return copyResult; +} + +bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect, + SkBitmap* bitmap) { + /* + * In the past only TextureView readback was setting the temporary surface color space to null. + * Now all 3 readback flows are drawing into a SkSurface with null color space. + * At readback there are 3 options to convert the source image color space to the destination + * color space requested in "bitmap->info().colorSpace()": + * 1. Set color space for temporary surface render target to null (disables color management), + * colorspace tag from source SkImage is ignored by Skia, + * convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter, + * do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2", + * read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion. + * + * 2. Set color space for temporary surface render target to SRGB (not nullptr), + * colorspace tag on the source SkImage is used by Skia to enable conversion, + * convert SkImage to SRGB at draw time with drawImage (no filters), + * do a readback from temporary SkSurface, which will do a color conversion from SRGB to + * bitmap->info().colorSpace() on the CPU. + * + * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(), + * colorspace tag on the source SkImage is used by Skia to enable conversion, + * convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters), + * do a readback from SkSurface, which will not do any color conversion, because + * surface was created with the same color space as the "bitmap". + * + * Option 1 is used for all readback flows. + * Options 2 and 3 are new, because skia added support for non-SRGB render targets without + * linear blending. + * TODO: evaluate if options 2 or 3 for color space conversion are better. + */ + + // drop the colorSpace from the temporary surface. + SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr); + + /* This intermediate surface is present to work around a bug in SwiftShader that + * prevents us from reading the contents of the layer's texture directly. The + * workaround involves first rendering that texture into an intermediate buffer and + * then reading from the intermediate buffer into the bitmap. + * Another reason to render in an offscreen buffer is to scale and to avoid an issue b/62262733 + * with reading incorrect data from EGLImage backed SkImage (likely a driver bug). + */ + sk_sp tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), + SkBudgeted::kYes, surfaceInfo); + + if (!tmpSurface.get()) { + surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType); + tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes, + surfaceInfo); + if (!tmpSurface.get()) { + ALOGW("Unable to readback GPU contents into the provided bitmap"); + return false; + } + } + + if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(), + tmpSurface->getCanvas(), layer, srcRect, dstRect, + false)) { + // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on + // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has + // disabled color conversion. + SkColorSpace* destColorSpace = bitmap->info().colorSpace(); + SkBitmap tempSRGBBitmap; + SkBitmap tmpN32Bitmap; + SkBitmap* bitmapInSRGB; + if (destColorSpace && !destColorSpace->isSRGB()) { + tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB())); + bitmapInSRGB = &tempSRGBBitmap; // Need to convert latter from SRGB to non-SRGB. + } else { + bitmapInSRGB = bitmap; // No need for color conversion - write directly into output. + } + bool success = false; + + // TODO: does any of the readbacks below clamp F16 exSRGB? + // Readback into a SRGB SkBitmap. + if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(), + bitmapInSRGB->rowBytes(), 0, 0)) { + success = true; + } else { + // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into + // 8888 and then convert that into the destination format before giving up. + SkImageInfo bitmapInfo = + SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(), + SkColorSpace::MakeSRGB()); + if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) && + tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(), + tmpN32Bitmap.rowBytes(), 0, 0)) { + success = true; + bitmapInSRGB = &tmpN32Bitmap; + } + } + + if (success) { + if (bitmapInSRGB != bitmap) { + // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to + // destination bitmap color format if needed. + if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(), + bitmap->rowBytes(), 0, 0)) { + return false; + } + } + bitmap->notifyPixelsChanged(); + return true; + } + } + + return false; +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index ad3a8b690617..d9e10cedc0e8 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -16,16 +16,21 @@ #pragma once +#include "Matrix.h" #include "Rect.h" #include "renderthread/RenderThread.h" #include namespace android { +class Bitmap; class GraphicBuffer; class Surface; namespace uirenderer { +class DeferredLayerUpdater; +class Layer; + // Keep in sync with PixelCopy.java codes enum class CopyResult { Success = 0, @@ -38,15 +43,22 @@ enum class CopyResult { class Readback { public: + explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {} /** * Copies the surface's most recently queued buffer into the provided bitmap. */ - virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) = 0; - virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0; + CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap); -protected: - explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {} - virtual ~Readback() {} + CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap); + + CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); + +private: + CopyResult copyImageInto(const sk_sp& image, sk_sp& colorSpaceFilter, + Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap); + + bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect, + SkBitmap* bitmap); renderthread::RenderThread& mRenderThread; }; diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 3939696692d2..440620a6a417 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -290,7 +290,7 @@ void Bitmap::getSkBitmap(SkBitmap* outBitmap) { if (isHardware()) { outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(), info().colorType(), info().alphaType(), nullptr)); - uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap); + uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap); if (mInfo.colorSpace()) { sk_sp pixelRef = sk_ref_sp(outBitmap->pixelRef()); outBitmap->setInfo(mInfo); diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index b6cd4b08e5fe..3ca0f8139c02 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -28,12 +28,13 @@ namespace skiapipeline { void LayerDrawable::onDraw(SkCanvas* canvas) { Layer* layer = mLayerUpdater->backingLayer(); if (layer) { - DrawLayer(canvas->getGrContext(), canvas, layer); + DrawLayer(canvas->getGrContext(), canvas, layer, nullptr, nullptr, true); } } bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, - const SkRect* dstRect) { + const SkRect* srcRect, const SkRect* dstRect, + bool useLayerTransform) { if (context == nullptr) { SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface")); return false; @@ -60,12 +61,10 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer } SkMatrix matrix; - if (dstRect) { - // Destination rectangle is set only when we are trying to read back the content - // of the layer. In this case we don't want to apply layer transform. - matrix = textureMatrix; - } else { + if (useLayerTransform) { matrix = SkMatrix::Concat(layerTransform, textureMatrix); + } else { + matrix = textureMatrix; } SkPaint paint; @@ -81,16 +80,26 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer canvas->save(); canvas->concat(matrix); } - if (dstRect) { + if (dstRect || srcRect) { SkMatrix matrixInv; if (!matrix.invert(&matrixInv)) { matrixInv = matrix; } - SkRect srcRect = SkRect::MakeIWH(layerWidth, layerHeight); - matrixInv.mapRect(&srcRect); - SkRect skiaDestRect = *dstRect; + SkRect skiaSrcRect; + if (srcRect) { + skiaSrcRect = *srcRect; + } else { + skiaSrcRect = SkRect::MakeIWH(layerWidth, layerHeight); + } + matrixInv.mapRect(&skiaSrcRect); + SkRect skiaDestRect; + if (dstRect) { + skiaDestRect = *dstRect; + } else { + skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight); + } matrixInv.mapRect(&skiaDestRect); - canvas->drawImageRect(layerImage.get(), srcRect, skiaDestRect, &paint, + canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint, SkCanvas::kFast_SrcRectConstraint); } else { canvas->drawImage(layerImage.get(), 0, 0, &paint); diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h index 18d118405a39..5c125908ffb2 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.h +++ b/libs/hwui/pipeline/skia/LayerDrawable.h @@ -33,7 +33,7 @@ public: explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {} static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, - const SkRect* dstRect = nullptr); + const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform); protected: virtual SkRect onGetBounds() override { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 2ae37233098e..d58b59e83380 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -127,65 +127,6 @@ bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect return *requireSwap; } -bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) { - if (!mRenderThread.getGrContext()) { - return false; - } - - // acquire most recent buffer for drawing - deferredLayer->updateTexImage(); - deferredLayer->apply(); - - // drop the colorSpace as we only support readback into sRGB or extended sRGB - SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr); - - /* This intermediate surface is present to work around a bug in SwiftShader that - * prevents us from reading the contents of the layer's texture directly. The - * workaround involves first rendering that texture into an intermediate buffer and - * then reading from the intermediate buffer into the bitmap. - */ - sk_sp tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - SkBudgeted::kYes, surfaceInfo); - - if (!tmpSurface.get()) { - surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType); - tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes, - surfaceInfo); - if (!tmpSurface.get()) { - ALOGW("Unable to readback GPU contents into the provided bitmap"); - return false; - } - } - - Layer* layer = deferredLayer->backingLayer(); - const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height()); - if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer, - &dstRect)) { - sk_sp tmpImage = tmpSurface->makeImageSnapshot(); - if (tmpImage->readPixels(surfaceInfo, bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) { - bitmap->notifyPixelsChanged(); - return true; - } - - // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into 8888 - // and then draw that into the destination format before giving up. - SkBitmap tmpBitmap; - SkImageInfo bitmapInfo = - SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType()); - if (tmpBitmap.tryAllocPixels(bitmapInfo) && - tmpImage->readPixels(bitmapInfo, tmpBitmap.getPixels(), tmpBitmap.rowBytes(), 0, 0)) { - SkCanvas canvas(*bitmap); - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); - canvas.drawBitmap(tmpBitmap, 0, 0, &paint); - bitmap->notifyPixelsChanged(); - return true; - } - } - - return false; -} - DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); return new DeferredLayerUpdater(mRenderThread.renderState()); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 2e2e1522b717..808685ad4460 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -40,7 +40,6 @@ public: FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; - bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, renderthread::ColorMode colorMode) override; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp deleted file mode 100644 index f2f5056bb195..000000000000 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp +++ /dev/null @@ -1,147 +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 "SkiaOpenGLReadback.h" - -#include -#include -#include -#include -#include -#include -#include -#include "DeviceInfo.h" -#include "Matrix.h" -#include "Properties.h" -#include "utils/MathUtils.h" - -using namespace android::uirenderer::renderthread; - -namespace android { -namespace uirenderer { -namespace skiapipeline { - -CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, - int imgWidth, int imgHeight, const Rect& srcRect, - SkBitmap* bitmap) { - GLuint sourceTexId; - glGenTextures(1, &sourceTexId); - glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); - - sk_sp grContext = sk_ref_sp(mRenderThread.getGrContext()); - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - sk_sp glInterface(GrGLCreateNativeInterface()); - LOG_ALWAYS_FATAL_IF(!glInterface.get()); - grContext = GrContext::MakeGL(std::move(glInterface)); - } else { - grContext->resetContext(); - } - - if (bitmap->colorType() == kRGBA_F16_SkColorType && - !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) { - ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); - return CopyResult::DestinationInvalid; - } - - GrGLTextureInfo externalTexture; - externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES; - externalTexture.fID = sourceTexId; - switch (bitmap->colorType()) { - case kRGBA_F16_SkColorType: - externalTexture.fFormat = GL_RGBA16F; - break; - case kN32_SkColorType: - default: - externalTexture.fFormat = GL_RGBA8; - break; - } - - GrBackendTexture backendTexture(imgWidth, imgHeight, GrMipMapped::kNo, externalTexture); - - CopyResult copyResult = CopyResult::UnknownError; - sk_sp image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture, - kTopLeft_GrSurfaceOrigin, - bitmap->colorType())); - if (image) { - int displayedWidth = imgWidth, displayedHeight = imgHeight; - // If this is a 90 or 270 degree rotation we need to swap width/height to get the device - // size. - if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) { - std::swap(displayedWidth, displayedHeight); - } - SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height()); - SkRect skiaSrcRect = srcRect.toSkRect(); - if (skiaSrcRect.isEmpty()) { - skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight); - } - bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight)); - - if (srcNotEmpty) { - SkMatrix textureMatrixInv; - imgTransform.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; - flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); - textureMatrixInv.preConcat(flipV); - textureMatrixInv.preScale(1.0f / displayedWidth, 1.0f / displayedHeight); - textureMatrixInv.postScale(imgWidth, imgHeight); - SkMatrix textureMatrix; - if (!textureMatrixInv.invert(&textureMatrix)) { - textureMatrix = textureMatrixInv; - } - - textureMatrixInv.mapRect(&skiaSrcRect); - textureMatrixInv.mapRect(&skiaDestRect); - - // we render in an offscreen buffer to scale and to avoid an issue b/62262733 - // with reading incorrect data from EGLImage backed SkImage (likely a driver bug) - sk_sp scaledSurface = - SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, bitmap->info()); - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); - // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage - // is codified by tests using golden images like DecodeAccuracyTest. - bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) - && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height()); - if (!disableFilter) { - paint.setFilterQuality(kLow_SkFilterQuality); - } - scaledSurface->getCanvas()->concat(textureMatrix); - scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint, - SkCanvas::kFast_SrcRectConstraint); - - image = scaledSurface->makeImageSnapshot(); - - if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) { - bitmap->notifyPixelsChanged(); - copyResult = CopyResult::Success; - } - } - } - - // make sure that we have deleted the texture (in the SkImage) before we - // destroy the EGLImage that it was created from - image.reset(); - glFinish(); - - return copyResult; -} - -} /* namespace skiapipeline */ -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h deleted file mode 100644 index 1ce4773e7d67..000000000000 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h +++ /dev/null @@ -1,37 +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. - */ - -#pragma once - -#include "EglReadback.h" - -namespace android { -namespace uirenderer { -namespace skiapipeline { - -class SkiaOpenGLReadback : public EglReadback { -public: - SkiaOpenGLReadback(renderthread::RenderThread& thread) : EglReadback(thread) {} - -protected: - virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, - int imgWidth, int imgHeight, const Rect& srcRect, - SkBitmap* bitmap) override; -}; - -} /* namespace skiapipeline */ -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 5f2eee4523fc..611a34c069d4 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -108,11 +108,6 @@ bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect return *requireSwap; } -bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { - // TODO: implement copyLayerInto for vulkan. - return false; -} - DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { mVkManager.initialize(); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 7806b42e03dc..900b054e35bd 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -38,7 +38,6 @@ public: FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; - bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior, renderthread::ColorMode colorMode) override; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h deleted file mode 100644 index 65b89d617f7b..000000000000 --- a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h +++ /dev/null @@ -1,44 +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 "Readback.h" - -namespace android { -namespace uirenderer { -namespace skiapipeline { - -class SkiaVulkanReadback : public Readback { -public: - SkiaVulkanReadback(renderthread::RenderThread& thread) : Readback(thread) {} - - virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, - SkBitmap* bitmap) override { - //TODO: implement Vulkan readback. - return CopyResult::UnknownError; - } - - virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, - SkBitmap* bitmap) override { - //TODO: implement Vulkan readback. - return CopyResult::UnknownError; - } -}; - -} /* namespace skiapipeline */ -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8b07d1dadeb6..727cef3035f5 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -562,10 +562,6 @@ void CanvasContext::buildLayer(RenderNode* node) { mPrefetchedLayers.insert(node); } -bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { - return mRenderPipeline->copyLayerInto(layer, bitmap); -} - void CanvasContext::destroyHardwareResources() { stopDrawing(); if (mRenderPipeline->isContextReady()) { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 8ca54af13baa..02ee72f05f04 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -135,7 +135,6 @@ public: void prepareAndDraw(RenderNode* node); void buildLayer(RenderNode* node); - bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); void markLayerInUse(RenderNode* node); void destroyHardwareResources(); diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index b94a7588a507..b7b7853e6ed7 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -60,7 +60,6 @@ public: FrameInfoVisualizer* profiler) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; - virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0; virtual void onStop() = 0; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index e3807e634890..7a5348ac85d9 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -160,8 +160,10 @@ void RenderProxy::buildLayer(RenderNode* node) { } bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) { - return mRenderThread.queue().runSync( - [&]() -> bool { return mContext->copyLayerInto(layer, &bitmap); }); + auto& thread = RenderThread::getInstance(); + return thread.queue().runSync( + [&]() -> bool { return thread.readback().copyLayerInto(layer, &bitmap) + == CopyResult::Success; }); } void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) { @@ -331,14 +333,14 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) { } } -int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) { +int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) { RenderThread& thread = RenderThread::getInstance(); if (gettid() == thread.getTid()) { // TODO: fix everything that hits this. We should never be triggering a readback ourselves. - return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap); + return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap); } else { return thread.queue().runSync([&]() -> int { - return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap); + return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap); }); } } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index c2964a4e3515..969ad00d7443 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -125,7 +125,7 @@ public: int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); - static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap); + static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap); static void onBitmapDestroyed(uint32_t pixelRefId); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 2322fbf21f27..7258a0aa4f02 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -19,13 +19,12 @@ #include "CanvasContext.h" #include "DeviceInfo.h" #include "EglManager.h" +#include "Readback.h" #include "RenderProxy.h" #include "VulkanManager.h" #include "hwui/Bitmap.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" -#include "pipeline/skia/SkiaOpenGLReadback.h" #include "pipeline/skia/SkiaVulkanPipeline.h" -#include "pipeline/skia/SkiaVulkanReadback.h" #include "renderstate/RenderState.h" #include "utils/FatVector.h" #include "utils/TimeUtils.h" @@ -235,18 +234,7 @@ void RenderThread::dumpGraphicsMemory(int fd) { Readback& RenderThread::readback() { if (!mReadback) { - auto renderType = Properties::getRenderPipelineType(); - switch (renderType) { - case RenderPipelineType::SkiaGL: - mReadback = new skiapipeline::SkiaOpenGLReadback(*this); - break; - case RenderPipelineType::SkiaVulkan: - mReadback = new skiapipeline::SkiaVulkanReadback(*this); - break; - default: - LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType); - break; - } + mReadback = new Readback(*this); } return *mReadback; -- cgit v1.2.3-59-g8ed1b From 28a4d9935c214374cbd487d3419abb80f57d6f80 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Thu, 20 Sep 2018 13:37:24 -0400 Subject: Refactor RenderState and remove unused functionality. Test: hwui_unit_tests still pass Change-Id: Ie943671535ab8c5da1bac05985e815e0cb842dc1 --- libs/hwui/DeferredLayerUpdater.cpp | 8 ++- libs/hwui/DeferredLayerUpdater.h | 6 +- libs/hwui/RenderNode.cpp | 6 +- libs/hwui/hwui/Bitmap.cpp | 2 - libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 1 + libs/hwui/pipeline/skia/SkiaPipeline.cpp | 4 -- libs/hwui/pipeline/skia/SkiaPipeline.h | 2 - libs/hwui/renderstate/RenderState.cpp | 76 +------------------------- libs/hwui/renderstate/RenderState.h | 55 +------------------ libs/hwui/renderthread/CanvasContext.cpp | 7 --- libs/hwui/renderthread/CanvasContext.h | 6 -- libs/hwui/renderthread/RenderProxy.cpp | 7 --- libs/hwui/renderthread/RenderProxy.h | 2 - libs/hwui/tests/common/TestUtils.h | 1 + 14 files changed, 22 insertions(+), 161 deletions(-) (limited to 'libs/hwui/DeferredLayerUpdater.cpp') diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 837d5461d2a8..b772e5b87f2a 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -29,12 +29,16 @@ DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) , mGLContextAttached(false) , mUpdateTexImage(false) , mLayer(nullptr) { - renderState.registerDeferredLayerUpdater(this); + renderState.registerContextCallback(this); } DeferredLayerUpdater::~DeferredLayerUpdater() { setTransform(nullptr); - mRenderState.unregisterDeferredLayerUpdater(this); + mRenderState.removeContextCallback(this); + destroyLayer(); +} + +void DeferredLayerUpdater::onContextDestroyed() { destroyLayer(); } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index 4c323b861002..b2c5131dd613 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -27,6 +27,7 @@ #include #include +#include "renderstate/RenderState.h" #include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" @@ -38,7 +39,7 @@ class RenderState; // Container to hold the properties a layer should be set to at the start // of a render pass -class DeferredLayerUpdater : public VirtualLightRefBase { +class DeferredLayerUpdater : public VirtualLightRefBase, public IGpuContextCallback { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. @@ -98,6 +99,9 @@ public: void destroyLayer(); +protected: + void onContextDestroyed() override; + private: RenderState& mRenderState; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d5afb2011429..896b4340f1b8 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -158,7 +158,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) || CC_UNLIKELY(!properties().fitsOnLayer())) { if (CC_UNLIKELY(hasLayer())) { - renderthread::CanvasContext::destroyLayer(this); + this->setLayerSurface(nullptr); } return; } @@ -313,7 +313,7 @@ void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) { void RenderNode::destroyHardwareResources(TreeInfo* info) { if (hasLayer()) { - renderthread::CanvasContext::destroyLayer(this); + this->setLayerSurface(nullptr); } setStagingDisplayList(nullptr); @@ -323,7 +323,7 @@ void RenderNode::destroyHardwareResources(TreeInfo* info) { void RenderNode::destroyLayers() { if (hasLayer()) { - renderthread::CanvasContext::destroyLayer(this); + this->setLayerSurface(nullptr); } if (mDisplayList) { mDisplayList->updateChildren([](RenderNode* child) { child->destroyLayers(); }); diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 440620a6a417..f1c10d1570a4 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -230,8 +230,6 @@ Bitmap::~Bitmap() { mPixelStorage.hardware.buffer = nullptr; break; } - - android::uirenderer::renderthread::RenderProxy::onBitmapDestroyed(getStableID()); } bool Bitmap::hasHardwareMipMap() const { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index e586be1d0708..e8bf4922cd46 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -21,6 +21,7 @@ #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "hwui/Bitmap.h" +#include "private/hwui/DrawGlInfo.h" #include "renderstate/RenderState.h" #include "renderthread/EglManager.h" #include "renderthread/Frame.h" diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 7f8abb8afa97..2dfe7c71ca1b 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -200,10 +200,6 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator return false; } -void SkiaPipeline::destroyLayer(RenderNode* node) { - node->setLayerSurface(nullptr); -} - void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { GrContext* context = thread.getGrContext(); if (context) { diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index b78dea1a5c87..ee9158c5ffc1 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -54,8 +54,6 @@ public: std::vector* getVectorDrawables() { return &mVectorDrawables; } - static void destroyLayer(RenderNode* node); - static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap); void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index b595ab859a61..fad9440be73f 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -14,90 +14,28 @@ * limitations under the License. */ #include "renderstate/RenderState.h" -#include -#include "DeferredLayerUpdater.h" -#include "Snapshot.h" -#include "renderthread/CanvasContext.h" -#include "renderthread/EglManager.h" -#include "utils/GLUtils.h" - -#include - -#include +#include "renderthread/RenderThread.h" +#include "GpuMemoryTracker.h" namespace android { namespace uirenderer { -RenderState::RenderState(renderthread::RenderThread& thread) - : mRenderThread(thread), mViewportWidth(0), mViewportHeight(0), mFramebuffer(0) { +RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thread) { mThreadId = pthread_self(); } -RenderState::~RenderState() { -} - void RenderState::onContextCreated() { GpuMemoryTracker::onGpuContextCreated(); } -static void destroyLayerInUpdater(DeferredLayerUpdater* layerUpdater) { - layerUpdater->destroyLayer(); -} - void RenderState::onContextDestroyed() { - std::for_each(mActiveLayerUpdaters.begin(), mActiveLayerUpdaters.end(), destroyLayerInUpdater); for(auto callback : mContextCallbacks) { callback->onContextDestroyed(); } GpuMemoryTracker::onGpuContextDestroyed(); } -GrContext* RenderState::getGrContext() const { - return mRenderThread.getGrContext(); -} - -void RenderState::onBitmapDestroyed(uint32_t pixelRefId) { - // DEAD CODE -} - -void RenderState::setViewport(GLsizei width, GLsizei height) { - mViewportWidth = width; - mViewportHeight = height; - glViewport(0, 0, mViewportWidth, mViewportHeight); -} - -void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) { - *outWidth = mViewportWidth; - *outHeight = mViewportHeight; -} - -void RenderState::bindFramebuffer(GLuint fbo) { - if (mFramebuffer != fbo) { - mFramebuffer = fbo; - glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - } -} - -GLuint RenderState::createFramebuffer() { - GLuint ret; - glGenFramebuffers(1, &ret); - return ret; -} - -void RenderState::deleteFramebuffer(GLuint fbo) { - if (mFramebuffer == fbo) { - // GL defines that deleting the currently bound FBO rebinds FBO 0. - // Reflect this in our cached value. - mFramebuffer = 0; - } - glDeleteFramebuffers(1, &fbo); -} - -void RenderState::debugOverdraw(bool enable, bool clear) { - // DEAD CODE -} - void RenderState::postDecStrong(VirtualLightRefBase* object) { if (pthread_equal(mThreadId, pthread_self())) { object->decStrong(nullptr); @@ -110,13 +48,5 @@ void RenderState::postDecStrong(VirtualLightRefBase* object) { // Render /////////////////////////////////////////////////////////////////////////////// -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 dee02e9873d8..ff5d02fe359a 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -18,26 +18,16 @@ #include "utils/Macros.h" -#include -#include -#include -#include -#include -#include #include #include -class GrContext; - namespace android { namespace uirenderer { class Layer; -class DeferredLayerUpdater; namespace renderthread { class CacheManager; -class CanvasContext; class RenderThread; } @@ -55,69 +45,30 @@ class RenderState { friend class renderthread::CacheManager; public: - void onBitmapDestroyed(uint32_t pixelRefId); - - void setViewport(GLsizei width, GLsizei height); - void getViewport(GLsizei* outWidth, GLsizei* outHeight); - - void bindFramebuffer(GLuint fbo); - GLuint getFramebuffer() { return mFramebuffer; } - GLuint createFramebuffer(); - void deleteFramebuffer(GLuint fbo); - - void debugOverdraw(bool enable, bool clear); - void registerContextCallback(IGpuContextCallback* cb) { mContextCallbacks.insert(cb); } void removeContextCallback(IGpuContextCallback* cb) { mContextCallbacks.erase(cb); } void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } void unregisterLayer(Layer* layer) { mActiveLayers.erase(layer); } - void registerCanvasContext(renderthread::CanvasContext* context) { - mRegisteredContexts.insert(context); - } - - void unregisterCanvasContext(renderthread::CanvasContext* context) { - mRegisteredContexts.erase(context); - } - - void registerDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) { - mActiveLayerUpdaters.insert(layerUpdater); - } - - void unregisterDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) { - mActiveLayerUpdaters.erase(layerUpdater); - } - // TODO: This system is a little clunky feeling, this could use some // more thinking... void postDecStrong(VirtualLightRefBase* object); - GrContext* getGrContext() const; - - void dump(); - - renderthread::RenderThread& getRenderThread(); + renderthread::RenderThread& getRenderThread() const { return mRenderThread; } private: explicit RenderState(renderthread::RenderThread& thread); - ~RenderState(); + ~RenderState() {} // Context notifications are only to be triggered by renderthread::RenderThread void onContextCreated(); void onContextDestroyed(); - renderthread::RenderThread& mRenderThread; - std::set mContextCallbacks; std::set mActiveLayers; - std::set mActiveLayerUpdaters; - std::set mRegisteredContexts; - - GLsizei mViewportWidth; - GLsizei mViewportHeight; - GLuint mFramebuffer; + renderthread::RenderThread& mRenderThread; pthread_t mThreadId; }; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index ea6a851419c0..c8c394a72541 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -27,7 +27,6 @@ #include "pipeline/skia/SkiaOpenGLPipeline.h" #include "pipeline/skia/SkiaPipeline.h" #include "pipeline/skia/SkiaVulkanPipeline.h" -#include "renderstate/RenderState.h" #include "utils/GLUtils.h" #include "utils/TimeUtils.h" #include "../Properties.h" @@ -76,10 +75,6 @@ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent, return nullptr; } -void CanvasContext::destroyLayer(RenderNode* node) { - skiapipeline::SkiaPipeline::destroyLayer(node); -} - void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) { ATRACE_CALL(); auto renderType = Properties::getRenderPipelineType(); @@ -113,13 +108,11 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mRenderPipeline(std::move(renderPipeline)) { rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); - mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); } CanvasContext::~CanvasContext() { destroy(); - mRenderThread.renderState().unregisterCanvasContext(this); for (auto& node : mRenderNodes) { node->clearRoot(); } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 8448788eb9a6..2315cb9c73f9 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -96,12 +96,6 @@ public: */ void unpinImages() { mRenderPipeline->unpinImages(); } - /** - * Destroy any layers that have been attached to the provided RenderNode removing - * any state that may have been set during createOrUpdateLayer(). - */ - static void destroyLayer(RenderNode* node); - static void invokeFunctor(const RenderThread& thread, Functor* functor); static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 7a5348ac85d9..6106e24c093b 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -345,13 +345,6 @@ int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) { } } -void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) { - if (!RenderThread::hasInstance()) return; - RenderThread& thread = RenderThread::getInstance(); - thread.queue().post( - [&thread, pixelRefId]() { thread.renderState().onBitmapDestroyed(pixelRefId); }); -} - void RenderProxy::disableVsync() { Properties::disableVsync = true; } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 969ad00d7443..d22f56ef38fd 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -127,8 +127,6 @@ public: static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap); - static void onBitmapDestroyed(uint32_t pixelRefId); - ANDROID_API static void disableVsync(); static void repackVectorDrawableAtlas(); diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index c35f512553d8..a00b8db3c617 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3-59-g8ed1b From d01b5916d8b512ee4df8d749022c10419b58b4b2 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Fri, 19 Oct 2018 15:55:33 -0400 Subject: Set the color space to sRGB on the Surface and remove colorFilter. Also for a canvas wrapping a bitmap the colorspace of the bitmap will be used to correctly blend content. Test: CtsUiRenderingTestCases Bug: 111436479 Change-Id: I63ad7a30605a7f725cc0ef4716d42ea978fb03e3 --- core/jni/android/graphics/Shader.cpp | 6 +- libs/hwui/DeferredLayerUpdater.cpp | 8 +- libs/hwui/DeferredLayerUpdater.h | 2 +- libs/hwui/Layer.cpp | 31 ----- libs/hwui/Layer.h | 22 +--- libs/hwui/Readback.cpp | 145 +++++---------------- libs/hwui/Readback.h | 4 +- libs/hwui/SkiaCanvas.cpp | 75 ++--------- libs/hwui/SkiaCanvas.h | 1 - libs/hwui/hwui/Bitmap.cpp | 14 +- libs/hwui/hwui/Bitmap.h | 8 +- libs/hwui/pipeline/skia/LayerDrawable.cpp | 2 +- libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 3 +- libs/hwui/pipeline/skia/SkiaPipeline.cpp | 5 +- libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 20 ++- libs/hwui/surfacetexture/ImageConsumer.cpp | 14 +- libs/hwui/surfacetexture/ImageConsumer.h | 7 +- libs/hwui/surfacetexture/SurfaceTexture.cpp | 4 +- libs/hwui/surfacetexture/SurfaceTexture.h | 4 +- libs/hwui/tests/common/TestUtils.cpp | 4 +- libs/hwui/tests/common/scenes/BitmapShaders.cpp | 3 +- .../common/scenes/HwBitmapInCompositeShader.cpp | 3 +- libs/hwui/tests/unit/CacheManagerTests.cpp | 3 +- libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp | 2 +- libs/hwui/tests/unit/SkiaCanvasTests.cpp | 6 +- 25 files changed, 92 insertions(+), 304 deletions(-) (limited to 'libs/hwui/DeferredLayerUpdater.cpp') diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 68f5bef18de1..ed6a84b35670 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -64,11 +64,10 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j jint tileModeX, jint tileModeY) { const SkMatrix* matrix = reinterpret_cast(matrixPtr); sk_sp image; - sk_sp colorFilter; if (jbitmap) { // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise, // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. - image = android::bitmap::toBitmap(env, jbitmap).makeImage(&colorFilter); + image = android::bitmap::toBitmap(env, jbitmap).makeImage(); } if (!image.get()) { @@ -81,9 +80,6 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j if (matrix) { shader = shader->makeWithLocalMatrix(*matrix); } - if(colorFilter) { - shader = shader->makeWithColorFilter(colorFilter); - } ThrowIAE_IfNull(env, shader.get()); return reinterpret_cast(shader.release()); diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index b772e5b87f2a..3bee3018d36e 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -85,19 +85,18 @@ void DeferredLayerUpdater::apply() { 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, + layerImage = mSurfaceTexture->dequeueImage(textureTransform, &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); + updateLayer(forceFilter, textureTransform, layerImage); } } @@ -109,12 +108,11 @@ void DeferredLayerUpdater::apply() { } void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, - android_dataspace dataspace, const sk_sp& layerImage) { + const sk_sp& layerImage) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); mLayer->getTexTransform() = textureTransform; - mLayer->setDataSpace(dataspace); mLayer->setImage(layerImage); } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index b2c5131dd613..a91c111933c4 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -95,7 +95,7 @@ public: void detachSurfaceTexture(); void updateLayer(bool forceFilter, const SkMatrix& textureTransform, - android_dataspace dataspace, const sk_sp& layerImage); + const sk_sp& layerImage); void destroyLayer(); diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 32aaa54e696c..d0df200d2fa6 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -33,7 +33,6 @@ Layer::Layer(RenderState& renderState, sk_sp colorFilter, int alp // TODO: This is a violation of Android's typical ref counting, but it // preserves the old inc/dec ref locations. This should be changed... incStrong(nullptr); - buildColorSpaceWithFilter(); renderState.registerLayer(this); texTransform.setIdentity(); transform.setIdentity(); @@ -43,36 +42,6 @@ Layer::~Layer() { mRenderState.unregisterLayer(this); } -void Layer::setColorFilter(sk_sp filter) { - if (filter != mColorFilter) { - mColorFilter = filter; - buildColorSpaceWithFilter(); - } -} - -void Layer::setDataSpace(android_dataspace dataspace) { - if (dataspace != mCurrentDataspace) { - mCurrentDataspace = dataspace; - buildColorSpaceWithFilter(); - } -} - -void Layer::buildColorSpaceWithFilter() { - sk_sp colorSpaceFilter; - sk_sp colorSpace = DataSpaceToColorSpace(mCurrentDataspace); - if (colorSpace && !colorSpace->isSRGB()) { - colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace); - } - - if (mColorFilter && colorSpaceFilter) { - mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter); - } else if (colorSpaceFilter) { - mColorSpaceWithFilter = colorSpaceFilter; - } else { - mColorSpaceWithFilter = mColorFilter; - } -} - void Layer::postDecStrong() { mRenderState.postDecStrong(this); } diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index e4f96e914c36..98600dbf1eea 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -69,15 +69,9 @@ public: SkBlendMode getMode() const; - inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); } + inline sk_sp getColorFilter() const { return mColorFilter; } - void setColorFilter(sk_sp filter); - - void setDataSpace(android_dataspace dataspace); - - void setColorSpace(sk_sp colorSpace); - - inline sk_sp getColorSpaceWithFilter() const { return mColorSpaceWithFilter; } + void setColorFilter(sk_sp filter) { mColorFilter = filter; }; inline SkMatrix& getTexTransform() { return texTransform; } @@ -98,23 +92,11 @@ protected: RenderState& mRenderState; private: - void buildColorSpaceWithFilter(); - /** * Color filter used to draw this layer. Optional. */ sk_sp mColorFilter; - /** - * Colorspace of the contents of the layer. Optional. - */ - android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN; - - /** - * A color filter that is the combination of the mColorFilter and mColorSpace. Optional. - */ - sk_sp mColorSpaceWithFilter; - /** * Indicates raster data backing the layer is scaled, requiring filtration. */ diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 80f2b5714659..2a488378e3a8 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -66,13 +66,10 @@ CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBi sk_sp colorSpace = DataSpaceToColorSpace(static_cast(surface.getBuffersDataSpace())); - sk_sp colorSpaceFilter; - if (colorSpace && !colorSpace->isSRGB()) { - colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace); - } sk_sp image = SkImage::MakeFromAHardwareBuffer( - reinterpret_cast(sourceBuffer.get()), kPremul_SkAlphaType); - return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap); + reinterpret_cast(sourceBuffer.get()), + kPremul_SkAlphaType, colorSpace); + return copyImageInto(image, texTransform, srcRect, bitmap); } CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) { @@ -83,20 +80,7 @@ CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) { transform.loadScale(1, -1, 1); transform.translate(0, -1); - // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage". - // TODO: When this was attempted, it resulted in instability. - sk_sp colorSpaceFilter; - sk_sp colorSpace = hwBitmap->info().refColorSpace(); - if (colorSpace && !colorSpace->isSRGB()) { - colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace); - } - sk_sp image = SkImage::MakeFromAHardwareBuffer( - reinterpret_cast(hwBitmap->graphicBuffer()), kPremul_SkAlphaType); - - // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format - // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for - // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test. - return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap); + return copyImageInto(hwBitmap->makeImage(), transform, srcRect, bitmap); } CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) { @@ -118,8 +102,7 @@ CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap return copyResult; } -CopyResult Readback::copyImageInto(const sk_sp& image, - sk_sp& colorSpaceFilter, Matrix4& texTransform, +CopyResult Readback::copyImageInto(const sk_sp& image, Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap) { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { mRenderThread.requireGlContext(); @@ -157,11 +140,7 @@ CopyResult Readback::copyImageInto(const sk_sp& image, return copyResult; } - // See Readback::copyLayerInto for an overview of color space conversion. - // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder). - // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion. - // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally. - Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc); + Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc); bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height()); layer.setForceFilter(!disableFilter); @@ -177,38 +156,6 @@ CopyResult Readback::copyImageInto(const sk_sp& image, bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect, SkBitmap* bitmap) { - /* - * In the past only TextureView readback was setting the temporary surface color space to null. - * Now all 3 readback flows are drawing into a SkSurface with null color space. - * At readback there are 3 options to convert the source image color space to the destination - * color space requested in "bitmap->info().colorSpace()": - * 1. Set color space for temporary surface render target to null (disables color management), - * colorspace tag from source SkImage is ignored by Skia, - * convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter, - * do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2", - * read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion. - * - * 2. Set color space for temporary surface render target to SRGB (not nullptr), - * colorspace tag on the source SkImage is used by Skia to enable conversion, - * convert SkImage to SRGB at draw time with drawImage (no filters), - * do a readback from temporary SkSurface, which will do a color conversion from SRGB to - * bitmap->info().colorSpace() on the CPU. - * - * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(), - * colorspace tag on the source SkImage is used by Skia to enable conversion, - * convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters), - * do a readback from SkSurface, which will not do any color conversion, because - * surface was created with the same color space as the "bitmap". - * - * Option 1 is used for all readback flows. - * Options 2 and 3 are new, because skia added support for non-SRGB render targets without - * linear blending. - * TODO: evaluate if options 2 or 3 for color space conversion are better. - */ - - // drop the colorSpace from the temporary surface. - SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr); - /* This intermediate surface is present to work around a bug in SwiftShader that * prevents us from reading the contents of the layer's texture directly. The * workaround involves first rendering that texture into an intermediate buffer and @@ -217,70 +164,44 @@ bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* * with reading incorrect data from EGLImage backed SkImage (likely a driver bug). */ sk_sp tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), - SkBudgeted::kYes, surfaceInfo); + SkBudgeted::kYes, bitmap->info()); + // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we + // attempt to do the intermediate rendering step in 8888 if (!tmpSurface.get()) { - surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType); + SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType); tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes, - surfaceInfo); + tmpInfo); if (!tmpSurface.get()) { - ALOGW("Unable to readback GPU contents into the provided bitmap"); + ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap"); return false; } } - if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(), - tmpSurface->getCanvas(), layer, srcRect, dstRect, - false)) { - // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on - // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has - // disabled color conversion. - SkColorSpace* destColorSpace = bitmap->info().colorSpace(); - SkBitmap tempSRGBBitmap; - SkBitmap tmpN32Bitmap; - SkBitmap* bitmapInSRGB; - if (destColorSpace && !destColorSpace->isSRGB()) { - tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB())); - bitmapInSRGB = &tempSRGBBitmap; // Need to convert latter from SRGB to non-SRGB. - } else { - bitmapInSRGB = bitmap; // No need for color conversion - write directly into output. - } - bool success = false; - - // TODO: does any of the readbacks below clamp F16 exSRGB? - // Readback into a SRGB SkBitmap. - if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(), - bitmapInSRGB->rowBytes(), 0, 0)) { - success = true; - } else { - // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into - // 8888 and then convert that into the destination format before giving up. - SkImageInfo bitmapInfo = - SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(), - SkColorSpace::MakeSRGB()); - if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) && - tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(), - tmpN32Bitmap.rowBytes(), 0, 0)) { - success = true; - bitmapInSRGB = &tmpN32Bitmap; - } - } - - if (success) { - if (bitmapInSRGB != bitmap) { - // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to - // destination bitmap color format if needed. - if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(), - bitmap->rowBytes(), 0, 0)) { - return false; - } - } - bitmap->notifyPixelsChanged(); - return true; + if (!skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(), + tmpSurface->getCanvas(), layer, srcRect, dstRect, + false)) { + ALOGW("Unable to draw content from GPU into the provided bitmap"); + return false; + } + + if (!tmpSurface->readPixels(*bitmap, 0, 0)) { + // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into + // 8888 and then convert that into the destination format before giving up. + SkBitmap tmpBitmap; + SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType); + if (bitmap->info().colorType() == SkColorType::kN32_SkColorType || + !tmpBitmap.tryAllocPixels(tmpInfo) || + !tmpSurface->readPixels(tmpBitmap, 0, 0) || + !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(), + bitmap->rowBytes(), 0, 0)) { + ALOGW("Unable to convert content into the provided bitmap"); + return false; } } - return false; + bitmap->notifyPixelsChanged(); + return true; } } /* namespace uirenderer */ diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index d9e10cedc0e8..e86a8136cfa3 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -54,8 +54,8 @@ public: CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); private: - CopyResult copyImageInto(const sk_sp& image, sk_sp& colorSpaceFilter, - Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap); + CopyResult copyImageInto(const sk_sp& image, Matrix4& texTransform, + const Rect& srcRect, SkBitmap* bitmap); bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect, SkBitmap* bitmap); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 9c707bab95f1..154338604663 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -60,19 +59,8 @@ SkiaCanvas::SkiaCanvas() {} SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {} SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) { - sk_sp cs = bitmap.refColorSpace(); - mCanvasOwned = - std::unique_ptr(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy)); - if (cs.get() == nullptr || cs->isSRGB()) { - mCanvas = mCanvasOwned.get(); - } else { - /** The wrapper is needed if we are drawing into a non-sRGB destination, since - * we need to transform all colors (not just bitmaps via filters) into the - * destination's colorspace. - */ - mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs)); - mCanvas = mCanvasWrapper.get(); - } + mCanvasOwned = std::unique_ptr(new SkCanvas(bitmap)); + mCanvas = mCanvasOwned.get(); } SkiaCanvas::~SkiaCanvas() {} @@ -81,7 +69,6 @@ void SkiaCanvas::reset(SkCanvas* skiaCanvas) { if (mCanvas != skiaCanvas) { mCanvas = skiaCanvas; mCanvasOwned.reset(); - mCanvasWrapper.reset(); } mSaveStack.reset(nullptr); } @@ -91,18 +78,9 @@ void SkiaCanvas::reset(SkCanvas* skiaCanvas) { // ---------------------------------------------------------------------------- void SkiaCanvas::setBitmap(const SkBitmap& bitmap) { - sk_sp cs = bitmap.refColorSpace(); - std::unique_ptr newCanvas = - std::unique_ptr(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy)); - std::unique_ptr newCanvasWrapper; - if (cs.get() != nullptr && !cs->isSRGB()) { - newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs)); - } - // deletes the previously owned canvas (if any) - mCanvasOwned = std::move(newCanvas); - mCanvasWrapper = std::move(newCanvasWrapper); - mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get(); + mCanvasOwned.reset(new SkCanvas(bitmap)); + mCanvas = mCanvasOwned.get(); // clean up the old save stack mSaveStack.reset(nullptr); @@ -547,40 +525,14 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons // Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint, - sk_sp colorSpaceFilter) const { - /* We don't apply the colorSpace filter if this canvas is already wrapped with - * a SkColorSpaceXformCanvas since it already takes care of converting the - * contents of the bitmap into the appropriate colorspace. The mCanvasWrapper - * should only be used if this canvas is backed by a surface/bitmap that is known - * to have a non-sRGB colorspace. - */ - if (!mCanvasWrapper && colorSpaceFilter) { - SkPaint& tmpPaint = paint.writeable(); - if (tmpPaint.getColorFilter()) { - tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(), - std::move(colorSpaceFilter))); - LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter()); - } else { - tmpPaint.setColorFilter(std::move(colorSpaceFilter)); - } - } - return filterPaint(std::move(paint)); -} - void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); - mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter))); + mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint)); } void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); - - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); - mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter))); + mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint)); } void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, @@ -589,9 +541,7 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); - mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)), + mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint), SkCanvas::kFast_SrcRectConstraint); } @@ -673,13 +623,9 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, PaintCoW paintCoW(paint); SkPaint& tmpPaint = paintCoW.writeable(); - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); + sk_sp image = bitmap.makeImage(); sk_sp shader = image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - if (colorFilter) { - shader = shader->makeWithColorFilter(std::move(colorFilter)); - } tmpPaint.setShader(std::move(shader)); mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, @@ -710,10 +656,7 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); - mCanvas->drawImageLattice(image.get(), lattice, dst, - filterBitmap(paint, std::move(colorFilter))); + mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint)); } double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) { diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 3a877cf84010..24d9c08ec1c6 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -232,7 +232,6 @@ private: class Clip; - std::unique_ptr mCanvasWrapper; // might own a wrapper on the canvas std::unique_ptr mCanvasOwned; // might own a canvas we allocated SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us // unless it is the same as mCanvasOwned.get() diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 75a6e722dd8a..6c77f9ee1845 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -287,14 +286,8 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) { void Bitmap::getSkBitmap(SkBitmap* outBitmap) { if (isHardware()) { - outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(), - info().colorType(), info().alphaType(), nullptr)); + outBitmap->allocPixels(mInfo); uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap); - if (mInfo.colorSpace()) { - sk_sp pixelRef = sk_ref_sp(outBitmap->pixelRef()); - outBitmap->setInfo(mInfo); - outBitmap->setPixelRef(std::move(pixelRef), 0, 0); - } return; } outBitmap->setInfo(mInfo, rowBytes()); @@ -313,7 +306,7 @@ GraphicBuffer* Bitmap::graphicBuffer() { return nullptr; } -sk_sp Bitmap::makeImage(sk_sp* outputColorFilter) { +sk_sp Bitmap::makeImage() { sk_sp image = mImage; if (!image) { SkASSERT(!isHardware()); @@ -325,9 +318,6 @@ sk_sp Bitmap::makeImage(sk_sp* outputColorFilter) { // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here. image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode); } - if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) { - *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace()); - } return image; } diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 238c764cdea6..d446377ec1d9 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -105,14 +105,8 @@ public: * Creates or returns a cached SkImage and is safe to be invoked from either * the UI or RenderThread. * - * @param outputColorFilter is a required param that will be populated by - * this function if the bitmap's colorspace is not sRGB. If populated the - * filter will convert colors from the bitmaps colorspace into sRGB. It - * is the callers responsibility to use this colorFilter when drawing - * this image into any destination that is presumed to be sRGB (i.e. a - * buffer that has no colorspace defined). */ - sk_sp makeImage(sk_sp* outputColorFilter); + sk_sp makeImage(); static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes); diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index 13d2dae8e281..41788b6ba55d 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -70,7 +70,7 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer SkPaint paint; paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); - paint.setColorFilter(layer->getColorSpaceWithFilter()); + paint.setColorFilter(layer->getColorFilter()); const bool nonIdentityMatrix = !matrix.isIdentity(); if (nonIdentityMatrix) { canvas->save(); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index d401b385075e..d6adaf8087c4 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -97,7 +97,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con SkASSERT(mRenderThread.getGrContext() != nullptr); sk_sp surface(SkSurface::MakeFromBackendRenderTarget( mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType, - nullptr, &props)); + mSurfaceColorSpace, &props)); SkiaPipeline::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); @@ -172,6 +172,7 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, } else if (colorMode == ColorMode::WideColorGamut) { mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType; } + mSurfaceColorSpace = SkColorSpace::MakeSRGB(); if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 2dfe7c71ca1b..7a255c15bf5f 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -169,7 +169,7 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) { SkImageInfo info; info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(), - kPremul_SkAlphaType); + kPremul_SkAlphaType, getSurfaceColorSpace()); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), @@ -204,8 +204,7 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) { GrContext* context = thread.getGrContext(); if (context) { ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height()); - sk_sp colorFilter; - auto image = bitmap->makeImage(&colorFilter); + auto image = bitmap->makeImage(); if (image.get() && !bitmap->isHardware()) { SkImage_pinAsTexture(image.get(), context); SkImage_unpinAsTexture(image.get(), context); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 596b8aff6d4a..b66a843223f0 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -179,9 +179,8 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint, } void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); - mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette()); + sk_sp image = bitmap.makeImage(); + mRecorder.drawImage(image, left, top, filterPaint(paint), bitmap.palette()); // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. @@ -194,9 +193,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con SkAutoCanvasRestore acr(&mRecorder, true); concat(matrix); - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); - mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette()); + sk_sp image = bitmap.makeImage(); + mRecorder.drawImage(image, 0, 0, filterPaint(paint), bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } @@ -208,9 +206,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); - mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)), + sk_sp image = bitmap.makeImage(); + mRecorder.drawImageRect(image, srcRect, dstRect, filterPaint(paint), SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { @@ -247,10 +244,9 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) { filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality); } - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); + sk_sp image = bitmap.makeImage(); mRecorder.drawImageLattice(image, lattice, dst, - filterBitmap(std::move(filteredPaint), std::move(colorFilter)), + filterPaint(std::move(filteredPaint)), bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp index 9ffccfb4d340..15aec9f291a4 100644 --- a/libs/hwui/surfacetexture/ImageConsumer.cpp +++ b/libs/hwui/surfacetexture/ImageConsumer.cpp @@ -22,6 +22,7 @@ #include "renderthread/EglManager.h" #include "renderthread/RenderThread.h" #include "renderthread/VulkanManager.h" +#include "utils/Color.h" // Macro for including the SurfaceTexture name in log messages #define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) @@ -44,13 +45,16 @@ void ImageConsumer::onReleaseBufferLocked(int buf) { mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR; } -void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer) { - if (!mImage.get()) { +void ImageConsumer::ImageSlot::createIfNeeded(sp graphicBuffer, + android_dataspace dataspace) { + if (!mImage.get() || dataspace != mDataspace) { mImage = graphicBuffer.get() ? SkImage::MakeFromAHardwareBuffer( reinterpret_cast(graphicBuffer.get()), - kPremul_SkAlphaType) + kPremul_SkAlphaType, + uirenderer::DataSpaceToColorSpace(dataspace)) : nullptr; + mDataspace = dataspace; } } @@ -66,7 +70,7 @@ sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, int slot = st.mCurrentTexture; if (slot != BufferItem::INVALID_BUFFER_SLOT) { *queueEmpty = true; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace); return mImageSlots[slot].mImage; } } @@ -145,7 +149,7 @@ sk_sp ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, st.computeCurrentTransformMatrixLocked(); *queueEmpty = false; - mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace); return mImageSlots[slot].mImage; } diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h index 31ee8db52874..5bab0ef58a9a 100644 --- a/libs/hwui/surfacetexture/ImageConsumer.h +++ b/libs/hwui/surfacetexture/ImageConsumer.h @@ -68,18 +68,21 @@ private: * ImageConsumer maintains about a BufferQueue buffer slot. */ struct ImageSlot { - ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + ImageSlot() : mDataspace(HAL_DATASPACE_UNKNOWN), mEglFence(EGL_NO_SYNC_KHR) {} // mImage is the SkImage created from mGraphicBuffer. sk_sp mImage; + // the dataspace associated with the current image + android_dataspace mDataspace; + /** * 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); + void createIfNeeded(sp graphicBuffer, android_dataspace dataspace); }; /** diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp index 4bff715822e8..90f891265572 100644 --- a/libs/hwui/surfacetexture/SurfaceTexture.cpp +++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp @@ -470,8 +470,7 @@ void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { ConsumerBase::dumpLocked(result, prefix); } -sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, - bool* queueEmpty, +sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty, uirenderer::RenderState& renderState) { Mutex::Autolock _l(mMutex); @@ -488,7 +487,6 @@ sk_sp SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_d auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); if (image.get()) { uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); - dataSpace = mCurrentDataSpace; } return image; } diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h index db392a9f8476..96afd82b0d40 100644 --- a/libs/hwui/surfacetexture/SurfaceTexture.h +++ b/libs/hwui/surfacetexture/SurfaceTexture.h @@ -258,8 +258,8 @@ public: */ status_t attachToContext(uint32_t tex); - sk_sp dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, - bool* queueEmpty, uirenderer::RenderState& renderState); + sk_sp dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty, + uirenderer::RenderState& renderState); /** * attachToView attaches a SurfaceTexture that is currently in the diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 66b9b85bdbe7..8a1bc4d2f7f2 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -72,9 +72,7 @@ sp TestUtils::createTextureLayerUpdater( 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, SkMatrix::I(), nullptr); return layerUpdater; } diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index 15039b5fa976..ad11a1d32310 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -44,8 +44,7 @@ public: }); SkPaint paint; - sk_sp colorFilter; - sk_sp image = hwuiBitmap->makeImage(&colorFilter); + sk_sp image = hwuiBitmap->makeImage(); sk_sp repeatShader = image->makeShader(SkShader::TileMode::kRepeat_TileMode, SkShader::TileMode::kRepeat_TileMode, nullptr); diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index f137562e7c73..448408d19eb1 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -72,8 +72,7 @@ public: void doFrame(int frameNr) override {} sk_sp createBitmapShader(Bitmap& bitmap) { - sk_sp colorFilter; - sk_sp image = bitmap.makeImage(&colorFilter); + sk_sp image = bitmap.makeImage(); return image->makeShader(SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode); } diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index c235715073f5..210fced574e9 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -54,8 +54,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { // create an image and pin it so that we have something with a unique key in the cache sk_sp bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h)); - sk_sp filter; - sk_sp image = bitmap->makeImage(&filter); + sk_sp image = bitmap->makeImage(); ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext)); // attempt to trim all memory while we still hold strong refs diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index 6c8775b1bdbb..a6869791a915 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -43,7 +43,7 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { SkBitmap bitmap; bitmap.allocN32Pixels(16, 16); sk_sp layerImage = SkImage::MakeFromBitmap(bitmap); - layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage); + layerUpdater->updateLayer(true, scaledMatrix, layerImage); // the backing layer should now have all the properties applied. EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 634ceffe0741..f3a764874e2b 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -53,12 +53,12 @@ TEST(SkiaCanvas, colorSpaceXform) { adobeBitmap->getSkBitmap(&adobeSkBitmap); *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red - SkImageInfo info = adobeInfo.makeColorSpace(nullptr); + SkImageInfo info = adobeInfo.makeColorSpace(SkColorSpace::MakeSRGB()); sk_sp bitmap = Bitmap::allocateHeapBitmap(info); SkBitmap skBitmap; bitmap->getSkBitmap(&skBitmap); - // Create a software canvas. + // Create a software sRGB canvas. SkiaCanvas canvas(skBitmap); canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr); // The result should be fully red, since we convert to sRGB at draw time. @@ -77,7 +77,7 @@ TEST(SkiaCanvas, colorSpaceXform) { picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr); sk_sp picture = recorder.finishRecordingAsPicture(); - // Playback to an software canvas. The result should be fully red. + // Playback to a software sRGB canvas. The result should be fully red. canvas.asSkCanvas()->drawPicture(picture); ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0)); } -- cgit v1.2.3-59-g8ed1b