diff options
55 files changed, 2202 insertions, 2490 deletions
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 <Caches.h> + 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 <Caches.h> #include <jni.h> 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 <Caches.h> #include <jni.h> 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 <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> -#include "surfacetexture/SurfaceTexture.h" // ---------------------------------------------------------------------------- @@ -81,10 +80,10 @@ static bool isProtectedContext() { // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp<SurfaceTexture>& surfaceTexture) + const sp<GLConsumer>& 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<SurfaceTexture::FrameAvailableListener> listener) + jobject thiz, sp<GLConsumer::FrameAvailableListener> 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> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { - return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); +sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { + return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); } sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) { @@ -132,7 +131,7 @@ sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) } sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz)); sp<Surface> 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> surfaceTexture; + sp<GLConsumer> 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(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> 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(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> 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(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> 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(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> 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(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz) { - sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<GLConsumer> 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<DeferredLayerUpdater*>(layerUpdaterPtr); - layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); + sp<GLConsumer> 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<ANativeWindow> 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> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */ +extern sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); /* gets the producer end of the SurfaceTexture */ extern sp<IGraphicBufferProducer> 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 <cutils/properties.h> +#include <utils/Log.h> +#include <utils/String8.h> + +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 <memory> +#include <vector> + +#include <GLES3/gl3.h> + +#include <utils/KeyedVector.h> + +#include <cutils/compiler.h> + +#include <SkPath.h> + +#include <vector> + +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<TextureVertex[]> 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<SkImage> 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<GlLayer*>(mLayer)->getTextureId()); } + if (mUpdateTexImage) { + mUpdateTexImage = false; + doUpdateTexImage(); + } + GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); + static_cast<GlLayer*>(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<SkImage>& 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<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer(); + if (buffer != nullptr) { + // force filtration if buffer size != layer size + forceFilter = mWidth != static_cast<int>(buffer->getWidth()) || + mHeight != static_cast<int>(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<VkLayer*>(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 <SkColorFilter.h> -#include <SkImage.h> #include <SkMatrix.h> #include <cutils/compiler.h> -#include <map> +#include <gui/GLConsumer.h> #include <system/graphics.h> #include <utils/StrongPointer.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#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<Layer*(RenderState& renderState, uint32_t layerWidth, + uint32_t layerHeight, sk_sp<SkColorFilter> 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<SurfaceTexture>& consumer) { - if (consumer.get() != mSurfaceTexture.get()) { - mSurfaceTexture = consumer; + ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& 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<SkImage>& 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<SkColorFilter> mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp<SurfaceTexture> mSurfaceTexture; + sp<GLConsumer> 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<SkColorFilter> 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<SkColorFilter> 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 <GpuMemoryTracker.h> @@ -116,6 +117,22 @@ void GpuMemoryTracker::onFrameCompleted() { ATRACE_INT(buf, stats.count); } } + + std::vector<const Texture*> freeList; + for (const auto& obj : gObjectSet) { + if (obj->objectType() == GpuObjectType::Texture) { + const Texture* texture = static_cast<Texture*>(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*>(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 <SkToSRGBColorFilter.h> namespace android { namespace uirenderer { -Layer::Layer(RenderState& renderState, sk_sp<SkColorFilter> colorFilter, int alpha, - SkBlendMode mode) +Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> 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<SkColorFilter> 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 <SkColorFilter.h> #include <SkColorSpace.h> #include <SkPaint.h> -#include <SkImage.h> -#include <SkMatrix.h> -#include <system/graphics.h> + +#include "Matrix.h" namespace android { namespace uirenderer { @@ -41,19 +40,24 @@ class RenderState; */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - Layer(RenderState& renderState, sk_sp<SkColorFilter>, 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<SkColorFilter> 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<SkImage>& image) { this->layerImage = image; } - - inline sk_sp<SkImage> getImage() const { return this->layerImage; } - protected: + Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, 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<SkImage> 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 <utils/Log.h> + +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<uint8_t[]> 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<void*>(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 <GLES3/gl3.h> + +#include <log/log.h> + +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 <utils/Log.h> + +#include <math/mat4.h> + +#include <SkCanvas.h> + +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<SkColorSpace> 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<SkColorSpace> 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 <memory> + +#include <math/mat3.h> + +#include <ui/ColorSpace.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES3/gl3.h> +#include <SkBitmap.h> + +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<SkColorSpace> 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<ColorSpaceConnector> 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 <SkCanvas.h> +#include <SkSurface.h> + +namespace android { +namespace uirenderer { + +void VkLayer::updateTexture() { + sk_sp<SkSurface> 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 <SkImage.h> + +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<SkColorFilter> 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<SkImage> 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<SkImage> 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 <utils/Trace.h> #include <sys/mman.h> 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<SkImage> layerImage = layer->getImage(); + SkMatrix layerTransform; + layer->getTransform().copyTo(layerTransform); + sk_sp<SkImage> layerImage; const int layerWidth = layer->getWidth(); const int layerHeight = layer->getHeight(); + if (layer->getApi() == Layer::Api::OpenGL) { + GlLayer* glLayer = static_cast<GlLayer*>(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<VkLayer*>(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<SkColorFilter> 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<SkColorFilter> 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 <GLES3/gl3.h> + +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 <GpuMemoryTracker.h> #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<GlLayer*>(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<VkLayer*>(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 <GLES2/gl2.h> @@ -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<Layer*> mActiveLayers; std::set<DeferredLayerUpdater*> 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 <GLES3/gl3.h> +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <memory> + +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 <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <memory> + +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<Texture> 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 <GrContextOptions.h> @@ -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<Layer*>::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 <GpuMemoryTracker.h> #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 <cutils/properties.h> #include <log/log.h> -#include <private/gui/SyncFeatures.h> #include <utils/Trace.h> #include "utils/StringUtils.h" @@ -465,109 +464,6 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } -status_t EglManager::fenceWait(sp<Fence>& 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<Fence>& 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 <EGL/egl.h> -#include <EGL/eglext.h> #include <SkRect.h> #include <cutils/compiler.h> -#include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> @@ -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>& 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<Fence>& 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>& 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<Fence>& 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 <vulkan/vulkan.h> #include <SkSurface.h> -#include <ui/Fence.h> -#include <utils/StrongPointer.h> #include <vk/GrVkBackendContext.h> 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>& fence); - - // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. - status_t createReleaseFence(sp<Fence>& 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 <inttypes.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <cutils/compiler.h> -#include <gui/BufferItem.h> -#include <gui/BufferQueue.h> -#include <private/gui/SyncFeatures.h> -#include "EGLConsumer.h" -#include "SurfaceTexture.h" - -#include <utils/Log.h> -#include <utils/String8.h> -#include <utils/Trace.h> - -#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<GraphicBuffer> 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<GraphicBuffer> 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<GraphicBuffer> 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<void**>(&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<EglImage> 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> 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> 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<GraphicBuffer>& 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<GLeglImageOES>(mEglImage)); -} - -EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer) { - EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(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 <EGL/egl.h> -#include <EGL/eglext.h> - -#include <gui/BufferQueueDefs.h> - -#include <ui/FenceTime.h> -#include <ui/GraphicBuffer.h> -#include <utils/Mutex.h> - -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> 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<EglImage> { - public: - EglImage(sp<GraphicBuffer> 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>& graphicBuffer() { return mGraphicBuffer; } - const native_handle* graphicBufferHandle() { - return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; - } - - private: - // Only allow instantiation using ref counting. - friend class LightRefBase<EglImage>; - virtual ~EglImage(); - - // createImage creates a new EGLImage from a GraphicBuffer. - EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); - - // Disallow copying - EglImage(const EglImage& rhs); - void operator=(const EglImage& rhs); - - // mGraphicBuffer is the buffer that was used to create this image. - sp<GraphicBuffer> 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<GraphicBuffer> 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<EglImage> 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<EglImage> 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<GraphicBuffer> sReleasedTexImageBuffer; - sp<EglImage> 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 <gui/BufferQueue.h> -#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> graphicBuffer) { - if (!mImage.get()) { - mImage = graphicBuffer.get() - ? SkImage::MakeFromAHardwareBuffer( - reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()), - kPremul_SkAlphaType, SkColorSpace::MakeSRGB()) - : nullptr; - } -} - -sk_sp<SkImage> 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<Fence> 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 <EGL/egl.h> -#include <EGL/eglext.h> - -#include <gui/BufferQueueDefs.h> - -#include <SkImage.h> -#include <cutils/compiler.h> -#include <gui/BufferItem.h> -#include <system/graphics.h> - -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<SkImage> 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<SkImage> 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> 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 <cutils/compiler.h> -#include <gui/BufferQueue.h> -#include <math/mat4.h> -#include <system/window.h> - -#include <utils/Trace.h> - -#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<IGraphicBufferConsumer>& 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<IGraphicBufferConsumer>& 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> 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<GraphicBuffer> 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<GraphicBuffer>& 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<uint32_t>(crop.width()); - uint32_t newHeight = static_cast<uint32_t>(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<uint32_t>(crop.width()); - uint32_t currentHeight = static_cast<uint32_t>(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<Fence> SurfaceTexture::getCurrentFence() const { - Mutex::Autolock lock(mMutex); - return mCurrentFence; -} - -std::shared_ptr<FenceTime> 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<SkImage> 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 <gui/BufferQueueDefs.h> -#include <gui/ConsumerBase.h> - -#include <ui/FenceTime.h> -#include <ui/GraphicBuffer.h> - -#include <utils/Mutex.h> -#include <utils/String8.h> - -#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<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texureTarget, - bool useFenceSync, bool isControlledByApp); - - SurfaceTexture(const sp<IGraphicBufferConsumer>& 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<GraphicBuffer>& 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<Fence> getCurrentFence() const; - - /** - * getCurrentFence returns the FenceTime indicating when the current - * buffer is ready to be read from. - */ - std::shared_ptr<FenceTime> 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<SkImage> 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> 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<Fence> mCurrentFence; - - /** - * The FenceTime wrapper around mCurrentFence. - */ - std::shared_ptr<FenceTime> 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 <memunreachable/memunreachable.h> @@ -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<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { sp<DeferredLayerUpdater> 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<GlLayer*>(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 <DeviceInfo.h> #include <DisplayList.h> +#include <GlLayer.h> #include <Matrix.h> #include <Properties.h> #include <Rect.h> 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 <gtest/gtest.h> -#include <SkBitmap.h> -#include <SkImage.h> 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<GlLayer*>(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<SkImage> 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<GlLayer*>(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<GlLayer*>(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 <signal.h> 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 <GLES2/gl2.h> #include <utils/Blur.h> #include <SkColorFilter.h> 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 <utils/Log.h> +#include <gui/GLConsumer.h> #include <gui/Surface.h> #include <android_runtime/android_graphics_SurfaceTexture.h> -#include "surfacetexture/SurfaceTexture.h" - using namespace android; struct ASurfaceTexture { - sp<SurfaceTexture> consumer; + sp<GLConsumer> consumer; sp<IGraphicBufferProducer> producer; }; |