Support new EGL extensions
Bug: 21753739
Includes a revert of 13d1b4ab10fbee5e81a2ba1ac59cfae1e51d3ef0
as that only supported EGL_EXT_buffer_age
Change-Id: Ia86a47d19e3355c067934d7764c330b640c6958d
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ea73387..67c42f3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -35,6 +35,14 @@
#define TRIM_MEMORY_COMPLETE 80
#define TRIM_MEMORY_UI_HIDDEN 20
+#define LOG_FRAMETIME_MMA 0
+
+#if LOG_FRAMETIME_MMA
+static float sBenchMma = 0;
+static int sFrameCount = 0;
+static const float NANOS_PER_MILLIS_F = 1000000.0f;
+#endif
+
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -93,16 +101,6 @@
}
}
-void CanvasContext::swapBuffers(const SkRect& dirty, EGLint width, EGLint height) {
- if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface, dirty, width, height))) {
- setSurface(nullptr);
- }
- mHaveNewSurface = false;
- if (mEglManager.useBufferAgeExt()) {
- mDirtyHistory.prepend(Rect(dirty));
- }
-}
-
void CanvasContext::requireSurface() {
LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
"requireSurface() called but no surface set!");
@@ -230,8 +228,6 @@
"drawRenderNode called on a context with no canvas or surface!");
SkRect dirty;
- bool useBufferAgeExt = mEglManager.useBufferAgeExt();
- Rect patchedDirty;
mDamageAccumulator.finish(&dirty);
// TODO: Re-enable after figuring out cause of b/22592975
@@ -242,40 +238,59 @@
mCurrentFrameInfo->markIssueDrawCommandsStart();
- EGLint width, height, framebufferAge;
- mEglManager.beginFrame(mEglSurface, &width, &height, &framebufferAge);
-
- if (useBufferAgeExt && mHaveNewSurface) {
- mDirtyHistory.clear();
- }
-
- if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
- mCanvas->setViewport(width, height);
+ Frame frame = mEglManager.beginFrame(mEglSurface);
+ if (frame.width() != mCanvas->getViewportWidth()
+ || frame.height() != mCanvas->getViewportHeight()) {
+ mCanvas->setViewport(frame.width(), frame.height());
dirty.setEmpty();
- } else if (!mBufferPreserved || mHaveNewSurface) {
- mDirtyHistory.clear();
+ } else if (mHaveNewSurface || frame.bufferAge() == 0) {
+ // New surface needs a full draw
dirty.setEmpty();
} else {
- if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
+ if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) {
ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
- SK_RECT_ARGS(dirty), width, height);
+ SK_RECT_ARGS(dirty), frame.width(), frame.height());
dirty.setEmpty();
}
profiler().unionDirty(&dirty);
}
- patchedDirty = dirty;
- if (useBufferAgeExt && !dirty.isEmpty()) {
- patchedDirty = mDirtyHistory.unionWith(Rect(dirty), framebufferAge-1);
+ if (dirty.isEmpty()) {
+ dirty.set(0, 0, frame.width(), frame.height());
}
- if (!patchedDirty.isEmpty()) {
- mCanvas->prepareDirty(patchedDirty.left, patchedDirty.top,
- patchedDirty.right, patchedDirty.bottom, mOpaque);
- } else {
- mCanvas->prepare(mOpaque);
+ // At this point dirty is the area of the screen to update. However,
+ // the area of the frame we need to repaint is potentially different, so
+ // stash the screen area for later
+ SkRect screenDirty(dirty);
+
+ // If the buffer age is 0 we do a full-screen repaint (handled above)
+ // If the buffer age is 1 the buffer contents are the same as they were
+ // last frame so there's nothing to union() against
+ // Therefore we only care about the > 1 case.
+ if (frame.bufferAge() > 1) {
+ if (frame.bufferAge() > (int) mDamageHistory.size()) {
+ // We don't have enough history to handle this old of a buffer
+ // Just do a full-draw
+ dirty.set(0, 0, frame.width(), frame.height());
+ } else {
+ // At this point we haven't yet added the latest frame
+ // to the damage history (happens below)
+ // So we need to damage
+ for (int i = mDamageHistory.size() - 1;
+ i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) {
+ dirty.join(mDamageHistory[i]);
+ }
+ }
}
+ // Add the screen damage to the ring buffer.
+ mDamageHistory.next() = screenDirty;
+
+ mEglManager.damageFrame(frame, dirty);
+ mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
+ dirty.fRight, dirty.fBottom, mOpaque);
+
Rect outBounds;
mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
@@ -288,11 +303,30 @@
mCurrentFrameInfo->markSwapBuffers();
if (drew) {
- swapBuffers(dirty, width, height);
+ if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
+ setSurface(nullptr);
+ }
+ mHaveNewSurface = false;
}
// TODO: Use a fence for real completion?
mCurrentFrameInfo->markFrameCompleted();
+
+#if LOG_FRAMETIME_MMA
+ float thisFrame = mCurrentFrameInfo->duration(
+ FrameInfoIndex::IssueDrawCommandsStart,
+ FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F;
+ if (sFrameCount) {
+ sBenchMma = ((9 * sBenchMma) + thisFrame) / 10;
+ } else {
+ sBenchMma = thisFrame;
+ }
+ if (++sFrameCount == 10) {
+ sFrameCount = 1;
+ ALOGD("Average frame time: %.4f", sBenchMma);
+ }
+#endif
+
mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 59f9c3a..0ceb9f1 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,7 +25,6 @@
#include "utils/RingBuffer.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
-#include "renderthread/DirtyHistory.h"
#include <cutils/compiler.h>
#include <EGL/egl.h>
@@ -119,7 +118,6 @@
friend class android::uirenderer::RenderState;
void setSurface(ANativeWindow* window);
- void swapBuffers(const SkRect& dirty, EGLint width, EGLint height);
void requireSurface();
void freePrefetechedLayers();
@@ -130,6 +128,7 @@
EGLSurface mEglSurface = EGL_NO_SURFACE;
bool mBufferPreserved = false;
SwapBehavior mSwapBehavior = kSwap_default;
+ RingBuffer<SkRect, 3> mDamageHistory;
bool mOpaque;
OpenGLRenderer* mCanvas = nullptr;
@@ -147,8 +146,6 @@
FrameInfoVisualizer mProfiler;
std::set<RenderNode*> mPrefetechedLayers;
-
- DirtyHistory mDirtyHistory;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DirtyHistory.cpp b/libs/hwui/renderthread/DirtyHistory.cpp
deleted file mode 100644
index 1419e84..0000000
--- a/libs/hwui/renderthread/DirtyHistory.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "DirtyHistory.h"
-
-namespace android {
-namespace uirenderer {
-namespace renderthread {
-
-DirtyHistory::DirtyHistory()
- : mBack(DIRTY_HISTORY_SIZE - 1) {
- clear();
-}
-
-void DirtyHistory::clear()
-{
- for (int i = 0; i < DIRTY_HISTORY_SIZE; i++) {
- mHistory[i].clear();
- }
-}
-
-Rect DirtyHistory::get(int index) {
- if (index >= DIRTY_HISTORY_SIZE || index < 0)
- return Rect();
- return mHistory[(1 + mBack + index) % DIRTY_HISTORY_SIZE];
-}
-
-Rect DirtyHistory::unionWith(Rect rect, int count) {
- if (rect.isEmpty() || count > DIRTY_HISTORY_SIZE || count < 0)
- return Rect();
-
- for (int i = 0; i < count; i++) {
- Rect ith = get(i);
- if (ith.isEmpty())
- return Rect();
-
- // rect union
- rect.left = fminf(rect.left, ith.left);
- rect.top = fminf(rect.top, ith.top);
- rect.right = fmaxf(rect.right, ith.right);
- rect.bottom = fmaxf(rect.bottom, ith.bottom);
- }
- return rect;
-}
-
-void DirtyHistory::prepend(Rect rect) {
- if (rect.isEmpty()) {
- mHistory[mBack].clear();
- } else {
- mHistory[mBack].set(rect);
- }
- mBack = (mBack + DIRTY_HISTORY_SIZE - 1) % DIRTY_HISTORY_SIZE;
-}
-
-} /* namespace renderthread */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderthread/DirtyHistory.h b/libs/hwui/renderthread/DirtyHistory.h
deleted file mode 100644
index d5ea597..0000000
--- a/libs/hwui/renderthread/DirtyHistory.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef DIRTYHISTORY_H
-#define DIRTYHISTORY_H
-
-#include <Rect.h>
-
-namespace android {
-namespace uirenderer {
-namespace renderthread {
-
-#define DIRTY_HISTORY_SIZE 4
-
-class DirtyHistory {
-public:
- DirtyHistory();
- ~DirtyHistory() {}
-
- Rect get(int index);
- Rect unionWith(Rect rect, int count);
- void prepend(Rect rect);
- void clear();
-private:
- Rect mHistory[DIRTY_HISTORY_SIZE];
- int mBack;
-};
-
-} /* namespace renderthread */
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* DIRTYHISTORY_H */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index ac36f53..d2ce49f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -25,7 +25,8 @@
#include <cutils/properties.h>
#include <EGL/eglext.h>
-#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#include <string>
+
#define GLES_VERSION 2
#define WAIT_FOR_GPU_COMPLETION 0
@@ -63,10 +64,27 @@
return egl_error_str(eglGetError());
}
-static bool load_dirty_regions_property() {
- char buf[PROPERTY_VALUE_MAX];
- int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
- return !strncasecmp("true", buf, len);
+static struct {
+ bool bufferAge = false;
+ bool setDamage = false;
+} EglExtensions;
+
+void Frame::map(const SkRect& in, EGLint* out) const {
+ /* The rectangles are specified relative to the bottom-left of the surface
+ * and the x and y components of each rectangle specify the bottom-left
+ * position of that rectangle.
+ *
+ * HWUI does everything with 0,0 being top-left, so need to map
+ * the rect
+ */
+ SkIRect idirty;
+ in.roundOut(&idirty);
+ EGLint y = mHeight - (idirty.y() + idirty.height());
+ // layout: {x, y, width, height}
+ out[0] = idirty.x();
+ out[1] = y;
+ out[2] = idirty.width();
+ out[3] = idirty.height();
}
EglManager::EglManager(RenderThread& thread)
@@ -75,13 +93,9 @@
, mEglConfig(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
- , mAllowPreserveBuffer(load_dirty_regions_property())
- , mHasBufferAgeExt(false)
, mCurrentSurface(EGL_NO_SURFACE)
, mAtlasMap(nullptr)
, mAtlasMapSize(0) {
- mCanSetPreserveBuffer = mAllowPreserveBuffer;
- ALOGD("Use EGL_SWAP_BEHAVIOR_PRESERVED: %s", mAllowPreserveBuffer ? "true" : "false");
}
void EglManager::initialize() {
@@ -99,10 +113,18 @@
ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
- findExtensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS), mEglExtensionList);
- mHasBufferAgeExt = hasEglExtension("EGL_EXT_buffer_age");
+ initExtensions();
- loadConfig(mHasBufferAgeExt);
+ // Now that extensions are loaded, pick a swap behavior
+ if (Properties::enablePartialUpdates) {
+ if (Properties::useBufferAge && EglExtensions.bufferAge) {
+ mSwapBehavior = SwapBehavior::BufferAge;
+ } else {
+ mSwapBehavior = SwapBehavior::Preserved;
+ }
+ }
+
+ loadConfig();
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface);
@@ -110,17 +132,23 @@
initAtlas();
}
+void EglManager::initExtensions() {
+ std::string extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+ auto has = [&](const char* ext) {
+ return extensions.find(ext) != std::string::npos;
+ };
+ EglExtensions.bufferAge = has("EGL_EXT_buffer_age");
+ EglExtensions.setDamage = has("EGL_KHR_partial_update");
+}
+
bool EglManager::hasEglContext() {
return mEglDisplay != EGL_NO_DISPLAY;
}
-bool EglManager::hasEglExtension(const char* extension) const {
- const std::string s(extension);
- return mEglExtensionList.find(s) != mEglExtensionList.end();
-}
-
-void EglManager::loadConfig(bool useBufferAgeExt) {
- EGLint swapBehavior = (!useBufferAgeExt && mCanSetPreserveBuffer) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+void EglManager::loadConfig() {
+ ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
+ EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved)
+ ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
@@ -137,13 +165,13 @@
EGLint num_configs = 1;
if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
|| num_configs != 1) {
- // Failed to get a valid config
- if (mCanSetPreserveBuffer) {
- ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+ if (mSwapBehavior == SwapBehavior::Preserved) {
// Try again without dirty regions enabled
- mCanSetPreserveBuffer = false;
- loadConfig(useBufferAgeExt);
+ ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+ mSwapBehavior = SwapBehavior::Discard;
+ loadConfig();
} else {
+ // Failed to get a valid config
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
}
}
@@ -247,24 +275,47 @@
return true;
}
-void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height, EGLint* framebufferAge) {
+EGLint EglManager::queryBufferAge(EGLSurface surface) {
+ switch (mSwapBehavior) {
+ case SwapBehavior::Discard:
+ return 0;
+ case SwapBehavior::Preserved:
+ return 1;
+ case SwapBehavior::BufferAge:
+ EGLint bufferAge;
+ eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge);
+ return bufferAge;
+ }
+ return 0;
+}
+
+Frame EglManager::beginFrame(EGLSurface surface) {
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Tried to beginFrame on EGL_NO_SURFACE!");
makeCurrent(surface);
- if (width) {
- eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
- }
- if (height) {
- eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
- }
- if (useBufferAgeExt()) {
- eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, framebufferAge);
- }
+ Frame frame;
+ frame.mSurface = surface;
+ eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
+ eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
+ frame.mBufferAge = queryBufferAge(surface);
eglBeginFrame(mEglDisplay, surface);
+ return frame;
}
-bool EglManager::swapBuffers(EGLSurface surface, const SkRect& dirty,
- EGLint width, EGLint height) {
+void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
+#ifdef EGL_KHR_partial_update
+ if (EglExtensions.setDamage && mSwapBehavior == SwapBehavior::BufferAge) {
+ EGLint rects[4];
+ frame.map(dirty, rects);
+ if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
+ LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
+ (void*)frame.mSurface, egl_error_str());
+ }
+ }
+#endif
+}
+
+bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
#if WAIT_FOR_GPU_COMPLETION
{
@@ -275,28 +326,15 @@
#ifdef EGL_KHR_swap_buffers_with_damage
if (CC_LIKELY(Properties::swapBuffersWithDamage)) {
- SkIRect idirty;
- dirty.roundOut(&idirty);
- /*
- * EGL_KHR_swap_buffers_with_damage spec states:
- *
- * The rectangles are specified relative to the bottom-left of the surface
- * and the x and y components of each rectangle specify the bottom-left
- * position of that rectangle.
- *
- * HWUI does everything with 0,0 being top-left, so need to map
- * the rect
- */
- EGLint y = height - (idirty.y() + idirty.height());
- // layout: {x, y, width, height}
- EGLint rects[4] = { idirty.x(), y, idirty.width(), idirty.height() };
- EGLint numrects = dirty.isEmpty() ? 0 : 1;
- eglSwapBuffersWithDamageKHR(mEglDisplay, surface, rects, numrects);
+ EGLint rects[4];
+ frame.map(screenDirty, rects);
+ eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
+ screenDirty.isEmpty() ? 0 : 1);
} else {
- eglSwapBuffers(mEglDisplay, surface);
+ eglSwapBuffers(mEglDisplay, frame.mSurface);
}
#else
- eglSwapBuffers(mEglDisplay, surface);
+ eglSwapBuffers(mEglDisplay, frame.mSurface);
#endif
EGLint err = eglGetError();
@@ -307,7 +345,8 @@
// For some reason our surface was destroyed out from under us
// This really shouldn't happen, but if it does we can recover easily
// by just not trying to use the surface anymore
- ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...", surface);
+ ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...",
+ frame.mSurface);
return false;
}
LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering",
@@ -316,10 +355,6 @@
return false;
}
-bool EglManager::useBufferAgeExt() {
- return mAllowPreserveBuffer && mHasBufferAgeExt;
-}
-
void EglManager::fence() {
EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
eglClientWaitSyncKHR(mEglDisplay, fence,
@@ -328,21 +363,13 @@
}
bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
- if (CC_UNLIKELY(!mAllowPreserveBuffer)) return false;
+ if (mSwapBehavior != SwapBehavior::Preserved) return false;
- // Use EGL_EXT_buffer_age instead if supported
- if (mHasBufferAgeExt) return true;
-
- bool preserved = false;
- if (mCanSetPreserveBuffer) {
- preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
- preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
- if (CC_UNLIKELY(!preserved)) {
- ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
- (void*) surface, egl_error_str());
- }
- }
- if (CC_UNLIKELY(!preserved)) {
+ bool preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
+ preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
+ if (!preserved) {
+ ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
+ (void*) surface, egl_error_str());
// Maybe it's already set?
EGLint swapBehavior;
if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
@@ -356,19 +383,6 @@
return preserved;
}
-void EglManager::findExtensions(const char* extensions, std::set<std::string>& list) const {
- const char* current = extensions;
- const char* head = current;
- do {
- head = strchr(current, ' ');
- std::string s(current, head ? head - current : strlen(current));
- if (s.length()) {
- list.insert(s);
- }
- current = head + 1;
- } while (head);
-}
-
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index bb5d24b..62b5b99 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -21,13 +21,35 @@
#include <SkRect.h>
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
-#include <set>
namespace android {
namespace uirenderer {
namespace renderthread {
class RenderThread;
+class EglManager;
+
+class Frame {
+public:
+ EGLint width() const { return mWidth; }
+ EGLint height() const { return mHeight; }
+
+ // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
+ // for what this means
+ EGLint bufferAge() const { return mBufferAge; }
+
+private:
+ friend class EglManager;
+
+ EGLSurface mSurface;
+ EGLint mWidth;
+ EGLint mHeight;
+ EGLint mBufferAge;
+
+ // Maps from 0,0 in top-left to 0,0 in bottom-left
+ // If out is not an EGLint[4] you're going to have a bad time
+ void map(const SkRect& in, EGLint* out) const;
+};
// This class contains the shared global EGL objects, such as EGLDisplay
// and EGLConfig, which are re-used by CanvasContext
@@ -38,8 +60,6 @@
bool hasEglContext();
- bool hasEglExtension(const char* extension) const;
-
EGLSurface createSurface(EGLNativeWindowType window);
void destroySurface(EGLSurface surface);
@@ -48,14 +68,13 @@
bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
// Returns true if the current surface changed, false if it was already current
bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr);
- void beginFrame(EGLSurface surface, EGLint* width, EGLint* height, EGLint* framebufferAge);
- bool swapBuffers(EGLSurface surface, const SkRect& dirty, EGLint width, EGLint height);
+ Frame beginFrame(EGLSurface surface);
+ void damageFrame(const Frame& frame, const SkRect& dirty);
+ bool swapBuffers(const Frame& frame, const SkRect& screenDirty);
// Returns true iff the surface is now preserving buffers.
bool setPreserveBuffer(EGLSurface surface, bool preserve);
- bool useBufferAgeExt();
-
void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
void fence();
@@ -67,12 +86,12 @@
// EglContext is never destroyed, method is purposely not implemented
~EglManager();
+ void initExtensions();
void createPBufferSurface();
- void loadConfig(bool useBufferAgeExt);
+ void loadConfig();
void createContext();
void initAtlas();
-
- void findExtensions(const char* extensions, std::set<std::string>& list) const;
+ EGLint queryBufferAge(EGLSurface surface);
RenderThread& mRenderThread;
@@ -81,18 +100,18 @@
EGLContext mEglContext;
EGLSurface mPBufferSurface;
- const bool mAllowPreserveBuffer;
- bool mCanSetPreserveBuffer;
-
- bool mHasBufferAgeExt;
-
EGLSurface mCurrentSurface;
sp<GraphicBuffer> mAtlasBuffer;
int64_t* mAtlasMap;
size_t mAtlasMapSize;
- std::set<std::string> mEglExtensionList;
+ enum class SwapBehavior {
+ Discard,
+ Preserved,
+ BufferAge,
+ };
+ SwapBehavior mSwapBehavior = SwapBehavior::Discard;
};
} /* namespace renderthread */