Merge "Added libgui_parcelable_fuzzer"
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
index e77c55c..910c9dc 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
@@ -49,6 +49,7 @@
});
sp<RpcSession> session = RpcSession::make();
+ session->setMaxIncomingThreads(1);
status_t status;
for (size_t tries = 0; tries < 5; tries++) {
usleep(10000);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c6cdeb7..2637f59 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -1184,18 +1184,23 @@
std::vector<sp<IGraphicBufferProducer>> mProducers;
};
-TEST_F(MultiDisplayTests, drop_input_if_layer_on_invalid_display) {
+TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) {
ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
// Do not create a display associated with the LayerStack.
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
surface->showAt(100, 100);
+ // Touches should be dropped if the layer is on an invalid display.
injectTapOnDisplay(101, 101, layerStack.id);
- surface->requestFocus(layerStack.id);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
-
EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ // However, we still let the window be focused and receive keys.
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ surface->expectKey(AKEYCODE_V);
}
TEST_F(MultiDisplayTests, virtual_display_receives_input) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index a0bba59..347b8b7 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -24,43 +24,11 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GrContextOptions.h>
-#include <SkBlendMode.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkColorFilter.h>
-#include <SkColorMatrix.h>
-#include <SkColorSpace.h>
-#include <SkData.h>
-#include <SkGraphics.h>
-#include <SkImage.h>
-#include <SkImageFilters.h>
-#include <SkImageInfo.h>
-#include <SkM44.h>
-#include <SkMatrix.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkPoint.h>
-#include <SkPoint3.h>
-#include <SkRect.h>
-#include <SkRefCnt.h>
-#include <SkRegion.h>
-#include <SkRRect.h>
-#include <SkRuntimeEffect.h>
-#include <SkSamplingOptions.h>
-#include <SkScalar.h>
-#include <SkShader.h>
-#include <SkShadowUtils.h>
-#include <SkString.h>
-#include <SkSurface.h>
-#include <SkTileMode.h>
#include <android-base/stringprintf.h>
#include <gl/GrGLInterface.h>
#include <gui/TraceUtils.h>
#include <sync/sync.h>
-#include <ui/BlurRegion.h>
-#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
-#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
#include <cmath>
@@ -69,23 +37,7 @@
#include <numeric>
#include "../gl/GLExtensions.h"
-#include "Cache.h"
-#include "ColorSpaces.h"
-#include "filters/BlurFilter.h"
-#include "filters/GaussianBlurFilter.h"
-#include "filters/KawaseBlurFilter.h"
-#include "filters/LinearEffect.h"
#include "log/log_main.h"
-#include "skia/debug/SkiaCapture.h"
-#include "skia/debug/SkiaMemoryReporter.h"
-#include "skia/filters/StretchShaderFactory.h"
-#include "system/graphics-base-v1.0.h"
-
-namespace {
-// Debugging settings
-static const bool kPrintLayerSettings = false;
-static const bool kFlushAfterEveryLayer = kPrintLayerSettings;
-} // namespace
bool checkGlError(const char* op, int lineNumber);
@@ -244,6 +196,7 @@
std::unique_ptr<SkiaGLRenderEngine> engine =
std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext,
protectedPlaceholder);
+ engine->ensureGrContextsCreated();
ALOGI("OpenGL ES informations:");
ALOGI("vendor : %s", extensions.getVendor());
@@ -256,11 +209,6 @@
return engine;
}
-std::future<void> SkiaGLRenderEngine::primeCache() {
- Cache::primeShaderCache(this);
- return {};
-}
-
EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
status_t err;
EGLConfig config;
@@ -300,72 +248,20 @@
return config;
}
-sk_sp<SkData> SkiaGLRenderEngine::SkSLCacheMonitor::load(const SkData& key) {
- // This "cache" does not actually cache anything. It just allows us to
- // monitor Skia's internal cache. So this method always returns null.
- return nullptr;
-}
-
-void SkiaGLRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data,
- const SkString& description) {
- mShadersCachedSinceLastCall++;
- mTotalShadersCompiled++;
- ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled);
-}
-
-int SkiaGLRenderEngine::reportShadersCompiled() {
- return mSkSLCacheMonitor.totalShadersCompiled();
-}
-
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
- : SkiaRenderEngine(args.renderEngineType),
+ : SkiaRenderEngine(args.renderEngineType,
+ static_cast<PixelFormat>(args.pixelFormat),
+ args.useColorManagement, args.supportsBackgroundBlur),
mEGLDisplay(display),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
- mProtectedPlaceholderSurface(protectedPlaceholder),
- mDefaultPixelFormat(static_cast<PixelFormat>(args.pixelFormat)),
- mUseColorManagement(args.useColorManagement) {
- sk_sp<const GrGLInterface> glInterface = GrGLMakeNativeInterface();
- LOG_ALWAYS_FATAL_IF(!glInterface.get());
-
- GrContextOptions options;
- options.fDisableDriverCorrectnessWorkarounds = true;
- options.fDisableDistanceFieldPaths = true;
- options.fReducedShaderVariations = true;
- options.fPersistentCache = &mSkSLCacheMonitor;
- mGrContext = GrDirectContext::MakeGL(glInterface, options);
- if (supportsProtectedContent()) {
- useProtectedContext(true);
- mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
- useProtectedContext(false);
- }
-
- if (args.supportsBackgroundBlur) {
- ALOGD("Background Blurs Enabled");
- mBlurFilter = new KawaseBlurFilter();
- }
- mCapture = std::make_unique<SkiaCapture>();
-}
+ mProtectedPlaceholderSurface(protectedPlaceholder) { }
SkiaGLRenderEngine::~SkiaGLRenderEngine() {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (mBlurFilter) {
- delete mBlurFilter;
- }
-
- mCapture = nullptr;
-
- mGrContext->flushAndSubmit(true);
- mGrContext->abandonContext();
-
- if (mProtectedGrContext) {
- mProtectedGrContext->flushAndSubmit(true);
- mProtectedGrContext->abandonContext();
- }
-
+ finishRenderingAndAbandonContext();
if (mPlaceholderSurface != EGL_NO_SURFACE) {
eglDestroySurface(mEGLDisplay, mPlaceholderSurface);
}
@@ -383,71 +279,69 @@
eglReleaseThread();
}
-bool SkiaGLRenderEngine::supportsProtectedContent() const {
+SkiaRenderEngine::Contexts SkiaGLRenderEngine::createDirectContexts(
+ const GrContextOptions& options) {
+
+ LOG_ALWAYS_FATAL_IF(isProtected(),
+ "Cannot setup contexts while already in protected mode");
+
+ sk_sp<const GrGLInterface> glInterface = GrGLMakeNativeInterface();
+
+ LOG_ALWAYS_FATAL_IF(!glInterface.get(), "GrGLMakeNativeInterface() failed");
+
+ SkiaRenderEngine::Contexts contexts;
+ contexts.first = GrDirectContext::MakeGL(glInterface, options);
+ if (supportsProtectedContentImpl()) {
+ useProtectedContextImpl(GrProtected::kYes);
+ contexts.second = GrDirectContext::MakeGL(glInterface, options);
+ useProtectedContextImpl(GrProtected::kNo);
+ }
+
+ return contexts;
+}
+
+bool SkiaGLRenderEngine::supportsProtectedContentImpl() const {
return mProtectedEGLContext != EGL_NO_CONTEXT;
}
-GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const {
- return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
-}
-
-void SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
- if (useProtectedContext == mInProtectedContext ||
- (useProtectedContext && !supportsProtectedContent())) {
- return;
- }
-
- // release any scratch resources before switching into a new mode
- if (getActiveGrContext()) {
- getActiveGrContext()->purgeUnlockedResources(true);
- }
-
+bool SkiaGLRenderEngine::useProtectedContextImpl(GrProtected isProtected) {
const EGLSurface surface =
- useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
- const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+ (isProtected == GrProtected::kYes) ?
+ mProtectedPlaceholderSurface : mPlaceholderSurface;
+ const EGLContext context = (isProtected == GrProtected::kYes) ?
+ mProtectedEGLContext : mEGLContext;
- if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) {
- mInProtectedContext = useProtectedContext;
- // given that we are sharing the same thread between two GrContexts we need to
- // make sure that the thread state is reset when switching between the two.
- if (getActiveGrContext()) {
- getActiveGrContext()->resetContext();
- }
- }
+ return eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
}
-base::unique_fd SkiaGLRenderEngine::flush() {
- ATRACE_CALL();
- if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
- return base::unique_fd();
- }
-
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
- return base::unique_fd();
- }
-
- // native fence fd will not be populated until flush() is done.
- glFlush();
-
- // get the fence fd
- base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
- }
-
- return fenceFd;
-}
-
-void SkiaGLRenderEngine::waitFence(base::borrowed_fd fenceFd) {
+void SkiaGLRenderEngine::waitFence(GrDirectContext*, base::borrowed_fd fenceFd) {
if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) {
ATRACE_NAME("SkiaGLRenderEngine::waitFence");
sync_wait(fenceFd.get(), -1);
}
}
+base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) {
+ base::unique_fd drawFence = flush();
+
+ bool requireSync = drawFence.get() < 0;
+ if (requireSync) {
+ ATRACE_BEGIN("Submit(sync=true)");
+ } else {
+ ATRACE_BEGIN("Submit(sync=false)");
+ }
+ bool success = grContext->submit(requireSync);
+ ATRACE_END();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ // Chances are, something illegal happened (Skia's internal GPU object
+ // doesn't exist, or the context was abandoned).
+ return drawFence;
+ }
+
+ return drawFence;
+}
+
bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) {
if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
!gl::GLExtensions::getInstance().hasWaitSync()) {
@@ -483,974 +377,29 @@
return true;
}
-static float toDegrees(uint32_t transform) {
- switch (transform) {
- case ui::Transform::ROT_90:
- return 90.0;
- case ui::Transform::ROT_180:
- return 180.0;
- case ui::Transform::ROT_270:
- return 270.0;
- default:
- return 0.0;
- }
-}
-
-static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
- return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
- matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
- matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
- matrix[3][3], 0);
-}
-
-static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
- int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
- int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
-
- // Treat unsupported dataspaces as srgb
- if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
- destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
- destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
- destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
- }
-
- if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
- sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
- sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
- sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
- }
-
- const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
- const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
- const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
- const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
-
- return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
- sourceTransfer != destTransfer;
-}
-
-void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
- bool isRenderable) {
- // Only run this if RE is running on its own thread. This way the access to GL
- // operations is guaranteed to be happening on the same thread.
- if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
- return;
- }
- // We currently don't attempt to map a buffer if the buffer contains protected content
- // because GPU resources for protected buffers is much more limited.
- const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
- if (isProtectedBuffer) {
- return;
- }
+base::unique_fd SkiaGLRenderEngine::flush() {
ATRACE_CALL();
-
- // If we were to support caching protected buffers then we will need to switch the
- // currently bound context if we are not already using the protected context (and subsequently
- // switch back after the buffer is cached). However, for non-protected content we can bind
- // the texture in either GL context because they are initialized with the same share_context
- // which allows the texture state to be shared between them.
- auto grContext = getActiveGrContext();
- auto& cache = mTextureCache;
-
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- mGraphicBufferExternalRefs[buffer->getId()]++;
-
- if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
- std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext,
- buffer->toAHardwareBuffer(),
- isRenderable, mTextureCleanupMgr);
- cache.insert({buffer->getId(), imageTextureRef});
- }
-}
-
-void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
- ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
- iter != mGraphicBufferExternalRefs.end()) {
- if (iter->second == 0) {
- ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64
- "> from RenderEngine texture, but the "
- "ref count was already zero!",
- buffer->getId());
- mGraphicBufferExternalRefs.erase(buffer->getId());
- return;
- }
-
- iter->second--;
-
- // Swap contexts if needed prior to deleting this buffer
- // See Issue 1 of
- // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt: even
- // when a protected context and an unprotected context are part of the same share group,
- // protected surfaces may not be accessed by an unprotected context, implying that protected
- // surfaces may only be freed when a protected context is active.
- const bool inProtected = mInProtectedContext;
- useProtectedContext(buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-
- if (iter->second == 0) {
- mTextureCache.erase(buffer->getId());
- mGraphicBufferExternalRefs.erase(buffer->getId());
- }
-
- // Swap back to the previous context so that cached values of isProtected in SurfaceFlinger
- // are up-to-date.
- if (inProtected != mInProtectedContext) {
- useProtectedContext(inProtected);
- }
- }
-}
-
-bool SkiaGLRenderEngine::canSkipPostRenderCleanup() const {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- return mTextureCleanupMgr.isEmpty();
-}
-
-void SkiaGLRenderEngine::cleanupPostRender() {
- ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- mTextureCleanupMgr.cleanup();
-}
-
-// Helper class intended to be used on the stack to ensure that texture cleanup
-// is deferred until after this class goes out of scope.
-class DeferTextureCleanup final {
-public:
- DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) {
- mMgr.setDeferredStatus(true);
- }
- ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); }
-
-private:
- DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup);
- AutoBackendTexture::CleanupManager& mMgr;
-};
-
-sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
- const RuntimeEffectShaderParameters& parameters) {
- // The given surface will be stretched by HWUI via matrix transformation
- // which gets similar results for most surfaces
- // Determine later on if we need to leverage the stertch shader within
- // surface flinger
- const auto& stretchEffect = parameters.layer.stretchEffect;
- auto shader = parameters.shader;
- if (stretchEffect.hasEffect()) {
- const auto targetBuffer = parameters.layer.source.buffer.buffer;
- const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicBuffer && parameters.shader) {
- shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
- }
+ if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
+ return base::unique_fd();
}
- if (parameters.requiresLinearEffect) {
- const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
- : ui::Dataspace::V0_SRGB_LINEAR;
- const ui::Dataspace outputDataspace = mUseColorManagement
- ? parameters.display.outputDataspace
- : ui::Dataspace::V0_SRGB_LINEAR;
-
- auto effect =
- shaders::LinearEffect{.inputDataspace = inputDataspace,
- .outputDataspace = outputDataspace,
- .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
-
- auto effectIter = mRuntimeEffects.find(effect);
- sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
- if (effectIter == mRuntimeEffects.end()) {
- runtimeEffect = buildRuntimeEffect(effect);
- mRuntimeEffects.insert({effect, runtimeEffect});
- } else {
- runtimeEffect = effectIter->second;
- }
- mat4 colorTransform = parameters.layer.colorTransform;
-
- colorTransform *=
- mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
- parameters.layerDimmingRatio, 1.f));
- const auto targetBuffer = parameters.layer.source.buffer.buffer;
- const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr;
- return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
- parameters.display.maxLuminance,
- parameters.display.currentLuminanceNits,
- parameters.layer.source.buffer.maxLuminanceNits,
- hardwareBuffer, parameters.display.renderIntent);
- }
- return parameters.shader;
-}
-
-void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
- if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
- // Record display settings when capture is running.
- std::stringstream displaySettings;
- PrintTo(display, &displaySettings);
- // Store the DisplaySettings in additional information.
- canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
- SkData::MakeWithCString(displaySettings.str().c_str()));
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+ return base::unique_fd();
}
- // Before doing any drawing, let's make sure that we'll start at the origin of the display.
- // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
- // displays might have different scaling when compared to the physical screen.
+ // native fence fd will not be populated until flush() is done.
+ glFlush();
- canvas->clipRect(getSkRect(display.physicalDisplay));
- canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
-
- const auto clipWidth = display.clip.width();
- const auto clipHeight = display.clip.height();
- auto rotatedClipWidth = clipWidth;
- auto rotatedClipHeight = clipHeight;
- // Scale is contingent on the rotation result.
- if (display.orientation & ui::Transform::ROT_90) {
- std::swap(rotatedClipWidth, rotatedClipHeight);
- }
- const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
- static_cast<SkScalar>(rotatedClipWidth);
- const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
- static_cast<SkScalar>(rotatedClipHeight);
- canvas->scale(scaleX, scaleY);
-
- // Canvas rotation is done by centering the clip window at the origin, rotating, translating
- // back so that the top left corner of the clip is at (0, 0).
- canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
- canvas->rotate(toDegrees(display.orientation));
- canvas->translate(-clipWidth / 2, -clipHeight / 2);
- canvas->translate(-display.clip.left, -display.clip.top);
-}
-
-class AutoSaveRestore {
-public:
- AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); }
- ~AutoSaveRestore() { restore(); }
- void replace(SkCanvas* canvas) {
- mCanvas = canvas;
- mSaveCount = canvas->save();
- }
- void restore() {
- if (mCanvas) {
- mCanvas->restoreToCount(mSaveCount);
- mCanvas = nullptr;
- }
+ // get the fence fd
+ base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
}
-private:
- SkCanvas* mCanvas;
- int mSaveCount;
-};
-
-static SkRRect getBlurRRect(const BlurRegion& region) {
- const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
- const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
- SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
- SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
- SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
- SkRRect roundedRect;
- roundedRect.setRectRadii(rect, radii);
- return roundedRect;
-}
-
-// Arbitrary default margin which should be close enough to zero.
-constexpr float kDefaultMargin = 0.0001f;
-static bool equalsWithinMargin(float expected, float value, float margin = kDefaultMargin) {
- LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
- return std::abs(expected - value) < margin;
-}
-
-namespace {
-template <typename T>
-void logSettings(const T& t) {
- std::stringstream stream;
- PrintTo(t, &stream);
- auto string = stream.str();
- size_t pos = 0;
- // Perfetto ignores \n, so split up manually into separate ALOGD statements.
- const size_t size = string.size();
- while (pos < size) {
- const size_t end = std::min(string.find("\n", pos), size);
- ALOGD("%s", string.substr(pos, end - pos).c_str());
- pos = end + 1;
- }
-}
-} // namespace
-
-void SkiaGLRenderEngine::drawLayersInternal(
- const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
- const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
- base::unique_fd&& bufferFence) {
- ATRACE_NAME("SkiaGL::drawLayersInternal");
-
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (buffer == nullptr) {
- ALOGE("No output buffer provided. Aborting GPU composition.");
- resultPromise->set_value({BAD_VALUE, base::unique_fd()});
- return;
- }
-
- validateOutputBufferUsage(buffer->getBuffer());
-
- auto grContext = getActiveGrContext();
- auto& cache = mTextureCache;
-
- // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
- DeferTextureCleanup dtc(mTextureCleanupMgr);
-
- std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
- if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
- surfaceTextureRef = it->second;
- } else {
- surfaceTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext,
- buffer->getBuffer()
- ->toAHardwareBuffer(),
- true, mTextureCleanupMgr);
- }
-
- // wait on the buffer to be ready to use prior to using it
- waitFence(bufferFence);
-
- const ui::Dataspace dstDataspace =
- mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
- sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
-
- SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
- if (dstCanvas == nullptr) {
- ALOGE("Cannot acquire canvas from Skia.");
- resultPromise->set_value({BAD_VALUE, base::unique_fd()});
- return;
- }
-
- // setup color filter if necessary
- sk_sp<SkColorFilter> displayColorTransform;
- if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) {
- displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
- }
- const bool ctModifiesAlpha =
- displayColorTransform && !displayColorTransform->isAlphaUnchanged();
-
- // Find the max layer white point to determine the max luminance of the scene...
- const float maxLayerWhitePoint = std::transform_reduce(
- layers.cbegin(), layers.cend(), 0.f,
- [](float left, float right) { return std::max(left, right); },
- [&](const auto& l) { return l.whitePointNits; });
-
- // ...and compute the dimming ratio if dimming is requested
- const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
- maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
- ? maxLayerWhitePoint / display.targetLuminanceNits
- : 1.f;
-
- // Find if any layers have requested blur, we'll use that info to decide when to render to an
- // offscreen buffer and when to render to the native buffer.
- sk_sp<SkSurface> activeSurface(dstSurface);
- SkCanvas* canvas = dstCanvas;
- SkiaCapture::OffscreenState offscreenCaptureState;
- const LayerSettings* blurCompositionLayer = nullptr;
- if (mBlurFilter) {
- bool requiresCompositionLayer = false;
- for (const auto& layer : layers) {
- // if the layer doesn't have blur or it is not visible then continue
- if (!layerHasBlur(layer, ctModifiesAlpha)) {
- continue;
- }
- if (layer.backgroundBlurRadius > 0 &&
- layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
- requiresCompositionLayer = true;
- }
- for (auto region : layer.blurRegions) {
- if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
- requiresCompositionLayer = true;
- }
- }
- if (requiresCompositionLayer) {
- activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
- canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
- blurCompositionLayer = &layer;
- break;
- }
- }
- }
-
- AutoSaveRestore surfaceAutoSaveRestore(canvas);
- // Clear the entire canvas with a transparent black to prevent ghost images.
- canvas->clear(SK_ColorTRANSPARENT);
- initCanvas(canvas, display);
-
- if (kPrintLayerSettings) {
- logSettings(display);
- }
- for (const auto& layer : layers) {
- ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
-
- if (kPrintLayerSettings) {
- logSettings(layer);
- }
-
- sk_sp<SkImage> blurInput;
- if (blurCompositionLayer == &layer) {
- LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
- LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
-
- // save a snapshot of the activeSurface to use as input to the blur shaders
- blurInput = activeSurface->makeImageSnapshot();
-
- // blit the offscreen framebuffer into the destination AHB, but only
- // if there are blur regions. backgroundBlurRadius blurs the entire
- // image below, so it can skip this step.
- if (layer.blurRegions.size()) {
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
- uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
- dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
- String8::format("SurfaceID|%" PRId64, id).c_str(),
- nullptr);
- dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
- } else {
- activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
- }
- }
-
- // assign dstCanvas to canvas and ensure that the canvas state is up to date
- canvas = dstCanvas;
- surfaceAutoSaveRestore.replace(canvas);
- initCanvas(canvas, display);
-
- LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
- dstSurface->getCanvas()->getSaveCount());
- LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
- dstSurface->getCanvas()->getTotalMatrix());
-
- // assign dstSurface to activeSurface
- activeSurface = dstSurface;
- }
-
- SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
- if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
- // Record the name of the layer if the capture is running.
- std::stringstream layerSettings;
- PrintTo(layer, &layerSettings);
- // Store the LayerSettings in additional information.
- canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(),
- SkData::MakeWithCString(layerSettings.str().c_str()));
- }
- // Layers have a local transform that should be applied to them
- canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());
-
- const auto [bounds, roundRectClip] =
- getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
- layer.geometry.roundedCornersRadius);
- if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
- std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
-
- // if multiple layers have blur, then we need to take a snapshot now because
- // only the lowest layer will have blurImage populated earlier
- if (!blurInput) {
- blurInput = activeSurface->makeImageSnapshot();
- }
- // rect to be blurred in the coordinate space of blurInput
- const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
-
- // if the clip needs to be applied then apply it now and make sure
- // it is restored before we attempt to draw any shadows.
- SkAutoCanvasRestore acr(canvas, true);
- if (!roundRectClip.isEmpty()) {
- canvas->clipRRect(roundRectClip, true);
- }
-
- // TODO(b/182216890): Filter out empty layers earlier
- if (blurRect.width() > 0 && blurRect.height() > 0) {
- if (layer.backgroundBlurRadius > 0) {
- ATRACE_NAME("BackgroundBlur");
- auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius,
- blurInput, blurRect);
-
- cachedBlurs[layer.backgroundBlurRadius] = blurredImage;
-
- mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f,
- blurRect, blurredImage, blurInput);
- }
-
- canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
- for (auto region : layer.blurRegions) {
- if (cachedBlurs[region.blurRadius] == nullptr) {
- ATRACE_NAME("BlurRegion");
- cachedBlurs[region.blurRadius] =
- mBlurFilter->generate(grContext, region.blurRadius, blurInput,
- blurRect);
- }
-
- mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
- region.alpha, blurRect,
- cachedBlurs[region.blurRadius], blurInput);
- }
- }
- }
-
- if (layer.shadow.length > 0) {
- // This would require a new parameter/flag to SkShadowUtils::DrawShadow
- LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow");
-
- SkRRect shadowBounds, shadowClip;
- if (layer.geometry.boundaries == layer.shadow.boundaries) {
- shadowBounds = bounds;
- shadowClip = roundRectClip;
- } else {
- std::tie(shadowBounds, shadowClip) =
- getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop,
- layer.geometry.roundedCornersRadius);
- }
-
- // Technically, if bounds is a rect and roundRectClip is not empty,
- // it means that the bounds and roundedCornersCrop were different
- // enough that we should intersect them to find the proper shadow.
- // In practice, this often happens when the two rectangles appear to
- // not match due to rounding errors. Draw the rounded version, which
- // looks more like the intent.
- const auto& rrect =
- shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
- drawShadow(canvas, rrect, layer.shadow);
- }
-
- const float layerDimmingRatio = layer.whitePointNits <= 0.f
- ? displayDimmingRatio
- : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
-
- const bool dimInLinearSpace = display.dimmingStage !=
- aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF;
-
- const bool requiresLinearEffect = layer.colorTransform != mat4() ||
- (mUseColorManagement &&
- needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
- (dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio));
-
- // quick abort from drawing the remaining portion of the layer
- if (layer.skipContentDraw ||
- (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending &&
- (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
- continue;
- }
-
- // If we need to map to linear space or color management is disabled, then mark the source
- // image with the same colorspace as the destination surface so that Skia's color
- // management is a no-op.
- const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
- ? dstDataspace
- : layer.sourceDataspace;
-
- SkPaint paint;
- if (layer.source.buffer.buffer) {
- ATRACE_NAME("DrawImage");
- validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
- const auto& item = layer.source.buffer;
- std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
-
- if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
- iter != cache.end()) {
- imageTextureRef = iter->second;
- } else {
- // If we didn't find the image in the cache, then create a local ref but don't cache
- // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
- // we didn't find anything in the cache then we intentionally did not cache this
- // buffer's resources.
- imageTextureRef = std::make_shared<
- AutoBackendTexture::LocalRef>(grContext,
- item.buffer->getBuffer()->toAHardwareBuffer(),
- false, mTextureCleanupMgr);
- }
-
- // if the layer's buffer has a fence, then we must must respect the fence prior to using
- // the buffer.
- if (layer.source.buffer.fence != nullptr) {
- waitFence(layer.source.buffer.fence->get());
- }
-
- // isOpaque means we need to ignore the alpha in the image,
- // replacing it with the alpha specified by the LayerSettings. See
- // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
- // The proper way to do this is to use an SkColorType that ignores
- // alpha, like kRGB_888x_SkColorType, and that is used if the
- // incoming image is kRGBA_8888_SkColorType. However, the incoming
- // image may be kRGBA_F16_SkColorType, for which there is no RGBX
- // SkColorType, or kRGBA_1010102_SkColorType, for which we have
- // kRGB_101010x_SkColorType, but it is not yet supported as a source
- // on the GPU. (Adding both is tracked in skbug.com/12048.) In the
- // meantime, we'll use a workaround that works unless we need to do
- // any color conversion. The workaround requires that we pretend the
- // image is already premultiplied, so that we do not premultiply it
- // before applying SkBlendMode::kPlus.
- const bool useIsOpaqueWorkaround = item.isOpaque &&
- (imageTextureRef->colorType() == kRGBA_1010102_SkColorType ||
- imageTextureRef->colorType() == kRGBA_F16_SkColorType);
- const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType
- : item.isOpaque ? kOpaque_SkAlphaType
- : item.usePremultipliedAlpha ? kPremul_SkAlphaType
- : kUnpremul_SkAlphaType;
- sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext);
-
- auto texMatrix = getSkM44(item.textureTransform).asM33();
- // textureTansform was intended to be passed directly into a shader, so when
- // building the total matrix with the textureTransform we need to first
- // normalize it, then apply the textureTransform, then scale back up.
- texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
- texMatrix.postScale(image->width(), image->height());
-
- SkMatrix matrix;
- if (!texMatrix.invert(&matrix)) {
- matrix = texMatrix;
- }
- // The shader does not respect the translation, so we add it to the texture
- // transform for the SkImage. This will make sure that the correct layer contents
- // are drawn in the correct part of the screen.
- matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
-
- sk_sp<SkShader> shader;
-
- if (layer.source.buffer.useTextureFiltering) {
- shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
- SkSamplingOptions(
- {SkFilterMode::kLinear, SkMipmapMode::kNone}),
- &matrix);
- } else {
- shader = image->makeShader(SkSamplingOptions(), matrix);
- }
-
- if (useIsOpaqueWorkaround) {
- shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
- SkShaders::Color(SkColors::kBlack,
- toSkColorSpace(layerDataspace)));
- }
-
- paint.setShader(createRuntimeEffectShader(
- RuntimeEffectShaderParameters{.shader = shader,
- .layer = layer,
- .display = display,
- .undoPremultipliedAlpha = !item.isOpaque &&
- item.usePremultipliedAlpha,
- .requiresLinearEffect = requiresLinearEffect,
- .layerDimmingRatio = dimInLinearSpace
- ? layerDimmingRatio
- : 1.f}));
-
- // Turn on dithering when dimming beyond this (arbitrary) threshold...
- static constexpr float kDimmingThreshold = 0.2f;
- // ...or we're rendering an HDR layer down to an 8-bit target
- // Most HDR standards require at least 10-bits of color depth for source content, so we
- // can just extract the transfer function rather than dig into precise gralloc layout.
- // Furthermore, we can assume that the only 8-bit target we support is RGBA8888.
- const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) &&
- buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888;
- if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) {
- paint.setDither(true);
- }
- paint.setAlphaf(layer.alpha);
-
- if (imageTextureRef->colorType() == kAlpha_8_SkColorType) {
- LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8");
-
- // SysUI creates the alpha layer as a coverage layer, which is
- // appropriate for the DPU. Use a color matrix to convert it to
- // a mask.
- // TODO (b/219525258): Handle input as a mask.
- //
- // The color matrix will convert A8 pixels with no alpha to
- // black, as described by this vector. If the display handles
- // the color transform, we need to invert it to find the color
- // that will result in black after the DPU applies the transform.
- SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a
- if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) {
- SkM44 colorSpaceMatrix = getSkM44(display.colorTransform);
- if (colorSpaceMatrix.invert(&colorSpaceMatrix)) {
- black = colorSpaceMatrix * black;
- } else {
- // We'll just have to use 0,0,0 as black, which should
- // be close to correct.
- ALOGI("Could not invert colorTransform!");
- }
- }
- SkColorMatrix colorMatrix(0, 0, 0, 0, black[0],
- 0, 0, 0, 0, black[1],
- 0, 0, 0, 0, black[2],
- 0, 0, 0, -1, 1);
- if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) {
- // On the other hand, if the device doesn't handle it, we
- // have to apply it ourselves.
- colorMatrix.postConcat(toSkColorMatrix(display.colorTransform));
- }
- paint.setColorFilter(SkColorFilters::Matrix(colorMatrix));
- }
- } else {
- ATRACE_NAME("DrawColor");
- const auto color = layer.source.solidColor;
- sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
- .fG = color.g,
- .fB = color.b,
- .fA = layer.alpha},
- toSkColorSpace(layerDataspace));
- paint.setShader(createRuntimeEffectShader(
- RuntimeEffectShaderParameters{.shader = shader,
- .layer = layer,
- .display = display,
- .undoPremultipliedAlpha = false,
- .requiresLinearEffect = requiresLinearEffect,
- .layerDimmingRatio = layerDimmingRatio}));
- }
-
- if (layer.disableBlending) {
- paint.setBlendMode(SkBlendMode::kSrc);
- }
-
- // An A8 buffer will already have the proper color filter attached to
- // its paint, including the displayColorTransform as needed.
- if (!paint.getColorFilter()) {
- if (!dimInLinearSpace && !equalsWithinMargin(1.0, layerDimmingRatio)) {
- // If we don't dim in linear space, then when we gamma correct the dimming ratio we
- // can assume a gamma 2.2 transfer function.
- static constexpr float kInverseGamma22 = 1.f / 2.2f;
- const auto gammaCorrectedDimmingRatio =
- std::pow(layerDimmingRatio, kInverseGamma22);
- auto dimmingMatrix =
- mat4::scale(vec4(gammaCorrectedDimmingRatio, gammaCorrectedDimmingRatio,
- gammaCorrectedDimmingRatio, 1.f));
-
- const auto colorFilter =
- SkColorFilters::Matrix(toSkColorMatrix(std::move(dimmingMatrix)));
- paint.setColorFilter(displayColorTransform
- ? displayColorTransform->makeComposed(colorFilter)
- : colorFilter);
- } else {
- paint.setColorFilter(displayColorTransform);
- }
- }
-
- if (!roundRectClip.isEmpty()) {
- canvas->clipRRect(roundRectClip, true);
- }
-
- if (!bounds.isRect()) {
- paint.setAntiAlias(true);
- canvas->drawRRect(bounds, paint);
- } else {
- canvas->drawRect(bounds.rect(), paint);
- }
- if (kFlushAfterEveryLayer) {
- ATRACE_NAME("flush surface");
- activeSurface->flush();
- }
- }
- for (const auto& borderRenderInfo : display.borderInfoList) {
- SkPaint p;
- p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g,
- borderRenderInfo.color.b, borderRenderInfo.color.a});
- p.setAntiAlias(true);
- p.setStyle(SkPaint::kStroke_Style);
- p.setStrokeWidth(borderRenderInfo.width);
- SkRegion sk_region;
- SkPath path;
-
- // Construct a final SkRegion using Regions
- for (const auto& r : borderRenderInfo.combinedRegion) {
- sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op);
- }
-
- sk_region.getBoundaryPath(&path);
- canvas->drawPath(path, p);
- path.close();
- }
-
- surfaceAutoSaveRestore.restore();
- mCapture->endCapture();
- {
- ATRACE_NAME("flush surface");
- LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
- activeSurface->flush();
- }
-
- base::unique_fd drawFence = flush();
-
- // If flush failed or we don't support native fences, we need to force the
- // gl command stream to be executed.
- bool requireSync = drawFence.get() < 0;
- if (requireSync) {
- ATRACE_BEGIN("Submit(sync=true)");
- } else {
- ATRACE_BEGIN("Submit(sync=false)");
- }
- bool success = grContext->submit(requireSync);
- ATRACE_END();
- if (!success) {
- ALOGE("Failed to flush RenderEngine commands");
- // Chances are, something illegal happened (either the caller passed
- // us bad parameters, or we messed up our shader generation).
- resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
- return;
- }
-
- // checkErrors();
- resultPromise->set_value({NO_ERROR, std::move(drawFence)});
- return;
-}
-
-inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
- return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
-}
-
-inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
- return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
-}
-
-/**
- * Verifies that common, simple bounds + clip combinations can be converted into
- * a single RRect draw call returning true if possible. If true the radii parameter
- * will be filled with the correct radii values that combined with bounds param will
- * produce the insected roundRect. If false, the returned state of the radii param is undefined.
- */
-static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
- const SkRect& insetCrop, const vec2& cornerRadius,
- SkVector radii[4]) {
- const bool leftEqual = bounds.fLeft == crop.fLeft;
- const bool topEqual = bounds.fTop == crop.fTop;
- const bool rightEqual = bounds.fRight == crop.fRight;
- const bool bottomEqual = bounds.fBottom == crop.fBottom;
-
- // In the event that the corners of the bounds only partially align with the crop we
- // need to ensure that the resulting shape can still be represented as a round rect.
- // In particular the round rect implementation will scale the value of all corner radii
- // if the sum of the radius along any edge is greater than the length of that edge.
- // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
- const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
- const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
- if (!requiredWidth || !requiredHeight) {
- return false;
- }
-
- // Check each cropped corner to ensure that it exactly matches the crop or its corner is
- // contained within the cropped shape and does not need rounded.
- // compute the UpperLeft corner radius
- if (leftEqual && topEqual) {
- radii[0].set(cornerRadius.x, cornerRadius.y);
- } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
- (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
- radii[0].set(0, 0);
- } else {
- return false;
- }
- // compute the UpperRight corner radius
- if (rightEqual && topEqual) {
- radii[1].set(cornerRadius.x, cornerRadius.y);
- } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
- (topEqual && bounds.fRight <= insetCrop.fRight)) {
- radii[1].set(0, 0);
- } else {
- return false;
- }
- // compute the BottomRight corner radius
- if (rightEqual && bottomEqual) {
- radii[2].set(cornerRadius.x, cornerRadius.y);
- } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
- (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
- radii[2].set(0, 0);
- } else {
- return false;
- }
- // compute the BottomLeft corner radius
- if (leftEqual && bottomEqual) {
- radii[3].set(cornerRadius.x, cornerRadius.y);
- } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
- (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
- radii[3].set(0, 0);
- } else {
- return false;
- }
-
- return true;
-}
-
-inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
- const FloatRect& cropRect,
- const vec2& cornerRadius) {
- const SkRect bounds = getSkRect(boundsRect);
- const SkRect crop = getSkRect(cropRect);
-
- SkRRect clip;
- if (cornerRadius.x > 0 && cornerRadius.y > 0) {
- // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
- if (bounds == crop || crop.isEmpty()) {
- return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
- }
-
- // This makes an effort to speed up common, simple bounds + clip combinations by
- // converting them to a single RRect draw. It is possible there are other cases
- // that can be converted.
- if (crop.contains(bounds)) {
- const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
- if (insetCrop.contains(bounds)) {
- return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
- }
-
- SkVector radii[4];
- if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
- SkRRect intersectionBounds;
- intersectionBounds.setRectRadii(bounds, radii);
- return {intersectionBounds, clip};
- }
- }
-
- // we didn't hit any of our fast paths so set the clip to the cropRect
- clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
- }
-
- // if we hit this point then we either don't have rounded corners or we are going to rely
- // on the clip to round the corners for us
- return {SkRRect::MakeRect(bounds), clip};
-}
-
-inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings& layer,
- bool colorTransformModifiesAlpha) {
- if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) {
- // return false if the content is opaque and would therefore occlude the blur
- const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque;
- const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha;
- return layer.skipContentDraw || !(opaqueContent && opaqueAlpha);
- }
- return false;
-}
-
-inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
- return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
-}
-
-inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
- return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
- matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
- matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
- matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
-}
-
-inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) {
- return SkPoint3::Make(vector.x, vector.y, vector.z);
-}
-
-size_t SkiaGLRenderEngine::getMaxTextureSize() const {
- return mGrContext->maxTextureSize();
-}
-
-size_t SkiaGLRenderEngine::getMaxViewportDims() const {
- return mGrContext->maxRenderTargetSize();
-}
-
-void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
- const ShadowSettings& settings) {
- ATRACE_CALL();
- const float casterZ = settings.length / 2.0f;
- const auto flags =
- settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
-
- SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
- getSkPoint3(settings.lightPos), settings.lightRadius,
- getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
- flags);
+ return fenceFd;
}
EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
@@ -1570,114 +519,14 @@
return value;
}
-void SkiaGLRenderEngine::onActiveDisplaySizeChanged(ui::Size size) {
- // This cache multiplier was selected based on review of cache sizes relative
- // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
- // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
- // conservative default based on that analysis.
- const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
- const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
-
- // start by resizing the current context
- getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
-
- // if it is possible to switch contexts then we will resize the other context
- const bool originalProtectedState = mInProtectedContext;
- useProtectedContext(!mInProtectedContext);
- if (mInProtectedContext != originalProtectedState) {
- getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
- // reset back to the initial context that was active when this method was called
- useProtectedContext(originalProtectedState);
- }
-}
-
-void SkiaGLRenderEngine::dump(std::string& result) {
+void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
-
- StringAppendF(&result, "\n ------------RE-----------------\n");
+ StringAppendF(&result, "\n ------------RE GLES------------\n");
StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
extensions.getVersion());
StringAppendF(&result, "%s\n", extensions.getExtensions());
- StringAppendF(&result, "RenderEngine supports protected context: %d\n",
- supportsProtectedContent());
- StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
- StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
- mSkSLCacheMonitor.shadersCachedSinceLastCall());
-
- std::vector<ResourcePair> cpuResourceMap = {
- {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
- {"skia/sk_resource_cache/rrect-blur_", "Masks"},
- {"skia/sk_resource_cache/rects-blur_", "Masks"},
- {"skia/sk_resource_cache/tessellated", "Shadows"},
- {"skia", "Other"},
- };
- SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
- SkGraphics::DumpMemoryStatistics(&cpuReporter);
- StringAppendF(&result, "Skia CPU Caches: ");
- cpuReporter.logTotals(result);
- cpuReporter.logOutput(result);
-
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
-
- std::vector<ResourcePair> gpuResourceMap = {
- {"texture_renderbuffer", "Texture/RenderBuffer"},
- {"texture", "Texture"},
- {"gr_text_blob_cache", "Text"},
- {"skia", "Other"},
- };
- SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
- mGrContext->dumpMemoryStatistics(&gpuReporter);
- StringAppendF(&result, "Skia's GPU Caches: ");
- gpuReporter.logTotals(result);
- gpuReporter.logOutput(result);
- StringAppendF(&result, "Skia's Wrapped Objects:\n");
- gpuReporter.logOutput(result, true);
-
- StringAppendF(&result, "RenderEngine tracked buffers: %zu\n",
- mGraphicBufferExternalRefs.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) {
- StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts);
- }
- StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
- mTextureCache.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- // TODO(178539829): It would be nice to know which layer these are coming from and what
- // the texture sizes are.
- for (const auto& [id, unused] : mTextureCache) {
- StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
- }
- StringAppendF(&result, "\n");
-
- SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
- if (mProtectedGrContext) {
- mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
- }
- StringAppendF(&result, "Skia's GPU Protected Caches: ");
- gpuProtectedReporter.logTotals(result);
- gpuProtectedReporter.logOutput(result);
- StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
- gpuProtectedReporter.logOutput(result, true);
-
- StringAppendF(&result, "\n");
- StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
- for (const auto& [linearEffect, unused] : mRuntimeEffects) {
- StringAppendF(&result, "- inputDataspace: %s\n",
- dataspaceDetails(
- static_cast<android_dataspace>(linearEffect.inputDataspace))
- .c_str());
- StringAppendF(&result, "- outputDataspace: %s\n",
- dataspaceDetails(
- static_cast<android_dataspace>(linearEffect.outputDataspace))
- .c_str());
- StringAppendF(&result, "undoPremultipliedAlpha: %s\n",
- linearEffect.undoPremultipliedAlpha ? "true" : "false");
- }
- }
- StringAppendF(&result, "\n");
}
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 34f18c1..4a37ffe 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -55,33 +55,23 @@
SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
EGLSurface placeholder, EGLContext protectedContext,
EGLSurface protectedPlaceholder);
- ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
+ ~SkiaGLRenderEngine() override;
- std::future<void> primeCache() override;
- void cleanupPostRender() override;
- void cleanFramebufferCache() override{};
int getContextPriority() override;
- bool isProtected() const override { return mInProtectedContext; }
- bool supportsProtectedContent() const override;
- void useProtectedContext(bool useProtectedContext) override;
- bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
- void onActiveDisplaySizeChanged(ui::Size size) override;
- int reportShadersCompiled() override;
protected:
- void dump(std::string& result) override;
- size_t getMaxTextureSize() const override;
- size_t getMaxViewportDims() const override;
- void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
- bool canSkipPostRenderCleanup() const override;
- void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
- const DisplaySettings& display,
- const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
+ // Implementations of abstract SkiaRenderEngine functions specific to
+ // rendering backend
+ virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options);
+ bool supportsProtectedContentImpl() const override;
+ bool useProtectedContextImpl(GrProtected isProtected) override;
+ void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override;
+ base::unique_fd flushAndSubmit(GrDirectContext* context) override;
+ void appendBackendSpecificInfoToDump(std::string& result) override;
private:
+ bool waitGpuFence(base::borrowed_fd fenceFd);
+ base::unique_fd flush();
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
EGLContext shareContext,
@@ -89,107 +79,14 @@
Protection protection);
static std::optional<RenderEngine::ContextPriority> createContextPriority(
const RenderEngineCreationArgs& args);
- static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection);
- inline SkRect getSkRect(const FloatRect& layer);
- inline SkRect getSkRect(const Rect& layer);
- inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
- const FloatRect& crop,
- const vec2& cornerRadius);
- inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
- inline SkColor getSkColor(const vec4& color);
- inline SkM44 getSkM44(const mat4& matrix);
- inline SkPoint3 getSkPoint3(const vec3& vector);
- inline GrDirectContext* getActiveGrContext() const;
-
- base::unique_fd flush();
- // waitFence attempts to wait in the GPU, and if unable to waits on the CPU instead.
- void waitFence(base::borrowed_fd fenceFd);
- bool waitGpuFence(base::borrowed_fd fenceFd);
-
- void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
- void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
- const ShadowSettings& shadowSettings);
-
- // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
- // Otherwise it returns the input shader.
- struct RuntimeEffectShaderParameters {
- sk_sp<SkShader> shader;
- const LayerSettings& layer;
- const DisplaySettings& display;
- bool undoPremultipliedAlpha;
- bool requiresLinearEffect;
- float layerDimmingRatio;
- };
- sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
+ static EGLSurface createPlaceholderEglPbufferSurface(
+ EGLDisplay display, EGLConfig config, int hwcFormat, Protection protection);
EGLDisplay mEGLDisplay;
EGLContext mEGLContext;
EGLSurface mPlaceholderSurface;
EGLContext mProtectedEGLContext;
EGLSurface mProtectedPlaceholderSurface;
- BlurFilter* mBlurFilter = nullptr;
-
- const PixelFormat mDefaultPixelFormat;
- const bool mUseColorManagement;
-
- // Identifier used or various mappings of layers to various
- // textures or shaders
- using GraphicBufferId = uint64_t;
-
- // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
- std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
- GUARDED_BY(mRenderingMutex);
- // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
- std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
- GUARDED_BY(mRenderingMutex);
- std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
- mRuntimeEffects;
- AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
-
- StretchShaderFactory mStretchShaderFactory;
- // Mutex guarding rendering operations, so that:
- // 1. GL operations aren't interleaved, and
- // 2. Internal state related to rendering that is potentially modified by
- // multiple threads is guaranteed thread-safe.
- mutable std::mutex mRenderingMutex;
-
- sp<Fence> mLastDrawFence;
-
- // Graphics context used for creating surfaces and submitting commands
- sk_sp<GrDirectContext> mGrContext;
- // Same as above, but for protected content (eg. DRM)
- sk_sp<GrDirectContext> mProtectedGrContext;
-
- bool mInProtectedContext = false;
- // Object to capture commands send to Skia.
- std::unique_ptr<SkiaCapture> mCapture;
-
- // Implements PersistentCache as a way to monitor what SkSL shaders Skia has
- // cached.
- class SkSLCacheMonitor : public GrContextOptions::PersistentCache {
- public:
- SkSLCacheMonitor() = default;
- ~SkSLCacheMonitor() override = default;
-
- sk_sp<SkData> load(const SkData& key) override;
-
- void store(const SkData& key, const SkData& data, const SkString& description) override;
-
- int shadersCachedSinceLastCall() {
- const int shadersCachedSinceLastCall = mShadersCachedSinceLastCall;
- mShadersCachedSinceLastCall = 0;
- return shadersCachedSinceLastCall;
- }
-
- int totalShadersCompiled() const { return mTotalShadersCompiled; }
-
- private:
- int mShadersCachedSinceLastCall = 0;
- int mTotalShadersCompiled = 0;
- };
-
- SkSLCacheMonitor mSkSLCacheMonitor;
};
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 1fb24f5..db983a8 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,16 +20,1244 @@
#include "SkiaRenderEngine.h"
+#include <GrBackendSemaphore.h>
+#include <GrContextOptions.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
+#include <SkColorSpace.h>
+#include <SkData.h>
+#include <SkGraphics.h>
+#include <SkImage.h>
+#include <SkImageFilters.h>
+#include <SkImageInfo.h>
+#include <SkM44.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPoint.h>
+#include <SkPoint3.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkRegion.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkSamplingOptions.h>
+#include <SkScalar.h>
+#include <SkShader.h>
+#include <SkShadowUtils.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <SkTileMode.h>
#include <src/core/SkTraceEventCommon.h>
+#include <android-base/stringprintf.h>
+#include <gui/TraceUtils.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/DataspaceUtils.h>
+#include <ui/DebugUtils.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+
+#include <cmath>
+#include <cstdint>
+#include <memory>
+#include <numeric>
+
+#include "Cache.h"
+#include "ColorSpaces.h"
+#include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
+#include "filters/LinearEffect.h"
+#include "log/log_main.h"
+#include "skia/debug/SkiaCapture.h"
+#include "skia/debug/SkiaMemoryReporter.h"
+#include "skia/filters/StretchShaderFactory.h"
+#include "system/graphics-base-v1.0.h"
+
+namespace {
+
+// Debugging settings
+static const bool kPrintLayerSettings = false;
+static const bool kFlushAfterEveryLayer = kPrintLayerSettings;
+
+} // namespace
+
+// Utility functions related to SkRect
+
+namespace {
+
+static inline SkRect getSkRect(const android::FloatRect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+static inline SkRect getSkRect(const android::Rect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+/**
+ * Verifies that common, simple bounds + clip combinations can be converted into
+ * a single RRect draw call returning true if possible. If true the radii parameter
+ * will be filled with the correct radii values that combined with bounds param will
+ * produce the insected roundRect. If false, the returned state of the radii param is undefined.
+ */
+static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
+ const SkRect& insetCrop, const android::vec2& cornerRadius,
+ SkVector radii[4]) {
+ const bool leftEqual = bounds.fLeft == crop.fLeft;
+ const bool topEqual = bounds.fTop == crop.fTop;
+ const bool rightEqual = bounds.fRight == crop.fRight;
+ const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+ // In the event that the corners of the bounds only partially align with the crop we
+ // need to ensure that the resulting shape can still be represented as a round rect.
+ // In particular the round rect implementation will scale the value of all corner radii
+ // if the sum of the radius along any edge is greater than the length of that edge.
+ // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
+ const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
+ const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
+ if (!requiredWidth || !requiredHeight) {
+ return false;
+ }
+
+ // Check each cropped corner to ensure that it exactly matches the crop or its corner is
+ // contained within the cropped shape and does not need rounded.
+ // compute the UpperLeft corner radius
+ if (leftEqual && topEqual) {
+ radii[0].set(cornerRadius.x, cornerRadius.y);
+ } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
+ radii[0].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the UpperRight corner radius
+ if (rightEqual && topEqual) {
+ radii[1].set(cornerRadius.x, cornerRadius.y);
+ } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fRight <= insetCrop.fRight)) {
+ radii[1].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the BottomRight corner radius
+ if (rightEqual && bottomEqual) {
+ radii[2].set(cornerRadius.x, cornerRadius.y);
+ } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
+ radii[2].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the BottomLeft corner radius
+ if (leftEqual && bottomEqual) {
+ radii[3].set(cornerRadius.x, cornerRadius.y);
+ } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
+ radii[3].set(0, 0);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const android::FloatRect& boundsRect,
+ const android::FloatRect& cropRect,
+ const android::vec2& cornerRadius) {
+ const SkRect bounds = getSkRect(boundsRect);
+ const SkRect crop = getSkRect(cropRect);
+
+ SkRRect clip;
+ if (cornerRadius.x > 0 && cornerRadius.y > 0) {
+ // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
+ if (bounds == crop || crop.isEmpty()) {
+ return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
+ }
+
+ // This makes an effort to speed up common, simple bounds + clip combinations by
+ // converting them to a single RRect draw. It is possible there are other cases
+ // that can be converted.
+ if (crop.contains(bounds)) {
+ const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
+ if (insetCrop.contains(bounds)) {
+ return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
+ }
+
+ SkVector radii[4];
+ if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
+ SkRRect intersectionBounds;
+ intersectionBounds.setRectRadii(bounds, radii);
+ return {intersectionBounds, clip};
+ }
+ }
+
+ // we didn't hit any of our fast paths so set the clip to the cropRect
+ clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
+ }
+
+ // if we hit this point then we either don't have rounded corners or we are going to rely
+ // on the clip to round the corners for us
+ return {SkRRect::MakeRect(bounds), clip};
+}
+
+static inline bool layerHasBlur(const android::renderengine::LayerSettings& layer,
+ bool colorTransformModifiesAlpha) {
+ if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) {
+ // return false if the content is opaque and would therefore occlude the blur
+ const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque;
+ const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha;
+ return layer.skipContentDraw || !(opaqueContent && opaqueAlpha);
+ }
+ return false;
+}
+
+static inline SkColor getSkColor(const android::vec4& color) {
+ return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
+}
+
+static inline SkM44 getSkM44(const android::mat4& matrix) {
+ return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
+ matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
+ matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
+ matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
+}
+
+static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
+ return SkPoint3::Make(vector.x, vector.y, vector.z);
+}
+
+} // namespace
namespace android {
namespace renderengine {
namespace skia {
-SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
+
+using base::StringAppendF;
+
+std::future<void> SkiaRenderEngine::primeCache() {
+ Cache::primeShaderCache(this);
+ return {};
+}
+
+sk_sp<SkData> SkiaRenderEngine::SkSLCacheMonitor::load(const SkData& key) {
+ // This "cache" does not actually cache anything. It just allows us to
+ // monitor Skia's internal cache. So this method always returns null.
+ return nullptr;
+}
+
+void SkiaRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data,
+ const SkString& description) {
+ mShadersCachedSinceLastCall++;
+ mTotalShadersCompiled++;
+ ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled);
+}
+
+int SkiaRenderEngine::reportShadersCompiled() {
+ return mSkSLCacheMonitor.totalShadersCompiled();
+}
void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) {
SkAndroidFrameworkTraceUtil::setEnableTracing(tracingEnabled);
}
+
+SkiaRenderEngine::SkiaRenderEngine(
+ RenderEngineType type,
+ PixelFormat pixelFormat,
+ bool useColorManagement,
+ bool supportsBackgroundBlur) :
+ RenderEngine(type),
+ mDefaultPixelFormat(pixelFormat),
+ mUseColorManagement(useColorManagement) {
+ if (supportsBackgroundBlur) {
+ ALOGD("Background Blurs Enabled");
+ mBlurFilter = new KawaseBlurFilter();
+ }
+ mCapture = std::make_unique<SkiaCapture>();
+}
+
+SkiaRenderEngine::~SkiaRenderEngine() { }
+
+// To be called from backend dtors.
+void SkiaRenderEngine::finishRenderingAndAbandonContext() {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+
+ if (mBlurFilter) {
+ delete mBlurFilter;
+ }
+
+ if (mGrContext) {
+ mGrContext->flushAndSubmit(true);
+ mGrContext->abandonContext();
+ }
+
+ if (mProtectedGrContext) {
+ mProtectedGrContext->flushAndSubmit(true);
+ mProtectedGrContext->abandonContext();
+ }
+}
+
+void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) {
+ if (useProtectedContext == mInProtectedContext ||
+ (useProtectedContext && !supportsProtectedContent())) {
+ return;
+ }
+
+ // release any scratch resources before switching into a new mode
+ if (getActiveGrContext()) {
+ getActiveGrContext()->purgeUnlockedResources(true);
+ }
+
+ // Backend-specific way to switch to protected context
+ if (useProtectedContextImpl(
+ useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) {
+ mInProtectedContext = useProtectedContext;
+ // given that we are sharing the same thread between two GrContexts we need to
+ // make sure that the thread state is reset when switching between the two.
+ if (getActiveGrContext()) {
+ getActiveGrContext()->resetContext();
+ }
+ }
+}
+
+GrDirectContext* SkiaRenderEngine::getActiveGrContext() {
+ return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
+}
+
+static float toDegrees(uint32_t transform) {
+ switch (transform) {
+ case ui::Transform::ROT_90:
+ return 90.0;
+ case ui::Transform::ROT_180:
+ return 180.0;
+ case ui::Transform::ROT_270:
+ return 270.0;
+ default:
+ return 0.0;
+ }
+}
+
+static SkColorMatrix toSkColorMatrix(const android::mat4& matrix) {
+ return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
+ matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
+ matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
+ matrix[3][3], 0);
+}
+
+static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
+ int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
+ int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ // Treat unsupported dataspaces as srgb
+ if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+ destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+ destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+ destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+ }
+
+ if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+ sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+ sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+ sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+ }
+
+ const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+ const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+ const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+ const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+
+ return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
+ sourceTransfer != destTransfer;
+}
+
+void SkiaRenderEngine::ensureGrContextsCreated() {
+ if (mGrContext) {
+ return;
+ }
+
+ GrContextOptions options;
+ options.fDisableDriverCorrectnessWorkarounds = true;
+ options.fDisableDistanceFieldPaths = true;
+ options.fReducedShaderVariations = true;
+ options.fPersistentCache = &mSkSLCacheMonitor;
+ std::tie(mGrContext, mProtectedGrContext) = createDirectContexts(options);
+}
+
+void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+ bool isRenderable) {
+ // Only run this if RE is running on its own thread. This way the access to GL
+ // operations is guaranteed to be happening on the same thread.
+ if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
+ return;
+ }
+ // We currently don't attempt to map a buffer if the buffer contains protected content
+ // because GPU resources for protected buffers is much more limited.
+ const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+ if (isProtectedBuffer) {
+ return;
+ }
+ ATRACE_CALL();
+
+ // If we were to support caching protected buffers then we will need to switch the
+ // currently bound context if we are not already using the protected context (and subsequently
+ // switch back after the buffer is cached). However, for non-protected content we can bind
+ // the texture in either GL context because they are initialized with the same share_context
+ // which allows the texture state to be shared between them.
+ auto grContext = getActiveGrContext();
+ auto& cache = mTextureCache;
+
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mGraphicBufferExternalRefs[buffer->getId()]++;
+
+ if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
+ std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
+ buffer->toAHardwareBuffer(),
+ isRenderable, mTextureCleanupMgr);
+ cache.insert({buffer->getId(), imageTextureRef});
+ }
+}
+
+void SkiaRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
+ iter != mGraphicBufferExternalRefs.end()) {
+ if (iter->second == 0) {
+ ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64
+ "> from RenderEngine texture, but the "
+ "ref count was already zero!",
+ buffer->getId());
+ mGraphicBufferExternalRefs.erase(buffer->getId());
+ return;
+ }
+
+ iter->second--;
+
+ // Swap contexts if needed prior to deleting this buffer
+ // See Issue 1 of
+ // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt: even
+ // when a protected context and an unprotected context are part of the same share group,
+ // protected surfaces may not be accessed by an unprotected context, implying that protected
+ // surfaces may only be freed when a protected context is active.
+ const bool inProtected = mInProtectedContext;
+ useProtectedContext(buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+
+ if (iter->second == 0) {
+ mTextureCache.erase(buffer->getId());
+ mGraphicBufferExternalRefs.erase(buffer->getId());
+ }
+
+ // Swap back to the previous context so that cached values of isProtected in SurfaceFlinger
+ // are up-to-date.
+ if (inProtected != mInProtectedContext) {
+ useProtectedContext(inProtected);
+ }
+ }
+}
+
+bool SkiaRenderEngine::canSkipPostRenderCleanup() const {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return mTextureCleanupMgr.isEmpty();
+}
+
+void SkiaRenderEngine::cleanupPostRender() {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mTextureCleanupMgr.cleanup();
+}
+
+sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader(
+ const RuntimeEffectShaderParameters& parameters) {
+ // The given surface will be stretched by HWUI via matrix transformation
+ // which gets similar results for most surfaces
+ // Determine later on if we need to leverage the stertch shader within
+ // surface flinger
+ const auto& stretchEffect = parameters.layer.stretchEffect;
+ auto shader = parameters.shader;
+ if (stretchEffect.hasEffect()) {
+ const auto targetBuffer = parameters.layer.source.buffer.buffer;
+ const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+ if (graphicBuffer && parameters.shader) {
+ shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
+ }
+ }
+
+ if (parameters.requiresLinearEffect) {
+ const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
+ const ui::Dataspace outputDataspace = mUseColorManagement
+ ? parameters.display.outputDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
+
+ auto effect =
+ shaders::LinearEffect{.inputDataspace = inputDataspace,
+ .outputDataspace = outputDataspace,
+ .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
+
+ auto effectIter = mRuntimeEffects.find(effect);
+ sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
+ if (effectIter == mRuntimeEffects.end()) {
+ runtimeEffect = buildRuntimeEffect(effect);
+ mRuntimeEffects.insert({effect, runtimeEffect});
+ } else {
+ runtimeEffect = effectIter->second;
+ }
+ mat4 colorTransform = parameters.layer.colorTransform;
+
+ colorTransform *=
+ mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
+ parameters.layerDimmingRatio, 1.f));
+ const auto targetBuffer = parameters.layer.source.buffer.buffer;
+ const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+ const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr;
+ return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
+ parameters.display.maxLuminance,
+ parameters.display.currentLuminanceNits,
+ parameters.layer.source.buffer.maxLuminanceNits,
+ hardwareBuffer, parameters.display.renderIntent);
+ }
+ return parameters.shader;
+}
+
+void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
+ if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+ // Record display settings when capture is running.
+ std::stringstream displaySettings;
+ PrintTo(display, &displaySettings);
+ // Store the DisplaySettings in additional information.
+ canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
+ SkData::MakeWithCString(displaySettings.str().c_str()));
+ }
+
+ // Before doing any drawing, let's make sure that we'll start at the origin of the display.
+ // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
+ // displays might have different scaling when compared to the physical screen.
+
+ canvas->clipRect(getSkRect(display.physicalDisplay));
+ canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
+
+ const auto clipWidth = display.clip.width();
+ const auto clipHeight = display.clip.height();
+ auto rotatedClipWidth = clipWidth;
+ auto rotatedClipHeight = clipHeight;
+ // Scale is contingent on the rotation result.
+ if (display.orientation & ui::Transform::ROT_90) {
+ std::swap(rotatedClipWidth, rotatedClipHeight);
+ }
+ const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
+ static_cast<SkScalar>(rotatedClipWidth);
+ const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
+ static_cast<SkScalar>(rotatedClipHeight);
+ canvas->scale(scaleX, scaleY);
+
+ // Canvas rotation is done by centering the clip window at the origin, rotating, translating
+ // back so that the top left corner of the clip is at (0, 0).
+ canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
+ canvas->rotate(toDegrees(display.orientation));
+ canvas->translate(-clipWidth / 2, -clipHeight / 2);
+ canvas->translate(-display.clip.left, -display.clip.top);
+}
+
+class AutoSaveRestore {
+public:
+ AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); }
+ ~AutoSaveRestore() { restore(); }
+ void replace(SkCanvas* canvas) {
+ mCanvas = canvas;
+ mSaveCount = canvas->save();
+ }
+ void restore() {
+ if (mCanvas) {
+ mCanvas->restoreToCount(mSaveCount);
+ mCanvas = nullptr;
+ }
+ }
+
+private:
+ SkCanvas* mCanvas;
+ int mSaveCount;
+};
+
+static SkRRect getBlurRRect(const BlurRegion& region) {
+ const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
+ const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
+ SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
+ SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
+ SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
+ SkRRect roundedRect;
+ roundedRect.setRectRadii(rect, radii);
+ return roundedRect;
+}
+
+// Arbitrary default margin which should be close enough to zero.
+constexpr float kDefaultMargin = 0.0001f;
+static bool equalsWithinMargin(float expected, float value, float margin = kDefaultMargin) {
+ LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
+ return std::abs(expected - value) < margin;
+}
+
+namespace {
+template <typename T>
+void logSettings(const T& t) {
+ std::stringstream stream;
+ PrintTo(t, &stream);
+ auto string = stream.str();
+ size_t pos = 0;
+ // Perfetto ignores \n, so split up manually into separate ALOGD statements.
+ const size_t size = string.size();
+ while (pos < size) {
+ const size_t end = std::min(string.find("\n", pos), size);
+ ALOGD("%s", string.substr(pos, end - pos).c_str());
+ pos = end + 1;
+ }
+}
+} // namespace
+
+// Helper class intended to be used on the stack to ensure that texture cleanup
+// is deferred until after this class goes out of scope.
+class DeferTextureCleanup final {
+public:
+ DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) {
+ mMgr.setDeferredStatus(true);
+ }
+ ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup);
+ AutoBackendTexture::CleanupManager& mMgr;
+};
+
+void SkiaRenderEngine::drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
+ base::unique_fd&& bufferFence) {
+ ATRACE_NAME("SkiaGL::drawLayersInternal");
+
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+
+ if (buffer == nullptr) {
+ ALOGE("No output buffer provided. Aborting GPU composition.");
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
+ }
+
+ validateOutputBufferUsage(buffer->getBuffer());
+
+ auto grContext = getActiveGrContext();
+ auto& cache = mTextureCache;
+
+ // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
+ DeferTextureCleanup dtc(mTextureCleanupMgr);
+
+ std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
+ if (const auto& it = cache.find(buffer->getBuffer()->getId()); it != cache.end()) {
+ surfaceTextureRef = it->second;
+ } else {
+ surfaceTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
+ buffer->getBuffer()
+ ->toAHardwareBuffer(),
+ true, mTextureCleanupMgr);
+ }
+
+ // wait on the buffer to be ready to use prior to using it
+ waitFence(grContext, bufferFence);
+
+ const ui::Dataspace dstDataspace =
+ mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
+ sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
+
+ SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
+ if (dstCanvas == nullptr) {
+ ALOGE("Cannot acquire canvas from Skia.");
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
+ }
+
+ // setup color filter if necessary
+ sk_sp<SkColorFilter> displayColorTransform;
+ if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) {
+ displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+ }
+ const bool ctModifiesAlpha =
+ displayColorTransform && !displayColorTransform->isAlphaUnchanged();
+
+ // Find the max layer white point to determine the max luminance of the scene...
+ const float maxLayerWhitePoint = std::transform_reduce(
+ layers.cbegin(), layers.cend(), 0.f,
+ [](float left, float right) { return std::max(left, right); },
+ [&](const auto& l) { return l.whitePointNits; });
+
+ // ...and compute the dimming ratio if dimming is requested
+ const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
+ maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+ ? maxLayerWhitePoint / display.targetLuminanceNits
+ : 1.f;
+
+ // Find if any layers have requested blur, we'll use that info to decide when to render to an
+ // offscreen buffer and when to render to the native buffer.
+ sk_sp<SkSurface> activeSurface(dstSurface);
+ SkCanvas* canvas = dstCanvas;
+ SkiaCapture::OffscreenState offscreenCaptureState;
+ const LayerSettings* blurCompositionLayer = nullptr;
+ if (mBlurFilter) {
+ bool requiresCompositionLayer = false;
+ for (const auto& layer : layers) {
+ // if the layer doesn't have blur or it is not visible then continue
+ if (!layerHasBlur(layer, ctModifiesAlpha)) {
+ continue;
+ }
+ if (layer.backgroundBlurRadius > 0 &&
+ layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
+ requiresCompositionLayer = true;
+ }
+ for (auto region : layer.blurRegions) {
+ if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
+ requiresCompositionLayer = true;
+ }
+ }
+ if (requiresCompositionLayer) {
+ activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
+ canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
+ blurCompositionLayer = &layer;
+ break;
+ }
+ }
+ }
+
+ AutoSaveRestore surfaceAutoSaveRestore(canvas);
+ // Clear the entire canvas with a transparent black to prevent ghost images.
+ canvas->clear(SK_ColorTRANSPARENT);
+ initCanvas(canvas, display);
+
+ if (kPrintLayerSettings) {
+ logSettings(display);
+ }
+ for (const auto& layer : layers) {
+ ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
+
+ if (kPrintLayerSettings) {
+ logSettings(layer);
+ }
+
+ sk_sp<SkImage> blurInput;
+ if (blurCompositionLayer == &layer) {
+ LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
+ LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
+
+ // save a snapshot of the activeSurface to use as input to the blur shaders
+ blurInput = activeSurface->makeImageSnapshot();
+
+ // blit the offscreen framebuffer into the destination AHB, but only
+ // if there are blur regions. backgroundBlurRadius blurs the entire
+ // image below, so it can skip this step.
+ if (layer.blurRegions.size()) {
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+ uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
+ dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
+ String8::format("SurfaceID|%" PRId64, id).c_str(),
+ nullptr);
+ dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
+ } else {
+ activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+ }
+ }
+
+ // assign dstCanvas to canvas and ensure that the canvas state is up to date
+ canvas = dstCanvas;
+ surfaceAutoSaveRestore.replace(canvas);
+ initCanvas(canvas, display);
+
+ LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
+ dstSurface->getCanvas()->getSaveCount());
+ LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
+ dstSurface->getCanvas()->getTotalMatrix());
+
+ // assign dstSurface to activeSurface
+ activeSurface = dstSurface;
+ }
+
+ SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
+ if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+ // Record the name of the layer if the capture is running.
+ std::stringstream layerSettings;
+ PrintTo(layer, &layerSettings);
+ // Store the LayerSettings in additional information.
+ canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(),
+ SkData::MakeWithCString(layerSettings.str().c_str()));
+ }
+ // Layers have a local transform that should be applied to them
+ canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());
+
+ const auto [bounds, roundRectClip] =
+ getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
+ if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
+ std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
+
+ // if multiple layers have blur, then we need to take a snapshot now because
+ // only the lowest layer will have blurImage populated earlier
+ if (!blurInput) {
+ blurInput = activeSurface->makeImageSnapshot();
+ }
+ // rect to be blurred in the coordinate space of blurInput
+ const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+
+ // if the clip needs to be applied then apply it now and make sure
+ // it is restored before we attempt to draw any shadows.
+ SkAutoCanvasRestore acr(canvas, true);
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
+
+ // TODO(b/182216890): Filter out empty layers earlier
+ if (blurRect.width() > 0 && blurRect.height() > 0) {
+ if (layer.backgroundBlurRadius > 0) {
+ ATRACE_NAME("BackgroundBlur");
+ auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius,
+ blurInput, blurRect);
+
+ cachedBlurs[layer.backgroundBlurRadius] = blurredImage;
+
+ mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f,
+ blurRect, blurredImage, blurInput);
+ }
+
+ canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
+ for (auto region : layer.blurRegions) {
+ if (cachedBlurs[region.blurRadius] == nullptr) {
+ ATRACE_NAME("BlurRegion");
+ cachedBlurs[region.blurRadius] =
+ mBlurFilter->generate(grContext, region.blurRadius, blurInput,
+ blurRect);
+ }
+
+ mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
+ region.alpha, blurRect,
+ cachedBlurs[region.blurRadius], blurInput);
+ }
+ }
+ }
+
+ if (layer.shadow.length > 0) {
+ // This would require a new parameter/flag to SkShadowUtils::DrawShadow
+ LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow");
+
+ SkRRect shadowBounds, shadowClip;
+ if (layer.geometry.boundaries == layer.shadow.boundaries) {
+ shadowBounds = bounds;
+ shadowClip = roundRectClip;
+ } else {
+ std::tie(shadowBounds, shadowClip) =
+ getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
+ }
+
+ // Technically, if bounds is a rect and roundRectClip is not empty,
+ // it means that the bounds and roundedCornersCrop were different
+ // enough that we should intersect them to find the proper shadow.
+ // In practice, this often happens when the two rectangles appear to
+ // not match due to rounding errors. Draw the rounded version, which
+ // looks more like the intent.
+ const auto& rrect =
+ shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
+ drawShadow(canvas, rrect, layer.shadow);
+ }
+
+ const float layerDimmingRatio = layer.whitePointNits <= 0.f
+ ? displayDimmingRatio
+ : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
+
+ const bool dimInLinearSpace = display.dimmingStage !=
+ aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF;
+
+ const bool requiresLinearEffect = layer.colorTransform != mat4() ||
+ (mUseColorManagement &&
+ needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
+ (dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio));
+
+ // quick abort from drawing the remaining portion of the layer
+ if (layer.skipContentDraw ||
+ (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending &&
+ (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
+ continue;
+ }
+
+ // If we need to map to linear space or color management is disabled, then mark the source
+ // image with the same colorspace as the destination surface so that Skia's color
+ // management is a no-op.
+ const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
+ ? dstDataspace
+ : layer.sourceDataspace;
+
+ SkPaint paint;
+ if (layer.source.buffer.buffer) {
+ ATRACE_NAME("DrawImage");
+ validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
+ const auto& item = layer.source.buffer;
+ std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
+
+ if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
+ iter != cache.end()) {
+ imageTextureRef = iter->second;
+ } else {
+ // If we didn't find the image in the cache, then create a local ref but don't cache
+ // it. If we're using skia, we're guaranteed to run on a dedicated GPU thread so if
+ // we didn't find anything in the cache then we intentionally did not cache this
+ // buffer's resources.
+ imageTextureRef = std::make_shared<
+ AutoBackendTexture::LocalRef>(grContext,
+ item.buffer->getBuffer()->toAHardwareBuffer(),
+ false, mTextureCleanupMgr);
+ }
+
+ // if the layer's buffer has a fence, then we must must respect the fence prior to using
+ // the buffer.
+ if (layer.source.buffer.fence != nullptr) {
+ waitFence(grContext, layer.source.buffer.fence->get());
+ }
+
+ // isOpaque means we need to ignore the alpha in the image,
+ // replacing it with the alpha specified by the LayerSettings. See
+ // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
+ // The proper way to do this is to use an SkColorType that ignores
+ // alpha, like kRGB_888x_SkColorType, and that is used if the
+ // incoming image is kRGBA_8888_SkColorType. However, the incoming
+ // image may be kRGBA_F16_SkColorType, for which there is no RGBX
+ // SkColorType, or kRGBA_1010102_SkColorType, for which we have
+ // kRGB_101010x_SkColorType, but it is not yet supported as a source
+ // on the GPU. (Adding both is tracked in skbug.com/12048.) In the
+ // meantime, we'll use a workaround that works unless we need to do
+ // any color conversion. The workaround requires that we pretend the
+ // image is already premultiplied, so that we do not premultiply it
+ // before applying SkBlendMode::kPlus.
+ const bool useIsOpaqueWorkaround = item.isOpaque &&
+ (imageTextureRef->colorType() == kRGBA_1010102_SkColorType ||
+ imageTextureRef->colorType() == kRGBA_F16_SkColorType);
+ const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType
+ : item.isOpaque ? kOpaque_SkAlphaType
+ : item.usePremultipliedAlpha ? kPremul_SkAlphaType
+ : kUnpremul_SkAlphaType;
+ sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext);
+
+ auto texMatrix = getSkM44(item.textureTransform).asM33();
+ // textureTansform was intended to be passed directly into a shader, so when
+ // building the total matrix with the textureTransform we need to first
+ // normalize it, then apply the textureTransform, then scale back up.
+ texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
+ texMatrix.postScale(image->width(), image->height());
+
+ SkMatrix matrix;
+ if (!texMatrix.invert(&matrix)) {
+ matrix = texMatrix;
+ }
+ // The shader does not respect the translation, so we add it to the texture
+ // transform for the SkImage. This will make sure that the correct layer contents
+ // are drawn in the correct part of the screen.
+ matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
+
+ sk_sp<SkShader> shader;
+
+ if (layer.source.buffer.useTextureFiltering) {
+ shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(
+ {SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ &matrix);
+ } else {
+ shader = image->makeShader(SkSamplingOptions(), matrix);
+ }
+
+ if (useIsOpaqueWorkaround) {
+ shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
+ SkShaders::Color(SkColors::kBlack,
+ toSkColorSpace(layerDataspace)));
+ }
+
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = !item.isOpaque &&
+ item.usePremultipliedAlpha,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = dimInLinearSpace
+ ? layerDimmingRatio
+ : 1.f}));
+
+ // Turn on dithering when dimming beyond this (arbitrary) threshold...
+ static constexpr float kDimmingThreshold = 0.2f;
+ // ...or we're rendering an HDR layer down to an 8-bit target
+ // Most HDR standards require at least 10-bits of color depth for source content, so we
+ // can just extract the transfer function rather than dig into precise gralloc layout.
+ // Furthermore, we can assume that the only 8-bit target we support is RGBA8888.
+ const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) &&
+ buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888;
+ if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) {
+ paint.setDither(true);
+ }
+ paint.setAlphaf(layer.alpha);
+
+ if (imageTextureRef->colorType() == kAlpha_8_SkColorType) {
+ LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8");
+
+ // SysUI creates the alpha layer as a coverage layer, which is
+ // appropriate for the DPU. Use a color matrix to convert it to
+ // a mask.
+ // TODO (b/219525258): Handle input as a mask.
+ //
+ // The color matrix will convert A8 pixels with no alpha to
+ // black, as described by this vector. If the display handles
+ // the color transform, we need to invert it to find the color
+ // that will result in black after the DPU applies the transform.
+ SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a
+ if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) {
+ SkM44 colorSpaceMatrix = getSkM44(display.colorTransform);
+ if (colorSpaceMatrix.invert(&colorSpaceMatrix)) {
+ black = colorSpaceMatrix * black;
+ } else {
+ // We'll just have to use 0,0,0 as black, which should
+ // be close to correct.
+ ALOGI("Could not invert colorTransform!");
+ }
+ }
+ SkColorMatrix colorMatrix(0, 0, 0, 0, black[0],
+ 0, 0, 0, 0, black[1],
+ 0, 0, 0, 0, black[2],
+ 0, 0, 0, -1, 1);
+ if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) {
+ // On the other hand, if the device doesn't handle it, we
+ // have to apply it ourselves.
+ colorMatrix.postConcat(toSkColorMatrix(display.colorTransform));
+ }
+ paint.setColorFilter(SkColorFilters::Matrix(colorMatrix));
+ }
+ } else {
+ ATRACE_NAME("DrawColor");
+ const auto color = layer.source.solidColor;
+ sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
+ .fG = color.g,
+ .fB = color.b,
+ .fA = layer.alpha},
+ toSkColorSpace(layerDataspace));
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = false,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
+ }
+
+ if (layer.disableBlending) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+
+ // An A8 buffer will already have the proper color filter attached to
+ // its paint, including the displayColorTransform as needed.
+ if (!paint.getColorFilter()) {
+ if (!dimInLinearSpace && !equalsWithinMargin(1.0, layerDimmingRatio)) {
+ // If we don't dim in linear space, then when we gamma correct the dimming ratio we
+ // can assume a gamma 2.2 transfer function.
+ static constexpr float kInverseGamma22 = 1.f / 2.2f;
+ const auto gammaCorrectedDimmingRatio =
+ std::pow(layerDimmingRatio, kInverseGamma22);
+ auto dimmingMatrix =
+ mat4::scale(vec4(gammaCorrectedDimmingRatio, gammaCorrectedDimmingRatio,
+ gammaCorrectedDimmingRatio, 1.f));
+
+ const auto colorFilter =
+ SkColorFilters::Matrix(toSkColorMatrix(std::move(dimmingMatrix)));
+ paint.setColorFilter(displayColorTransform
+ ? displayColorTransform->makeComposed(colorFilter)
+ : colorFilter);
+ } else {
+ paint.setColorFilter(displayColorTransform);
+ }
+ }
+
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
+
+ if (!bounds.isRect()) {
+ paint.setAntiAlias(true);
+ canvas->drawRRect(bounds, paint);
+ } else {
+ canvas->drawRect(bounds.rect(), paint);
+ }
+ if (kFlushAfterEveryLayer) {
+ ATRACE_NAME("flush surface");
+ activeSurface->flush();
+ }
+ }
+ for (const auto& borderRenderInfo : display.borderInfoList) {
+ SkPaint p;
+ p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g,
+ borderRenderInfo.color.b, borderRenderInfo.color.a});
+ p.setAntiAlias(true);
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeWidth(borderRenderInfo.width);
+ SkRegion sk_region;
+ SkPath path;
+
+ // Construct a final SkRegion using Regions
+ for (const auto& r : borderRenderInfo.combinedRegion) {
+ sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op);
+ }
+
+ sk_region.getBoundaryPath(&path);
+ canvas->drawPath(path, p);
+ path.close();
+ }
+
+ surfaceAutoSaveRestore.restore();
+ mCapture->endCapture();
+ {
+ ATRACE_NAME("flush surface");
+ LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
+ activeSurface->flush();
+ }
+
+ base::unique_fd drawFence = flushAndSubmit(grContext);
+ resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+ return;
+}
+
+size_t SkiaRenderEngine::getMaxTextureSize() const {
+ return mGrContext->maxTextureSize();
+}
+
+size_t SkiaRenderEngine::getMaxViewportDims() const {
+ return mGrContext->maxRenderTargetSize();
+}
+
+void SkiaRenderEngine::drawShadow(SkCanvas* canvas,
+ const SkRRect& casterRRect,
+ const ShadowSettings& settings) {
+ ATRACE_CALL();
+ const float casterZ = settings.length / 2.0f;
+ const auto flags =
+ settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
+
+ SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
+ getSkPoint3(settings.lightPos), settings.lightRadius,
+ getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
+ flags);
+}
+
+void SkiaRenderEngine::onActiveDisplaySizeChanged(ui::Size size) {
+ // This cache multiplier was selected based on review of cache sizes relative
+ // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
+ // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
+ // conservative default based on that analysis.
+ const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat);
+ const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
+
+ // start by resizing the current context
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
+
+ // if it is possible to switch contexts then we will resize the other context
+ const bool originalProtectedState = mInProtectedContext;
+ useProtectedContext(!mInProtectedContext);
+ if (mInProtectedContext != originalProtectedState) {
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
+ // reset back to the initial context that was active when this method was called
+ useProtectedContext(originalProtectedState);
+ }
+}
+
+void SkiaRenderEngine::dump(std::string& result) {
+ // Dump for the specific backend (GLES or Vk)
+ appendBackendSpecificInfoToDump(result);
+
+ // Info about protected content
+ StringAppendF(&result, "RenderEngine supports protected context: %d\n",
+ supportsProtectedContent());
+ StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
+ StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n",
+ mSkSLCacheMonitor.shadersCachedSinceLastCall());
+
+ std::vector<ResourcePair> cpuResourceMap = {
+ {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
+ {"skia/sk_resource_cache/rrect-blur_", "Masks"},
+ {"skia/sk_resource_cache/rects-blur_", "Masks"},
+ {"skia/sk_resource_cache/tessellated", "Shadows"},
+ {"skia", "Other"},
+ };
+ SkiaMemoryReporter cpuReporter(cpuResourceMap, false);
+ SkGraphics::DumpMemoryStatistics(&cpuReporter);
+ StringAppendF(&result, "Skia CPU Caches: ");
+ cpuReporter.logTotals(result);
+ cpuReporter.logOutput(result);
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+
+ std::vector<ResourcePair> gpuResourceMap = {
+ {"texture_renderbuffer", "Texture/RenderBuffer"},
+ {"texture", "Texture"},
+ {"gr_text_blob_cache", "Text"},
+ {"skia", "Other"},
+ };
+ SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
+ mGrContext->dumpMemoryStatistics(&gpuReporter);
+ StringAppendF(&result, "Skia's GPU Caches: ");
+ gpuReporter.logTotals(result);
+ gpuReporter.logOutput(result);
+ StringAppendF(&result, "Skia's Wrapped Objects:\n");
+ gpuReporter.logOutput(result, true);
+
+ StringAppendF(&result, "RenderEngine tracked buffers: %zu\n",
+ mGraphicBufferExternalRefs.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) {
+ StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts);
+ }
+ StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n",
+ mTextureCache.size());
+ StringAppendF(&result, "Dumping buffer ids...\n");
+ // TODO(178539829): It would be nice to know which layer these are coming from and what
+ // the texture sizes are.
+ for (const auto& [id, unused] : mTextureCache) {
+ StringAppendF(&result, "- 0x%" PRIx64 "\n", id);
+ }
+ StringAppendF(&result, "\n");
+
+ SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
+ if (mProtectedGrContext) {
+ mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
+ }
+ StringAppendF(&result, "Skia's GPU Protected Caches: ");
+ gpuProtectedReporter.logTotals(result);
+ gpuProtectedReporter.logOutput(result);
+ StringAppendF(&result, "Skia's Protected Wrapped Objects:\n");
+ gpuProtectedReporter.logOutput(result, true);
+
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size());
+ for (const auto& [linearEffect, unused] : mRuntimeEffects) {
+ StringAppendF(&result, "- inputDataspace: %s\n",
+ dataspaceDetails(
+ static_cast<android_dataspace>(linearEffect.inputDataspace))
+ .c_str());
+ StringAppendF(&result, "- outputDataspace: %s\n",
+ dataspaceDetails(
+ static_cast<android_dataspace>(linearEffect.outputDataspace))
+ .c_str());
+ StringAppendF(&result, "undoPremultipliedAlpha: %s\n",
+ linearEffect.undoPremultipliedAlpha ? "true" : "false");
+ }
+ }
+ StringAppendF(&result, "\n");
+}
+
} // namespace skia
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 160a186..a5cd278 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -20,6 +20,31 @@
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
+#include <GrBackendSemaphore.h>
+#include <GrDirectContext.h>
+#include <SkSurface.h>
+#include <android-base/thread_annotations.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+#include <mutex>
+#include <unordered_map>
+
+#include "AutoBackendTexture.h"
+#include "GrContextOptions.h"
+#include "SkImageInfo.h"
+#include "SkiaRenderEngine.h"
+#include "android-base/macros.h"
+#include "debug/SkiaCapture.h"
+#include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
+#include "filters/StretchShaderFactory.h"
+
+class SkData;
+
+struct SkPoint3;
+
namespace android {
namespace renderengine {
@@ -31,35 +56,141 @@
class BlurFilter;
-// TODO: Put common skia stuff here that can be shared between the GL & Vulkan backends
-// Currently mostly just handles all the no-op / missing APIs
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaRenderEngine(RenderEngineType type);
- ~SkiaRenderEngine() override {}
+ SkiaRenderEngine(RenderEngineType type,
+ PixelFormat pixelFormat,
+ bool useColorManagement,
+ bool supportsBackgroundBlur);
+ ~SkiaRenderEngine() override;
- virtual std::future<void> primeCache() override { return {}; };
- virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
- virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
- virtual bool isProtected() const override { return false; } // mInProtectedContext; }
- virtual bool supportsProtectedContent() const override { return false; };
- virtual int getContextPriority() override { return 0; }
- virtual int reportShadersCompiled() { return 0; }
- virtual void setEnableTracing(bool tracingEnabled) override;
+ std::future<void> primeCache() override final;
+ void cleanupPostRender() override final;
+ void cleanFramebufferCache() override final{ }
+ bool isProtected() const override final{ return mInProtectedContext; }
+ bool supportsBackgroundBlur() override final {
+ return mBlurFilter != nullptr;
+ }
+ void onActiveDisplaySizeChanged(ui::Size size) override final;
+ int reportShadersCompiled();
+ virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override final{};
+ virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override final{};
+ virtual void setEnableTracing(bool tracingEnabled) override final;
+
+ void useProtectedContext(bool useProtectedContext) override;
+ bool supportsProtectedContent() const override {
+ return supportsProtectedContentImpl();
+ }
+ void ensureGrContextsCreated();
protected:
- virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
- bool /*isRenderable*/) override = 0;
- virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
+ // This is so backends can stop the generic rendering state first before
+ // cleaning up backend-specific state
+ void finishRenderingAndAbandonContext();
- virtual void drawLayersInternal(
- const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
- const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence) override {
- resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ // Functions that a given backend (GLES, Vulkan) must implement
+ using Contexts = std::pair<sk_sp<GrDirectContext>, sk_sp<GrDirectContext>>;
+ virtual Contexts createDirectContexts(const GrContextOptions& options) = 0;
+ virtual bool supportsProtectedContentImpl() const = 0;
+ virtual bool useProtectedContextImpl(GrProtected isProtected) = 0;
+ virtual void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) = 0;
+ virtual base::unique_fd flushAndSubmit(GrDirectContext* context) = 0;
+ virtual void appendBackendSpecificInfoToDump(std::string& result) = 0;
+
+ size_t getMaxTextureSize() const override final;
+ size_t getMaxViewportDims() const override final;
+ GrDirectContext* getActiveGrContext();
+
+ // Implements PersistentCache as a way to monitor what SkSL shaders Skia has
+ // cached.
+ class SkSLCacheMonitor : public GrContextOptions::PersistentCache {
+ public:
+ SkSLCacheMonitor() = default;
+ ~SkSLCacheMonitor() override = default;
+
+ sk_sp<SkData> load(const SkData& key) override;
+
+ void store(const SkData& key, const SkData& data, const SkString& description) override;
+
+ int shadersCachedSinceLastCall() {
+ const int shadersCachedSinceLastCall = mShadersCachedSinceLastCall;
+ mShadersCachedSinceLastCall = 0;
+ return shadersCachedSinceLastCall;
+ }
+
+ int totalShadersCompiled() const { return mTotalShadersCompiled; }
+
+ private:
+ int mShadersCachedSinceLastCall = 0;
+ int mTotalShadersCompiled = 0;
};
+
+private:
+ void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
+ bool isRenderable) override final;
+ void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override final;
+ bool canSkipPostRenderCleanup() const override final;
+
+ void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
+ void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
+ const ShadowSettings& shadowSettings);
+ void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) override final;
+
+ void dump(std::string& result) override final;
+
+ // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
+ // Otherwise it returns the input shader.
+ struct RuntimeEffectShaderParameters {
+ sk_sp<SkShader> shader;
+ const LayerSettings& layer;
+ const DisplaySettings& display;
+ bool undoPremultipliedAlpha;
+ bool requiresLinearEffect;
+ float layerDimmingRatio;
+ };
+ sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
+
+ const PixelFormat mDefaultPixelFormat;
+ const bool mUseColorManagement;
+
+ // Identifier used for various mappings of layers to various
+ // textures or shaders
+ using GraphicBufferId = uint64_t;
+
+ // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
+ std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
+ GUARDED_BY(mRenderingMutex);
+ // Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
+ std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+ GUARDED_BY(mRenderingMutex);
+ std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
+ mRuntimeEffects;
+ AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
+
+ StretchShaderFactory mStretchShaderFactory;
+
+ sp<Fence> mLastDrawFence;
+ BlurFilter* mBlurFilter = nullptr;
+
+ // Object to capture commands send to Skia.
+ std::unique_ptr<SkiaCapture> mCapture;
+
+ // Mutex guarding rendering operations, so that internal state related to
+ // rendering that is potentially modified by multiple threads is guaranteed thread-safe.
+ mutable std::mutex mRenderingMutex;
+ SkSLCacheMonitor mSkSLCacheMonitor;
+
+ // Graphics context used for creating surfaces and submitting commands
+ sk_sp<GrDirectContext> mGrContext;
+ // Same as above, but for protected content (eg. DRM)
+ sk_sp<GrDirectContext> mProtectedGrContext;
+ bool mInProtectedContext = false;
};
} // namespace skia
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 23c9965..79e9d4b 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -6372,6 +6372,13 @@
{ // acquire lock
std::scoped_lock _l(mLock);
+
+ // Ensure that we have an entry created for all existing displays so that if a displayId has
+ // no windows, we can tell that the windows were removed from the display.
+ for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
+ handlesPerDisplay[displayId];
+ }
+
mDisplayInfos.clear();
for (const auto& displayInfo : displayInfos) {
mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index c1111f2..ba019f6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -392,6 +392,10 @@
}
if (changes && resetNeeded) {
+ // If device was reset, cancel touch event and update touch spot state.
+ cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
+ mCurrentCookedState.clear();
+ updateTouchSpots();
// Send reset, unless this is the first time the device has been configured,
// in which case the reader will call reset itself after all mappers are ready.
NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
@@ -951,6 +955,7 @@
bool skipViewportUpdate = false;
if (viewportChanged) {
const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
+ const bool viewportDisplayIdChanged = mViewport.displayId != newViewport->displayId;
mViewport = *newViewport;
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
@@ -1042,6 +1047,8 @@
mDisplayHeight = rawHeight;
mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
}
+ // If displayId changed, do not skip viewport update.
+ skipViewportUpdate &= !viewportDisplayIdChanged;
}
// If moving between pointer modes, need to reset some state.
@@ -1960,6 +1967,10 @@
}
void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+ if (mCurrentMotionAborted) {
+ // Current motion event was already aborted.
+ return;
+ }
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
if (!currentIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b2148ef..7f21dea 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2370,6 +2370,35 @@
thirdWindow->consumeMotionDown();
}
+TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFocusable(true);
+
+ mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyDown);
+ mDispatcher->notifyKey(&keyUp);
+
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+ // All windows are removed from the display. Ensure that we can no longer dispatch to it.
+ mDispatcher->onWindowInfosChanged({}, {});
+
+ window->consumeFocusEvent(false);
+
+ mDispatcher->notifyKey(&keyDown);
+ mDispatcher->notifyKey(&keyUp);
+ window->assertNoEvents();
+}
+
/**
* Ensure the correct coordinate spaces are used by InputDispatcher.
*
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 67fcc40..dcd8521 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6696,6 +6696,36 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+TEST_F(SingleTouchInputMapperTest,
+ Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Down.
+ int32_t x = 100;
+ int32_t y = 200;
+ processDown(mapper, x, y);
+ processSync(mapper);
+
+ // We should receive a down event
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // Change display id
+ clearViewports();
+ prepareSecondaryDisplay(ViewportType::INTERNAL);
+
+ // We should receive a cancel event
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+ // Then receive reset called
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -8739,6 +8769,10 @@
// window's coordinate space.
frames[0].rotate(getInverseRotation(orientation));
ASSERT_EQ(frames, motionArgs.videoFrames);
+
+ // Release finger.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
}
}
diff --git a/services/powermanager/tests/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
index b62be5f..7414958 100644
--- a/services/powermanager/tests/IThermalManagerTest.cpp
+++ b/services/powermanager/tests/IThermalManagerTest.cpp
@@ -33,26 +33,38 @@
using namespace android::os;
using namespace std::chrono_literals;
-class IThermalServiceTest : public testing::Test,
- public BnThermalStatusListener{
+class IThermalServiceTestListener : public BnThermalStatusListener {
+ public:
+ virtual binder::Status onStatusChange(int status) override;
+ std::condition_variable mCondition;
+ int mListenerStatus = 0;
+ std::mutex mMutex;
+};
+
+binder::Status IThermalServiceTestListener::onStatusChange(int status) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mListenerStatus = status;
+ ALOGI("IThermalServiceTestListener::notifyListener %d", mListenerStatus);
+ mCondition.notify_all();
+ return binder::Status::ok();
+}
+
+class IThermalServiceTest : public testing::Test {
public:
IThermalServiceTest();
void setThermalOverride(int level);
- virtual binder::Status onStatusChange(int status) override;
int getStatusFromService();
void SetUp() override;
void TearDown() override;
protected:
sp<IThermalService> mThermalSvc;
- std::condition_variable mCondition;
- int mListenerStatus;
int mServiceStatus;
- std::mutex mMutex;
+ sp<IThermalServiceTestListener> mCallback;
};
IThermalServiceTest::IThermalServiceTest()
- : mListenerStatus(0),
- mServiceStatus(0) {
+ : mServiceStatus(0),
+ mCallback(sp<IThermalServiceTestListener>::make()) {
}
void IThermalServiceTest::setThermalOverride(int level) {
@@ -60,14 +72,6 @@
system(cmdStr.c_str());
}
-binder::Status IThermalServiceTest::onStatusChange(int status) {
- std::unique_lock<std::mutex> lock(mMutex);
- mListenerStatus = status;
- ALOGI("IThermalServiceTest::notifyListener %d", mListenerStatus);
- mCondition.notify_all();
- return binder::Status::ok();
-}
-
int IThermalServiceTest::getStatusFromService() {
int status;
binder::Status ret = mThermalSvc->getCurrentThermalStatus(&status);
@@ -87,19 +91,19 @@
mThermalSvc = interface_cast<IThermalService>(binder);
EXPECT_NE(mThermalSvc, nullptr);
// Lock mutex for operation, so listener will only be processed after wait_for is called
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mCallback->mMutex);
bool success = false;
- binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success);
+ binder::Status ret = mThermalSvc->registerThermalStatusListener(mCallback, &success);
// Check the result
ASSERT_TRUE(success);
ASSERT_TRUE(ret.isOk());
// Wait for listener called after registration, shouldn't timeout
- EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
+ EXPECT_NE(mCallback->mCondition.wait_for(lock, 1s), std::cv_status::timeout);
}
void IThermalServiceTest::TearDown() {
bool success = false;
- binder::Status ret = mThermalSvc->unregisterThermalStatusListener(this, &success);
+ binder::Status ret = mThermalSvc->unregisterThermalStatusListener(mCallback, &success);
ASSERT_TRUE(success);
ASSERT_TRUE(ret.isOk());
}
@@ -114,14 +118,14 @@
TEST_P(IThermalListenerTest, TestListener) {
int level = GetParam();
// Lock mutex for operation, so listener will only be processed after wait_for is called
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mCallback->mMutex);
// Set the override thermal status
setThermalOverride(level);
// Wait for listener called, shouldn't timeout
- EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
+ EXPECT_NE(mCallback->mCondition.wait_for(lock, 1s), std::cv_status::timeout);
// Check the result
- EXPECT_EQ(level, mListenerStatus);
- ALOGI("Thermal listener status %d, expecting %d", mListenerStatus, level);
+ EXPECT_EQ(level, mCallback->mListenerStatus);
+ ALOGI("Thermal listener status %d, expecting %d", mCallback->mListenerStatus, level);
}
INSTANTIATE_TEST_SUITE_P(TestListenerLevels, IThermalListenerTest, testing::Range(
@@ -158,4 +162,4 @@
ALOGV("Test result = %d\n", status);
return status;
-}
\ No newline at end of file
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index f1457bf..a8ba15f 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -163,6 +163,7 @@
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
+ "HwcSlotGenerator.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerProtoHelper.cpp",
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 59e2d18..5656e05 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -25,7 +25,6 @@
#include <FrameTimeline/FrameTimeline.h>
#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerFECompositionState.h>
#include <gui/BufferQueue.h>
#include <private/gui/SyncFeatures.h>
#include <renderengine/Image.h>
@@ -71,22 +70,64 @@
using PresentState = frametimeline::SurfaceFrame::PresentState;
using gui::WindowInfo;
-void BufferStateLayer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
- const sp<GraphicBuffer>& buffer,
- uint64_t framenumber,
- const sp<Fence>& releaseFence,
- uint32_t currentMaxAcquiredBufferCount) {
- if (!listener) {
- return;
- }
- ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
- listener->onReleaseBuffer({buffer->getId(), framenumber},
- releaseFence ? releaseFence : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
-}
-
namespace {
static constexpr float defaultMaxLuminance = 1000.0;
+
+constexpr mat4 inverseOrientation(uint32_t transform) {
+ const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ mat4 tr;
+
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ tr = tr * rot90;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ tr = tr * flipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ tr = tr * flipV;
+ }
+ return inverse(tr);
+}
+
+bool assignTransform(ui::Transform* dst, ui::Transform& from) {
+ if (*dst == from) {
+ return false;
+ }
+ *dst = from;
+ return true;
+}
+
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+ using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+ using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+ const auto frameRateCompatibility = [frameRate] {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return FrameRateCompatibility::Default;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return FrameRateCompatibility::ExactOrMultiple;
+ default:
+ return FrameRateCompatibility::Undefined;
+ }
+ }();
+
+ const auto seamlessness = [frameRate] {
+ switch (frameRate.seamlessness) {
+ case scheduler::Seamlessness::OnlySeamless:
+ return Seamlessness::ShouldBeSeamless;
+ case scheduler::Seamlessness::SeamedAndSeamless:
+ return Seamlessness::NotRequired;
+ default:
+ return Seamlessness::Undefined;
+ }
+ }();
+
+ return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+ .frameRateCompatibility = frameRateCompatibility,
+ .seamlessness = seamlessness};
+}
} // namespace
BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
@@ -97,7 +138,6 @@
ALOGV("Creating Layer %s", getDebugName());
mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
-
mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
@@ -127,6 +167,20 @@
mFlinger->mFrameTracer->onDestroy(layerId);
}
+void BufferStateLayer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+ const sp<GraphicBuffer>& buffer,
+ uint64_t framenumber,
+ const sp<Fence>& releaseFence,
+ uint32_t currentMaxAcquiredBufferCount) {
+ if (!listener) {
+ return;
+ }
+ ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
+ listener->onReleaseBuffer({buffer->getId(), framenumber},
+ releaseFence ? releaseFence : Fence::NO_FENCE,
+ currentMaxAcquiredBufferCount);
+}
+
// -----------------------------------------------------------------------
// Interface implementation for Layer
// -----------------------------------------------------------------------
@@ -239,14 +293,6 @@
mDrawingState.callbackHandles = {};
}
-void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
- const CompositorTiming& compositorTiming) {
- for (const auto& handle : mDrawingState.callbackHandles) {
- handle->gpuCompositionDoneFence = glDoneFence;
- handle->compositorTiming = compositorTiming;
- }
-}
-
bool BufferStateLayer::willPresentCurrentTransaction() const {
// Returns true if the most recent Transaction applied to CurrentState will be presented.
return (getSidebandStreamChanged() || getAutoRefresh() ||
@@ -307,14 +353,6 @@
return true;
}
-static bool assignTransform(ui::Transform* dst, ui::Transform& from) {
- if (*dst == from) {
- return false;
- }
- *dst = from;
- return true;
-}
-
// Translate destination frame into scale and position. If a destination frame is not set, use the
// provided scale and position
bool BufferStateLayer::updateGeometry() {
@@ -641,14 +679,6 @@
return fenceSignaled;
}
-bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
- if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
- return true;
- }
-
- return mDrawingState.isAutoTimestamp || mDrawingState.desiredPresentTime <= expectedPresentTime;
-}
-
bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->refreshStartTime = refreshStartTime;
@@ -685,8 +715,7 @@
return (mDrawingStateModified || mDrawingState.modified) && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
-status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
- nsecs_t /*expectedPresentTime*/) {
+void BufferStateLayer::updateTexImage(nsecs_t latchTime) {
const State& s(getDrawingState());
if (!s.buffer) {
@@ -695,7 +724,7 @@
handle->latchTime = latchTime;
}
}
- return NO_ERROR;
+ return;
}
for (auto& handle : mDrawingState.callbackHandles) {
@@ -734,135 +763,36 @@
mDrawingState.callbackHandles = remainingHandles;
mDrawingStateModified = false;
-
- return NO_ERROR;
}
-status_t BufferStateLayer::updateActiveBuffer() {
- const State& s(getDrawingState());
-
- if (s.buffer == nullptr) {
- return BAD_VALUE;
- }
-
- if (!mBufferInfo.mBuffer || !s.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
+void BufferStateLayer::gatherBufferInfo() {
+ if (!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
decrementPendingBufferCount();
}
mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
- mBufferInfo.mBuffer = s.buffer;
- mBufferInfo.mFence = s.acquireFence;
- mBufferInfo.mFrameNumber = s.frameNumber;
-
- return NO_ERROR;
-}
-
-status_t BufferStateLayer::updateFrameNumber() {
- // TODO(marissaw): support frame history events
- mPreviousFrameNumber = mCurrentFrameNumber;
- mCurrentFrameNumber = mDrawingState.frameNumber;
- return NO_ERROR;
-}
-
-void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
- std::lock_guard lock(mMutex);
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, failed to erase buffer");
- return;
- }
- eraseBufferLocked(clientCacheId);
-}
-
-int BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- std::lock_guard<std::mutex> lock(mMutex);
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return addCachedBuffer(clientCacheId);
- }
- auto& [hwcCacheSlot, counter] = itr->second;
- counter = mCounter++;
- return hwcCacheSlot;
-}
-
-int BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
- REQUIRES(mMutex) {
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, returning invalid slot");
- return BufferQueue::INVALID_BUFFER_SLOT;
- }
-
- ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
-
- int hwcCacheSlot = getFreeHwcCacheSlot();
- mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
- return hwcCacheSlot;
-}
-
-int BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
- if (mFreeHwcCacheSlots.empty()) {
- evictLeastRecentlyUsed();
- }
-
- int hwcCacheSlot = mFreeHwcCacheSlots.top();
- mFreeHwcCacheSlots.pop();
- return hwcCacheSlot;
-}
-
-void BufferStateLayer::HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
- uint64_t minCounter = UINT_MAX;
- client_cache_t minClientCacheId = {};
- for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
- const auto& [hwcCacheSlot, counter] = slotCounter;
- if (counter < minCounter) {
- minCounter = counter;
- minClientCacheId = clientCacheId;
- }
- }
- eraseBufferLocked(minClientCacheId);
-
- ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId, this);
-}
-
-void BufferStateLayer::HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId)
- REQUIRES(mMutex) {
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return;
- }
- auto& [hwcCacheSlot, counter] = itr->second;
-
- // TODO send to hwc cache and resources
-
- mFreeHwcCacheSlots.push(hwcCacheSlot);
- mCachedBuffers.erase(clientCacheId);
-}
-
-void BufferStateLayer::gatherBufferInfo() {
+ mBufferInfo.mBuffer = mDrawingState.buffer;
+ mBufferInfo.mFence = mDrawingState.acquireFence;
+ mBufferInfo.mFrameNumber = mDrawingState.frameNumber;
mBufferInfo.mPixelFormat =
!mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
mBufferInfo.mFrameLatencyNeeded = true;
-
- const State& s(getDrawingState());
- mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
- mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
- mBufferInfo.mFence = s.acquireFence;
- mBufferInfo.mTransform = s.bufferTransform;
+ mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime;
+ mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+ mBufferInfo.mFence = mDrawingState.acquireFence;
+ mBufferInfo.mTransform = mDrawingState.bufferTransform;
auto lastDataspace = mBufferInfo.mDataspace;
- mBufferInfo.mDataspace = translateDataspace(s.dataspace);
+ mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
if (lastDataspace != mBufferInfo.mDataspace) {
mFlinger->mSomeDataspaceChanged = true;
}
- mBufferInfo.mCrop = computeBufferCrop(s);
+ mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
- mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
- mBufferInfo.mHdrMetadata = s.hdrMetadata;
- mBufferInfo.mApi = s.api;
- mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
- mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
-}
-
-uint32_t BufferStateLayer::getEffectiveScalingMode() const {
- return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+ mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion;
+ mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
+ mBufferInfo.mApi = mDrawingState.api;
+ mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
+ mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(mDrawingState.clientCacheId);
}
Rect BufferStateLayer::computeBufferCrop(const State& s) {
@@ -1178,32 +1108,6 @@
(mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
}
-bool BufferStateLayer::isFixedSize() const {
- return true;
-}
-
-bool BufferStateLayer::usesSourceCrop() const {
- return true;
-}
-
-static constexpr mat4 inverseOrientation(uint32_t transform) {
- const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
- const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- mat4 tr;
-
- if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- tr = tr * rot90;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
- tr = tr * flipH;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
- tr = tr * flipV;
- }
- return inverse(tr);
-}
-
std::optional<compositionengine::LayerFE::LayerSettings> BufferStateLayer::prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
ATRACE_CALL();
@@ -1373,38 +1277,6 @@
compositionState->sidebandStreamHasFrame = false;
}
-namespace {
-TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
- using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
- using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
- const auto frameRateCompatibility = [frameRate] {
- switch (frameRate.type) {
- case Layer::FrameRateCompatibility::Default:
- return FrameRateCompatibility::Default;
- case Layer::FrameRateCompatibility::ExactOrMultiple:
- return FrameRateCompatibility::ExactOrMultiple;
- default:
- return FrameRateCompatibility::Undefined;
- }
- }();
-
- const auto seamlessness = [frameRate] {
- switch (frameRate.seamlessness) {
- case scheduler::Seamlessness::OnlySeamless:
- return Seamlessness::ShouldBeSeamless;
- case scheduler::Seamlessness::SeamedAndSeamless:
- return Seamlessness::NotRequired;
- default:
- return Seamlessness::Undefined;
- }
- }();
-
- return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
- .frameRateCompatibility = frameRateCompatibility,
- .seamlessness = seamlessness};
-}
-} // namespace
-
void BufferStateLayer::onPostComposition(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
@@ -1413,8 +1285,10 @@
// composition.
if (!mBufferInfo.mFrameLatencyNeeded) return;
- // Update mFrameEventHistory.
- finalizeFrameEventHistory(glDoneFence, compositorTiming);
+ for (const auto& handle : mDrawingState.callbackHandles) {
+ handle->gpuCompositionDoneFence = glDoneFence;
+ handle->compositorTiming = compositorTiming;
+ }
// Update mFrameTracker.
nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
@@ -1480,8 +1354,7 @@
mBufferInfo.mFrameLatencyNeeded = false;
}
-bool BufferStateLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) {
+bool BufferStateLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
getDrawingState().frameNumber);
@@ -1499,27 +1372,16 @@
return false;
}
+ updateTexImage(latchTime);
+ if (mDrawingState.buffer == nullptr) {
+ return false;
+ }
+
// Capture the old state of the layer for comparisons later
- const State& s(getDrawingState());
- const bool oldOpacity = isOpaque(s);
-
BufferInfo oldBufferInfo = mBufferInfo;
-
- status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
- if (err != NO_ERROR) {
- return false;
- }
-
- err = updateActiveBuffer();
- if (err != NO_ERROR) {
- return false;
- }
-
- err = updateFrameNumber();
- if (err != NO_ERROR) {
- return false;
- }
-
+ const bool oldOpacity = isOpaque(mDrawingState);
+ mPreviousFrameNumber = mCurrentFrameNumber;
+ mCurrentFrameNumber = mDrawingState.frameNumber;
gatherBufferInfo();
if (oldBufferInfo.mBuffer == nullptr) {
@@ -1544,7 +1406,7 @@
}
}
- if (oldOpacity != isOpaque(s)) {
+ if (oldOpacity != isOpaque(mDrawingState)) {
recomputeVisibleRegions = true;
}
@@ -1628,7 +1490,7 @@
void BufferStateLayer::latchAndReleaseBuffer() {
if (hasReadyFrame()) {
bool ignored = false;
- latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
+ latchBuffer(ignored, systemTime());
}
releasePendingBuffer(systemTime());
}
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index df7ae61..8bad3d2 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -40,6 +40,7 @@
#include "DisplayHardware/HWComposer.h"
#include "FrameTimeline.h"
#include "FrameTracker.h"
+#include "HwcSlotGenerator.h"
#include "Layer.h"
#include "LayerVector.h"
#include "SurfaceFlinger.h"
@@ -74,10 +75,7 @@
// GRALLOC_USAGE_PROTECTED sense.
bool isProtected() const override;
- // isFixedSize - true if content has a fixed size
- bool isFixedSize() const override;
-
- bool usesSourceCrop() const override;
+ bool usesSourceCrop() const override { return true; }
bool isHdrY410() const override;
@@ -89,13 +87,9 @@
// the visible regions need to be recomputed (this is a fairly heavy
// operation, so this should be set only if needed). Typically this is used
// to figure out if the content or size of a surface has changed.
- bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) override;
+ bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
bool hasReadyFrame() const override;
- // Returns the current scaling mode
- uint32_t getEffectiveScalingMode() const override;
-
// Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
// This is used if the buffer is just latched and releases to free up the buffer
// and will not be shown on screen.
@@ -122,14 +116,6 @@
void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
- void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
- const CompositorTiming& compositorTiming) override;
-
- // Returns true if the next buffer should be presented at the expected present time,
- // overridden by BufferStateLayer and BufferQueueLayer for implementation
- // specific logic
- bool isBufferDue(nsecs_t /*expectedPresentTime*/) const { return true; }
-
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
}
@@ -166,7 +152,6 @@
bool updateGeometry() override;
bool fenceHasSignaled() const;
- bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const;
bool onPreComposition(nsecs_t) override;
// See mPendingBufferTransactions
@@ -174,9 +159,6 @@
std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
- // Returns true if the next buffer should be presented at the expected present time
- bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
-
protected:
void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
@@ -268,11 +250,7 @@
bool hasFrameUpdate() const;
- status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime);
-
- status_t updateActiveBuffer();
- status_t updateFrameNumber();
+ void updateTexImage(nsecs_t latchTime);
sp<Layer> createClone() override;
@@ -323,45 +301,6 @@
// not specify a destination frame.
ui::Transform mRequestedTransform;
- // TODO(marissaw): support sticky transform for LEGACY camera mode
-
- class HwcSlotGenerator : public ClientCache::ErasedRecipient {
- public:
- HwcSlotGenerator() {
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mFreeHwcCacheSlots.push(i);
- }
- }
-
- void bufferErased(const client_cache_t& clientCacheId);
-
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
-
- private:
- friend class SlotGenerationTest;
- int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
- int getFreeHwcCacheSlot() REQUIRES(mMutex);
- void evictLeastRecentlyUsed() REQUIRES(mMutex);
- void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-
- struct CachedBufferHash {
- std::size_t operator()(const client_cache_t& clientCacheId) const {
- return std::hash<uint64_t>{}(clientCacheId.id);
- }
- };
-
- std::mutex mMutex;
-
- std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
- CachedBufferHash>
- mCachedBuffers GUARDED_BY(mMutex);
- std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
-
- // The cache increments this counter value when a slot is updated or used.
- // Used to track the least recently-used buffer
- uint64_t mCounter = 0;
- };
-
sp<HwcSlotGenerator> mHwcSlotGenerator;
};
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 77dda6c..62078ba 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -187,7 +187,7 @@
if (!mSupportsPowerHint.has_value()) {
std::lock_guard lock(mPowerHalMutex);
HalWrapper* const halWrapper = getPowerHal();
- mSupportsPowerHint = halWrapper->supportsPowerHintSession();
+ mSupportsPowerHint = halWrapper && halWrapper->supportsPowerHintSession();
}
return *mSupportsPowerHint;
}
@@ -759,9 +759,10 @@
}
bool AidlPowerHalWrapper::shouldReportActualDurations() {
- // Report if we have never reported before or are approaching a stale session
+ // Report if we have never reported before or will go stale next frame
if (!mLastActualDurationSent.has_value() ||
- (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
+ (mLastTargetDurationSent + systemTime() - mLastActualReportTimestamp) >
+ kStaleTimeout.count()) {
return true;
}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 98921b0..a93744f 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -332,7 +332,7 @@
static const bool sTraceHintSessionData;
static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms;
// Amount of time after the last message was sent before the session goes stale
- // actually 100ms but we use 80 here to ideally avoid going stale
+ // actually 100ms but we use 80 here to give some slack
static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
};
diff --git a/services/surfaceflinger/HwcSlotGenerator.cpp b/services/surfaceflinger/HwcSlotGenerator.cpp
new file mode 100644
index 0000000..b7e9607
--- /dev/null
+++ b/services/surfaceflinger/HwcSlotGenerator.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HwcSlotGenerator"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <gui/BufferQueue.h>
+
+#include "HwcSlotGenerator.h"
+
+namespace android {
+
+HwcSlotGenerator::HwcSlotGenerator() {
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ mFreeHwcCacheSlots.push(i);
+ }
+}
+
+void HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
+ std::lock_guard lock(mMutex);
+ if (!clientCacheId.isValid()) {
+ ALOGE("invalid process, failed to erase buffer");
+ return;
+ }
+ eraseBufferLocked(clientCacheId);
+}
+
+int HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto itr = mCachedBuffers.find(clientCacheId);
+ if (itr == mCachedBuffers.end()) {
+ return addCachedBuffer(clientCacheId);
+ }
+ auto& [hwcCacheSlot, counter] = itr->second;
+ counter = mCounter++;
+ return hwcCacheSlot;
+}
+
+int HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
+ if (!clientCacheId.isValid()) {
+ ALOGE("invalid process, returning invalid slot");
+ return BufferQueue::INVALID_BUFFER_SLOT;
+ }
+
+ ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
+
+ int hwcCacheSlot = getFreeHwcCacheSlot();
+ mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
+ return hwcCacheSlot;
+}
+
+int HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
+ if (mFreeHwcCacheSlots.empty()) {
+ evictLeastRecentlyUsed();
+ }
+
+ int hwcCacheSlot = mFreeHwcCacheSlots.top();
+ mFreeHwcCacheSlots.pop();
+ return hwcCacheSlot;
+}
+
+void HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
+ uint64_t minCounter = UINT_MAX;
+ client_cache_t minClientCacheId = {};
+ for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
+ const auto& [hwcCacheSlot, counter] = slotCounter;
+ if (counter < minCounter) {
+ minCounter = counter;
+ minClientCacheId = clientCacheId;
+ }
+ }
+ eraseBufferLocked(minClientCacheId);
+
+ ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId, this);
+}
+
+void HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
+ auto itr = mCachedBuffers.find(clientCacheId);
+ if (itr == mCachedBuffers.end()) {
+ return;
+ }
+ auto& [hwcCacheSlot, counter] = itr->second;
+
+ // TODO send to hwc cache and resources
+
+ mFreeHwcCacheSlots.push(hwcCacheSlot);
+ mCachedBuffers.erase(clientCacheId);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/HwcSlotGenerator.h b/services/surfaceflinger/HwcSlotGenerator.h
new file mode 100644
index 0000000..5a1b6d7
--- /dev/null
+++ b/services/surfaceflinger/HwcSlotGenerator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 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 <functional>
+#include <mutex>
+#include <stack>
+#include <unordered_map>
+
+#include "ClientCache.h"
+
+namespace android {
+
+class HwcSlotGenerator : public ClientCache::ErasedRecipient {
+public:
+ HwcSlotGenerator();
+ void bufferErased(const client_cache_t& clientCacheId);
+ int getHwcCacheSlot(const client_cache_t& clientCacheId);
+
+private:
+ friend class SlotGenerationTest;
+ int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+ int getFreeHwcCacheSlot() REQUIRES(mMutex);
+ void evictLeastRecentlyUsed() REQUIRES(mMutex);
+ void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+
+ struct CachedBufferHash {
+ std::size_t operator()(const client_cache_t& clientCacheId) const {
+ return std::hash<uint64_t>{}(clientCacheId.id);
+ }
+ };
+
+ std::mutex mMutex;
+
+ std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
+ CachedBufferHash>
+ mCachedBuffers GUARDED_BY(mMutex);
+ std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
+
+ // The cache increments this counter value when a slot is updated or used.
+ // Used to track the least recently-used buffer
+ uint64_t mCounter = 0;
+};
+} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ba3cb51..0091f03 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -78,6 +78,7 @@
namespace android {
namespace {
constexpr int kDumpTableRowLength = 159;
+const ui::Transform kIdentityTransform;
} // namespace
using namespace ftl::flag_operators;
@@ -2112,8 +2113,6 @@
layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
layerInfo->set_queued_frames(getQueuedFrameCount());
layerInfo->set_curr_frame(mCurrentFrameNumber);
- layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
-
layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
layerInfo->set_corner_radius(
(getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
@@ -2209,7 +2208,8 @@
if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) {
WindowInfo info;
if (useDrawing) {
- info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
+ info = fillInputInfo(
+ InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true});
} else {
info = state.inputInfo;
}
@@ -2416,7 +2416,7 @@
}
}
-WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) {
+WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
mDrawingState.inputInfo.ownerUid = mOwnerUid;
@@ -2425,12 +2425,21 @@
mDrawingState.inputInfo.displayId = getLayerStack().id;
}
+ const ui::Transform& displayTransform =
+ displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform;
+
WindowInfo info = mDrawingState.inputInfo;
info.id = sequence;
info.displayId = getLayerStack().id;
fillInputFrameInfo(info, displayTransform);
+ if (displayArgs.transform == nullptr) {
+ // Do not let the window receive touches if it is not associated with a valid display
+ // transform. We still allow the window to receive keys and prevent ANRs.
+ info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
+ }
+
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
@@ -2448,7 +2457,7 @@
// If the window will be blacked out on a display because the display does not have the secure
// flag and the layer has the secure flag set, then drop input.
- if (!displayIsSecure && isSecure()) {
+ if (!displayArgs.isSecure && isSecure()) {
info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b809c8a..22bb866 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -528,8 +528,6 @@
virtual bool isHdrY410() const { return false; }
- virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
-
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
@@ -542,17 +540,13 @@
// If a buffer was replaced this frame, release the former buffer
virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
- virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
- const CompositorTiming& /*compositorTiming*/) {}
-
/*
* latchBuffer - called each time the screen is redrawn and returns whether
* the visible regions need to be recomputed (this is a fairly heavy
* operation, so this should be set only if needed). Typically this is used
* to figure out if the content or size of a surface has changed.
*/
- virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
- nsecs_t /*expectedPresentTime*/) {
+ virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
return false;
}
@@ -854,7 +848,11 @@
bool getPremultipledAlpha() const;
void setInputInfo(const gui::WindowInfo& info);
- gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure);
+ struct InputDisplayArgs {
+ const ui::Transform* transform = nullptr;
+ bool isSecure = false;
+ };
+ gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs);
/**
* Returns whether this layer has an explicitly set input-info.
@@ -926,11 +924,6 @@
virtual void commitTransaction(State& stateToCommit);
virtual void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&) {}
- // Returns mCurrentScaling mode (originating from the
- // Client) or mOverrideScalingMode mode (originating from
- // the Surface Controller) if set.
- virtual uint32_t getEffectiveScalingMode() const { return 0; }
-
sp<compositionengine::LayerFE> asLayerFE() const;
sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
bool isClone() { return mClonedFrom != nullptr; }
diff --git a/services/surfaceflinger/LocklessQueue.h b/services/surfaceflinger/LocklessQueue.h
new file mode 100644
index 0000000..6b63360
--- /dev/null
+++ b/services/surfaceflinger/LocklessQueue.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 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 <atomic>
+#include <optional>
+
+template <typename T>
+// Single consumer multi producer stack. We can understand the two operations independently to see
+// why they are without race condition.
+//
+// push is responsible for maintaining a linked list stored in mPush, and called from multiple
+// threads without lock. We can see that if two threads never observe the same value from
+// mPush.load, it just functions as a normal linked list. In the case where two threads observe the
+// same value, one of them has to execute the compare_exchange first. The one that doesn't execute
+// the compare exchange first, will receive false from compare_exchange. previousHead is updated (by
+// compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to
+// see that the process can repeat with an arbitrary number of threads.
+//
+// Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges
+// the entire push list with null. This is safe, since the only other reader (push)
+// of mPush will retry if it changes in between it's read and atomic compare. We
+// then store the list and pop one element.
+//
+// If we already had something in the pop list we just pop directly.
+class LocklessQueue {
+public:
+ class Entry {
+ public:
+ T mValue;
+ std::atomic<Entry*> mNext;
+ Entry(T value) : mValue(value) {}
+ };
+ std::atomic<Entry*> mPush = nullptr;
+ std::atomic<Entry*> mPop = nullptr;
+ bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); }
+
+ void push(T value) {
+ Entry* entry = new Entry(value);
+ Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
+ do {
+ entry->mNext = previousHead;
+ } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
+ }
+ std::optional<T> pop() {
+ Entry* popped = mPop.load(/*std::memory_order_acquire*/);
+ if (popped) {
+ // Single consumer so this is fine
+ mPop.store(popped->mNext /* , std::memory_order_release */);
+ auto value = popped->mValue;
+ delete popped;
+ return std::move(value);
+ } else {
+ Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */);
+ if (!grabbedList) return std::nullopt;
+ // Reverse the list
+ while (grabbedList->mNext) {
+ Entry* next = grabbedList->mNext;
+ grabbedList->mNext = popped;
+ popped = grabbedList;
+ grabbedList = next;
+ }
+ mPop.store(popped /* , std::memory_order_release */);
+ auto value = grabbedList->mValue;
+ delete grabbedList;
+ return std::move(value);
+ }
+ }
+};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3b15ac4..215f083 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3264,11 +3264,11 @@
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- // Do not create WindowInfos for windows on displays that cannot receive input.
- if (const auto opt = displayInputInfos.get(layer->getLayerStack())) {
- const auto& info = opt->get();
- outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure));
- }
+ const auto opt = displayInputInfos.get(layer->getLayerStack(),
+ [](const auto& info) -> Layer::InputDisplayArgs {
+ return {&info.transform, info.isSecure};
+ });
+ outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
});
sNumWindowInfos = outWindowInfos.size();
@@ -3488,8 +3488,6 @@
bool frameQueued = false;
bool newDataLatched = false;
- const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
-
// Store the set of layers that need updates. This set must not change as
// buffers are being latched, as this could result in a deadlock.
// Example: Two producers share the same command stream and:
@@ -3509,12 +3507,7 @@
if (layer->hasReadyFrame()) {
frameQueued = true;
- if (layer->shouldPresentNow(expectedPresentTime)) {
- mLayersWithQueuedFrames.emplace(layer);
- } else {
- ATRACE_NAME("!layer->shouldPresentNow()");
- layer->useEmptyDamage();
- }
+ mLayersWithQueuedFrames.emplace(layer);
} else {
layer->useEmptyDamage();
}
@@ -3535,7 +3528,7 @@
Mutex::Autolock lock(mStateLock);
for (const auto& layer : mLayersWithQueuedFrames) {
- if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
+ if (layer->latchBuffer(visibleRegions, latchTime)) {
mLayersPendingRefresh.push_back(layer);
newDataLatched = true;
}
@@ -3701,6 +3694,8 @@
transactions.emplace_back(std::move(transaction));
transactionQueue.pop();
+ mPendingTransactionCount--;
+ ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
}
if (transactionQueue.empty()) {
@@ -3724,8 +3719,6 @@
{
Mutex::Autolock _l(mStateLock);
{
- Mutex::Autolock _l(mQueueLock);
-
int lastTransactionsPendingBarrier = 0;
int transactionsPendingBarrier = 0;
// First collect transactions from the pending transaction queues.
@@ -3738,8 +3731,12 @@
// Second, collect transactions from the transaction queue.
// Here as well we are not allowing unsignaled buffers for the same
// reason as above.
- while (!mTransactionQueue.empty()) {
- auto& transaction = mTransactionQueue.front();
+ while (!mLocklessTransactionQueue.isEmpty()) {
+ auto maybeTransaction = mLocklessTransactionQueue.pop();
+ if (!maybeTransaction.has_value()) {
+ break;
+ }
+ auto transaction = maybeTransaction.value();
const bool pendingTransactions =
mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
@@ -3777,9 +3774,9 @@
}
});
transactions.emplace_back(std::move(transaction));
+ mPendingTransactionCount--;
+ ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
}
- mTransactionQueue.pop_front();
- ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
// Transactions with a buffer pending on a barrier may be on a different applyToken
@@ -3840,8 +3837,7 @@
}
bool SurfaceFlinger::transactionFlushNeeded() {
- Mutex::Autolock _l(mQueueLock);
- return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
+ return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
}
bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
@@ -4008,17 +4004,15 @@
void SurfaceFlinger::queueTransaction(TransactionState& state) {
state.queueTime = systemTime();
-
- Mutex::Autolock lock(mQueueLock);
-
// Generate a CountDownLatch pending state if this is a synchronous transaction.
if (state.flags & eSynchronous) {
state.transactionCommittedSignal =
std::make_shared<CountDownLatch>(CountDownLatch::eSyncTransaction);
}
- mTransactionQueue.emplace_back(state);
- ATRACE_INT("TransactionQueue", mTransactionQueue.size());
+ mLocklessTransactionQueue.push(state);
+ mPendingTransactionCount++;
+ ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
const auto schedule = [](uint32_t flags) {
if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a247bae..46d941f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -66,6 +66,7 @@
#include "FlagManager.h"
#include "FrameTracker.h"
#include "LayerVector.h"
+#include "LocklessQueue.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
@@ -754,13 +755,13 @@
std::vector<TransactionState>& transactions,
std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
- bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock);
+ bool tryApplyUnsignaled) REQUIRES(mStateLock);
int flushUnsignaledPendingTransactionQueues(
std::vector<TransactionState>& transactions,
std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions)
- REQUIRES(mStateLock, mQueueLock);
+ REQUIRES(mStateLock);
uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -1086,7 +1087,7 @@
status_t CheckTransactCodeCredentials(uint32_t code);
// Add transaction to the Transaction Queue
- void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
+ void queueTransaction(TransactionState& state);
void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
void signalSynchronousTransactions(const uint32_t flag);
@@ -1244,11 +1245,11 @@
uint32_t mTexturePoolSize = 0;
std::vector<uint32_t> mTexturePool;
- mutable Mutex mQueueLock;
Condition mTransactionQueueCV;
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
- mPendingTransactionQueues GUARDED_BY(mQueueLock);
- std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
+ mPendingTransactionQueues;
+ LocklessQueue<TransactionState> mLocklessTransactionQueue;
+ std::atomic<size_t> mPendingTransactionCount = 0;
/*
* Feature prototyping
*/
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index b41d7b9..ed57e18 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -724,7 +724,7 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto &getTransactionQueue() { return mFlinger->mTransactionQueue; }
+ auto &getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
auto setTransactionState(
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 4d90b1e..ed94e76 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -125,9 +125,7 @@
ftl::yield<FenceResult>(base::unexpected(mFdp.ConsumeIntegral<status_t>())).share());
layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
- layer->finalizeFrameEventHistory(fenceTime, compositorTiming);
layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
- layer->isBufferDue(mFdp.ConsumeIntegral<int64_t>());
layer->setTransform(mFdp.ConsumeIntegral<uint32_t>());
layer->setTransformToDisplayInverse(mFdp.ConsumeBool());
@@ -151,7 +149,6 @@
layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
layer->fenceHasSignaled();
- layer->framePresentTimeIsCurrent(mFdp.ConsumeIntegral<int64_t>());
layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
const std::vector<sp<CallbackHandle>> callbacks;
layer->setTransactionCompletedListeners(callbacks);
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 6f85498..7f203ce 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -26,8 +26,7 @@
class SlotGenerationTest : public testing::Test {
protected:
- sp<BufferStateLayer::HwcSlotGenerator> mHwcSlotGenerator =
- sp<BufferStateLayer::HwcSlotGenerator>::make();
+ sp<HwcSlotGenerator> mHwcSlotGenerator = sp<HwcSlotGenerator>::make();
sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer3{new GraphicBuffer(10, 10, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index f6ebfe5..9c16e4c 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -511,7 +511,7 @@
Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)));
bool ignoredRecomputeVisibleRegions;
- layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
+ layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
Mock::VerifyAndClear(test->mRenderEngine);
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 81e3f4a..77cefa6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -421,7 +421,7 @@
return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
- auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
+ auto& getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 84f1170..b2da34e 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -117,7 +117,7 @@
}
void NotPlacedOnTransactionQueue(uint32_t flags) {
- ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transaction;
setupSingle(transaction, flags,
@@ -140,12 +140,12 @@
EXPECT_LE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
}
// Each transaction should have been placed on the transaction queue
- auto transactionQueue = mFlinger.getTransactionQueue();
- EXPECT_EQ(1u, transactionQueue.size());
+ auto& transactionQueue = mFlinger.getTransactionQueue();
+ EXPECT_FALSE(transactionQueue.isEmpty());
}
void PlaceOnTransactionQueue(uint32_t flags) {
- ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// first check will see desired present time has not passed,
@@ -171,12 +171,12 @@
applicationSentTime + mFlinger.getAnimationTransactionTimeout());
}
// This transaction should have been placed on the transaction queue
- auto transactionQueue = mFlinger.getTransactionQueue();
- EXPECT_EQ(1u, transactionQueue.size());
+ auto& transactionQueue = mFlinger.getTransactionQueue();
+ EXPECT_FALSE(transactionQueue.isEmpty());
}
void BlockedByPriorTransaction(uint32_t flags) {
- ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
nsecs_t time = systemTime();
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
@@ -239,8 +239,8 @@
int mTransactionNumber = 0;
};
-TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
- ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+TEST_F(TransactionApplicationTest, AddToPendingQueue) {
+ ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
@@ -253,10 +253,27 @@
mHasListenerCallbacks, mCallbacks, transactionA.id);
auto& transactionQueue = mFlinger.getTransactionQueue();
- ASSERT_EQ(1u, transactionQueue.size());
+ ASSERT_FALSE(transactionQueue.isEmpty());
- auto& transactionState = transactionQueue.front();
+ auto transactionState = transactionQueue.pop().value();
checkEqual(transactionA, transactionState);
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
+ ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ TransactionInfo transactionA; // transaction to go on pending queue
+ setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
+ FrameTimelineInfo{});
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags, transactionA.applyToken,
+ transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+ transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+ mHasListenerCallbacks, mCallbacks, transactionA.id);
+
+ auto& transactionQueue = mFlinger.getTransactionQueue();
+ ASSERT_FALSE(transactionQueue.isEmpty());
// because flushing uses the cached expected present time, we send an empty
// transaction here (sending a null applyToken to fake it as from a
@@ -272,7 +289,7 @@
// passed
mFlinger.flushTransactionQueues();
- EXPECT_EQ(0u, transactionQueue.size());
+ EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
}
TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
@@ -306,7 +323,9 @@
void TearDown() override {
// Clear all transaction queues to release all transactions we sent
// in the tests. Otherwise, gmock complains about memory leaks.
- mFlinger.getTransactionQueue().clear();
+ while (!mFlinger.getTransactionQueue().isEmpty()) {
+ mFlinger.getTransactionQueue().pop();
+ }
mFlinger.getPendingTransactionQueue().clear();
mFlinger.getTransactionCommittedSignals().clear();
mFlinger.commitTransactionsLocked(eTransactionMask);
@@ -358,7 +377,7 @@
void setTransactionStates(const std::vector<TransactionInfo>& transactions,
size_t expectedTransactionsApplied,
size_t expectedTransactionsPending) {
- EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
for (const auto& transaction : transactions) {
@@ -370,7 +389,7 @@
mHasListenerCallbacks, mCallbacks, transaction.id);
}
mFlinger.flushTransactionQueues();
- EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size());
EXPECT_EQ(expectedTransactionsApplied, mFlinger.getTransactionCommittedSignals().size());
}
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 5364630..28bf8bf 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -127,7 +127,6 @@
dequeueTime, FrameTimelineInfo{});
commitTransaction(layer.get());
- bool computeVisisbleRegions;
nsecs_t latchTime = 25;
EXPECT_CALL(*mFlinger.getFrameTracer(),
traceFence(layerId, bufferId, frameNumber, _,
@@ -135,7 +134,7 @@
EXPECT_CALL(*mFlinger.getFrameTracer(),
traceTimestamp(layerId, bufferId, frameNumber, latchTime,
FrameTracer::FrameEvent::LATCH, /*duration*/ 0));
- layer->updateTexImage(computeVisisbleRegions, latchTime, /*expectedPresentTime*/ 0);
+ layer->updateTexImage(latchTime);
auto glDoneFence = fenceFactory.createFenceTimeForTest(fence);
auto presentFence = fenceFactory.createFenceTimeForTest(fence);
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 6d58303..e943644 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -140,8 +140,7 @@
// Buffers are presented only at latch time.
EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
- bool computeVisisbleRegions;
- layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ layer->updateTexImage(15);
EXPECT_EQ(1, surfaceFrame->getToken());
EXPECT_EQ(true, surfaceFrame->getIsBuffer());
@@ -192,8 +191,7 @@
const auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
commitTransaction(layer.get());
- bool computeVisisbleRegions;
- layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ layer->updateTexImage(15);
EXPECT_EQ(1, droppedSurfaceFrame->getToken());
EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer());
@@ -243,8 +241,7 @@
// Buffers are presented only at latch time.
EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
- bool computeVisisbleRegions;
- layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ layer->updateTexImage(15);
EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
}
@@ -331,8 +328,7 @@
// Buffers are presented only at latch time.
EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState());
- bool computeVisisbleRegions;
- layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ layer->updateTexImage(15);
EXPECT_EQ(PresentState::Presented, bufferSurfaceFrameTX->getPresentState());
}
@@ -377,8 +373,7 @@
auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
commitTransaction(layer.get());
- bool computeVisisbleRegions;
- layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ layer->updateTexImage(15);
// Both the droppedSurfaceFrame and presentedSurfaceFrame should be in
// pendingJankClassifications.
@@ -458,8 +453,7 @@
const auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
commitTransaction(layer.get());
- bool computeVisisbleRegions;
- layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ layer->updateTexImage(15);
EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer());
@@ -517,8 +511,7 @@
}
auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
- bool computeVisisbleRegions;
- layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ layer->updateTexImage(15);
// BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
// Since we don't have access to DisplayFrame here, trigger an onPresent directly.
for (auto& surfaceFrame : bufferlessSurfaceFrames) {