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.