Merge "[RenderEngine] relax locking conditions of image cache." into qt-r1-dev am: 5119d1adf3
am: 4b58b3bea4

Change-Id: I2897351922f0bf30c3d42303413918087789334a
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 6e7ec33..a2f12d0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -610,64 +610,29 @@
     }
 }
 
-status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
-    std::lock_guard<std::mutex> lock(mRenderingMutex);
-    return cacheExternalTextureBufferLocked(buffer);
-}
-
 status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
                                                      const sp<GraphicBuffer>& buffer,
                                                      const sp<Fence>& bufferFence) {
-    std::lock_guard<std::mutex> lock(mRenderingMutex);
-    return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
-}
-
-status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) {
-    if (buffer == nullptr) {
-        return BAD_VALUE;
-    }
-
     ATRACE_CALL();
-
-    if (mImageCache.count(buffer->getId()) > 0) {
-        return NO_ERROR;
-    }
-
-    std::unique_ptr<Image> newImage = createImage();
-
-    bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
-                                                   buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-    if (!created) {
-        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 NO_INIT;
-    }
-    mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
-
-    return NO_ERROR;
-}
-
-status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
-                                                           const sp<GraphicBuffer>& buffer,
-                                                           const sp<Fence>& bufferFence) {
-    ATRACE_CALL();
-    status_t cacheResult = cacheExternalTextureBufferLocked(buffer);
+    status_t cacheResult = cacheExternalTextureBuffer(buffer);
 
     if (cacheResult != NO_ERROR) {
         return cacheResult;
     }
 
-    auto cachedImage = mImageCache.find(buffer->getId());
+    {
+        std::lock_guard<std::mutex> lock(mRenderingMutex);
+        auto cachedImage = mImageCache.find(buffer->getId());
 
-    if (cachedImage == mImageCache.end()) {
-        // We failed creating the image if we got here, so bail out.
-        bindExternalTextureImage(texName, *createImage());
-        return NO_INIT;
+        if (cachedImage == mImageCache.end()) {
+            // We failed creating the image if we got here, so bail out.
+            bindExternalTextureImage(texName, *createImage());
+            return NO_INIT;
+        }
+
+        bindExternalTextureImage(texName, *cachedImage->second);
     }
 
-    bindExternalTextureImage(texName, *cachedImage->second);
-
     // Wait for the new buffer to be ready.
     if (bufferFence != nullptr && bufferFence->isValid()) {
         if (GLExtensions::getInstance().hasWaitSync()) {
@@ -692,6 +657,45 @@
     return NO_ERROR;
 }
 
+status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+    if (buffer == nullptr) {
+        return BAD_VALUE;
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(mRenderingMutex);
+        if (mImageCache.count(buffer->getId()) > 0) {
+            // If there's already an image then fail fast here.
+            return NO_ERROR;
+        }
+    }
+    ATRACE_CALL();
+
+    // Create the image without holding a lock so that we don't block anything.
+    std::unique_ptr<Image> newImage = createImage();
+
+    bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
+                                                   buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+    if (!created) {
+        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 NO_INIT;
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(mRenderingMutex);
+        if (mImageCache.count(buffer->getId()) > 0) {
+            // In theory it's possible for another thread to recache the image,
+            // so bail out if another thread won.
+            return NO_ERROR;
+        }
+        mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+    }
+
+    return NO_ERROR;
+}
+
 void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     const auto& cachedImage = mImageCache.find(bufferId);
@@ -884,127 +888,123 @@
         return BAD_VALUE;
     }
 
-    {
-        std::lock_guard<std::mutex> lock(mRenderingMutex);
+    BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
 
-        BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
-
-        if (fbo.getStatus() != NO_ERROR) {
-            ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
-                  buffer->handle);
-            checkErrors();
-            return fbo.getStatus();
-        }
-
-        // clear the entire buffer, sometimes when we reuse buffers we'd persist
-        // ghost images otherwise.
-        // we also require a full transparent framebuffer for overlays. This is
-        // probably not quite efficient on all GPUs, since we could filter out
-        // opaque layers.
-        clearWithColor(0.0, 0.0, 0.0, 0.0);
-
-        setViewportAndProjection(display.physicalDisplay, display.clip);
-
-        setOutputDataSpace(display.outputDataspace);
-        setDisplayMaxLuminance(display.maxLuminance);
-
-        mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
-        mState.projectionMatrix = projectionMatrix;
-        if (!display.clearRegion.isEmpty()) {
-            glDisable(GL_BLEND);
-            fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
-        }
-
-        Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
-        for (auto layer : layers) {
-            mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
-
-            const FloatRect bounds = layer.geometry.boundaries;
-            Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
-            position[0] = vec2(bounds.left, bounds.top);
-            position[1] = vec2(bounds.left, bounds.bottom);
-            position[2] = vec2(bounds.right, bounds.bottom);
-            position[3] = vec2(bounds.right, bounds.top);
-
-            setupLayerCropping(layer, mesh);
-            setColorTransform(display.colorTransform * layer.colorTransform);
-
-            bool usePremultipliedAlpha = true;
-            bool disableTexture = true;
-            bool isOpaque = false;
-
-            if (layer.source.buffer.buffer != nullptr) {
-                disableTexture = false;
-                isOpaque = layer.source.buffer.isOpaque;
-
-                sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
-                bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf,
-                                                layer.source.buffer.fence);
-
-                usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
-                Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
-                mat4 texMatrix = layer.source.buffer.textureTransform;
-
-                texture.setMatrix(texMatrix.asArray());
-                texture.setFiltering(layer.source.buffer.useTextureFiltering);
-
-                texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
-                setSourceY410BT2020(layer.source.buffer.isY410BT2020);
-
-                renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
-                texCoords[0] = vec2(0.0, 0.0);
-                texCoords[1] = vec2(0.0, 1.0);
-                texCoords[2] = vec2(1.0, 1.0);
-                texCoords[3] = vec2(1.0, 0.0);
-                setupLayerTexturing(texture);
-            }
-
-            const half3 solidColor = layer.source.solidColor;
-            const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
-            // Buffer sources will have a black solid color ignored in the shader,
-            // so in that scenario the solid color passed here is arbitrary.
-            setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
-                               layer.geometry.roundedCornersRadius);
-            if (layer.disableBlending) {
-                glDisable(GL_BLEND);
-            }
-            setSourceDataSpace(layer.sourceDataspace);
-
-            // We only want to do a special handling for rounded corners when having rounded corners
-            // is the only reason it needs to turn on blending, otherwise, we handle it like the
-            // usual way since it needs to turn on blending anyway.
-            if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
-                handleRoundedCorners(display, layer, mesh);
-            } else {
-                drawMesh(mesh);
-            }
-
-            // Cleanup if there's a buffer source
-            if (layer.source.buffer.buffer != nullptr) {
-                disableBlending();
-                setSourceY410BT2020(false);
-                disableTexturing();
-            }
-        }
-
-        if (drawFence != nullptr) {
-            *drawFence = flush();
-        }
-        // If flush failed or we don't support native fences, we need to force the
-        // gl command stream to be executed.
-        if (drawFence == nullptr || drawFence->get() < 0) {
-            bool success = finish();
-            if (!success) {
-                ALOGE("Failed to flush RenderEngine commands");
-                checkErrors();
-                // Chances are, something illegal happened (either the caller passed
-                // us bad parameters, or we messed up our shader generation).
-                return INVALID_OPERATION;
-            }
-        }
-
+    if (fbo.getStatus() != NO_ERROR) {
+        ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+              buffer->handle);
         checkErrors();
+        return fbo.getStatus();
     }
+
+    // clear the entire buffer, sometimes when we reuse buffers we'd persist
+    // ghost images otherwise.
+    // we also require a full transparent framebuffer for overlays. This is
+    // probably not quite efficient on all GPUs, since we could filter out
+    // opaque layers.
+    clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+    setViewportAndProjection(display.physicalDisplay, display.clip);
+
+    setOutputDataSpace(display.outputDataspace);
+    setDisplayMaxLuminance(display.maxLuminance);
+
+    mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
+    mState.projectionMatrix = projectionMatrix;
+    if (!display.clearRegion.isEmpty()) {
+        glDisable(GL_BLEND);
+        fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+    }
+
+    Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+    for (auto layer : layers) {
+        mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+
+        const FloatRect bounds = layer.geometry.boundaries;
+        Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+        position[0] = vec2(bounds.left, bounds.top);
+        position[1] = vec2(bounds.left, bounds.bottom);
+        position[2] = vec2(bounds.right, bounds.bottom);
+        position[3] = vec2(bounds.right, bounds.top);
+
+        setupLayerCropping(layer, mesh);
+        setColorTransform(display.colorTransform * layer.colorTransform);
+
+        bool usePremultipliedAlpha = true;
+        bool disableTexture = true;
+        bool isOpaque = false;
+
+        if (layer.source.buffer.buffer != nullptr) {
+            disableTexture = false;
+            isOpaque = layer.source.buffer.isOpaque;
+
+            sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+            bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
+                                      layer.source.buffer.fence);
+
+            usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+            Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+            mat4 texMatrix = layer.source.buffer.textureTransform;
+
+            texture.setMatrix(texMatrix.asArray());
+            texture.setFiltering(layer.source.buffer.useTextureFiltering);
+
+            texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+            setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+
+            renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+            texCoords[0] = vec2(0.0, 0.0);
+            texCoords[1] = vec2(0.0, 1.0);
+            texCoords[2] = vec2(1.0, 1.0);
+            texCoords[3] = vec2(1.0, 0.0);
+            setupLayerTexturing(texture);
+        }
+
+        const half3 solidColor = layer.source.solidColor;
+        const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+        // Buffer sources will have a black solid color ignored in the shader,
+        // so in that scenario the solid color passed here is arbitrary.
+        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+                           layer.geometry.roundedCornersRadius);
+        if (layer.disableBlending) {
+            glDisable(GL_BLEND);
+        }
+        setSourceDataSpace(layer.sourceDataspace);
+
+        // We only want to do a special handling for rounded corners when having rounded corners
+        // is the only reason it needs to turn on blending, otherwise, we handle it like the
+        // usual way since it needs to turn on blending anyway.
+        if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+            handleRoundedCorners(display, layer, mesh);
+        } else {
+            drawMesh(mesh);
+        }
+
+        // Cleanup if there's a buffer source
+        if (layer.source.buffer.buffer != nullptr) {
+            disableBlending();
+            setSourceY410BT2020(false);
+            disableTexturing();
+        }
+    }
+
+    if (drawFence != nullptr) {
+        *drawFence = flush();
+    }
+    // If flush failed or we don't support native fences, we need to force the
+    // gl command stream to be executed.
+    if (drawFence == nullptr || drawFence->get() < 0) {
+        bool success = finish();
+        if (!success) {
+            ALOGE("Failed to flush RenderEngine commands");
+            checkErrors();
+            // Chances are, something illegal happened (either the caller passed
+            // us bad parameters, or we messed up our shader generation).
+            return INVALID_OPERATION;
+        }
+    }
+
+    checkErrors();
     return NO_ERROR;
 }
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 70b704a..54fc987 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -73,8 +73,7 @@
     bool useProtectedContext(bool useProtectedContext) override;
     status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
                         ANativeWindowBuffer* buffer, const bool useFramebufferCache,
-                        base::unique_fd&& bufferFence, base::unique_fd* drawFence)
-            EXCLUDES(mRenderingMutex) override;
+                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
@@ -211,15 +210,6 @@
     // multiple threads is guaranteed thread-safe.
     std::mutex mRenderingMutex;
 
-    // See bindExternalTextureBuffer above, but requiring that mRenderingMutex
-    // is held.
-    status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer,
-                                             const sp<Fence>& fence) REQUIRES(mRenderingMutex);
-    // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex
-    // is held.
-    status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer)
-            REQUIRES(mRenderingMutex);
-
     std::unique_ptr<Framebuffer> mDrawingBuffer;
 
     class FlushTracer {
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 8a798ee..1c480a7 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -120,6 +120,17 @@
     // should be called for every display that needs to be rendered via the GPU.
     // @param display The display-wide settings that should be applied prior to
     // drawing any layers.
+    //
+    // Assumptions when calling this method:
+    // 1. There is exactly one caller - i.e. multi-threading is not supported.
+    // 2. Additional threads may be calling the {bind,cache}ExternalTexture
+    // methods above. But the main thread is responsible for holding resources
+    // such that Image destruction does not occur while this method is called.
+    //
+    // TODO(b/136806342): This should behavior should ideally be fixed since
+    // the above two assumptions are brittle, as conditional thread safetyness
+    // may be insufficient when maximizing rendering performance in the future.
+    //
     // @param layers The layers to draw onto the display, in Z-order.
     // @param buffer The buffer which will be drawn to. This buffer will be
     // ready once drawFence fires.