Make buffer age work in Vulkan
Test: manual testing in skiavk mode
Change-Id: I5b9d8af7d9cecf2f022ef104ec33a5b7477e9e0c
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cf571e9..21ebe5f 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -46,6 +46,7 @@
renderthread/RenderTask.cpp \
renderthread/RenderThread.cpp \
renderthread/TimeLord.cpp \
+ renderthread/Frame.cpp \
thread/TaskManager.cpp \
utils/Blur.cpp \
utils/GLUtils.cpp \
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index d3e765d..64ef866 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -16,6 +16,7 @@
#include "Bitmap.h"
#include "Caches.h"
+#include "renderthread/EglManager.h"
#include "renderthread/RenderThread.h"
#include "renderthread/RenderProxy.h"
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 7f3474a..c8258f7 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -19,6 +19,7 @@
#include "DeferredLayerUpdater.h"
#include "LayerDrawable.h"
#include "renderthread/EglManager.h"
+#include "renderthread/Frame.h"
#include "renderstate/RenderState.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index ca394b2..0d3f4ef 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -17,7 +17,7 @@
#include "SkiaVulkanPipeline.h"
#include "DeferredLayerUpdater.h"
-#include "renderthread/EglManager.h" // needed for Frame
+#include "renderthread/Frame.h"
#include "Readback.h"
#include "renderstate/RenderState.h"
#include "SkiaPipeline.h"
@@ -58,8 +58,7 @@
return Frame(-1, -1, 0);
}
- // TODO: support buffer age if Vulkan API can do it
- Frame frame(backBuffer->width(), backBuffer->height(), 0);
+ Frame frame(backBuffer->width(), backBuffer->height(), mVkManager.getAge(mVkSurface));
return frame;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c561c86..1b3bf96 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -20,6 +20,7 @@
#include "AnimationContext.h"
#include "Caches.h"
#include "EglManager.h"
+#include "Frame.h"
#include "LayerUpdateQueue.h"
#include "Properties.h"
#include "RenderThread.h"
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index de95bee..02021fc 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -19,6 +19,7 @@
#include "Texture.h"
#include "Caches.h"
#include "DeviceInfo.h"
+#include "Frame.h"
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
@@ -74,24 +75,6 @@
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)
: mRenderThread(thread)
, mEglDisplay(EGL_NO_DISPLAY)
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index b12522e..0251925 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -26,36 +26,8 @@
namespace uirenderer {
namespace renderthread {
+class Frame;
class RenderThread;
-class EglManager;
-
-class Frame {
-public:
- Frame(EGLint width, EGLint height, EGLint bufferAge)
- : mWidth(width)
- , mHeight(height)
- , mBufferAge(bufferAge) { }
-
- 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:
- Frame() {}
- 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
diff --git a/libs/hwui/renderthread/Frame.cpp b/libs/hwui/renderthread/Frame.cpp
new file mode 100644
index 0000000..126bb09
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 "Frame.h"
+#include <SkRect.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+void Frame::map(const SkRect& in, int32_t* 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);
+ int32_t 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();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/Frame.h b/libs/hwui/renderthread/Frame.h
new file mode 100644
index 0000000..99996fe
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+struct SkRect;
+typedef void *EGLSurface;
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class Frame {
+public:
+ Frame(int32_t width, int32_t height, int32_t bufferAge)
+ : mWidth(width)
+ , mHeight(height)
+ , mBufferAge(bufferAge) { }
+
+ int32_t width() const { return mWidth; }
+ int32_t height() const { return mHeight; }
+
+ // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
+ // for what this means
+ int32_t bufferAge() const { return mBufferAge; }
+
+private:
+ Frame() {}
+ friend class EglManager;
+
+ int32_t mWidth;
+ int32_t mHeight;
+ int32_t mBufferAge;
+
+ EGLSurface mSurface;
+
+ // Maps from 0,0 in top-left to 0,0 in bottom-left
+ // If out is not an int32_t[4] you're going to have a bad time
+ void map(const SkRect& in, int32_t* out) const;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 0e4000b..45f6718 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -17,7 +17,6 @@
#pragma once
#include "FrameInfoVisualizer.h"
-#include "EglManager.h"
#include <SkRect.h>
#include <utils/RefBase.h>
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index 9dc2b59..df08599 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -18,6 +18,7 @@
#include "DeferredLayerUpdater.h"
#include "EglManager.h"
+#include "Frame.h"
#include "ProfileRenderer.h"
#include "renderstate/RenderState.h"
#include "OpenGLReadback.h"
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4d239bc..68c04af 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -17,6 +17,7 @@
#include "VulkanManager.h"
#include "DeviceInfo.h"
+#include "Properties.h"
#include "RenderThread.h"
#include <GrContext.h>
@@ -100,6 +101,10 @@
mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend,
(GrBackendContext) mBackendContext.get()));
DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
+
+ if (Properties::enablePartialUpdates && Properties::useBufferAge) {
+ mSwapBehavior = SwapBehavior::BufferAge;
+ }
}
// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
@@ -162,7 +167,7 @@
}
// set up layout transfer from initial to color attachment
- VkImageLayout layout = surface->mImageLayouts[backbuffer->mImageIndex];
+ VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
@@ -215,7 +220,7 @@
// We need to notify Skia that we changed the layout of the wrapped VkImage
GrVkImageInfo* imageInfo;
- sk_sp<SkSurface> skSurface = surface->mSurfaces[backbuffer->mImageIndex];
+ sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
SkSurface::kFlushRead_BackendHandleAccess);
imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
@@ -243,10 +248,8 @@
delete[] surface->mBackbuffers;
surface->mBackbuffers = nullptr;
- delete[] surface->mSurfaces;
- surface->mSurfaces = nullptr;
- delete[] surface->mImageLayouts;
- surface->mImageLayouts = nullptr;
+ delete[] surface->mImageInfos;
+ surface->mImageInfos = nullptr;
delete[] surface->mImages;
surface->mImages = nullptr;
}
@@ -286,8 +289,7 @@
GrPixelConfig config = wantSRGB ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
// set up initial image layouts and create surfaces
- surface->mImageLayouts = new VkImageLayout[surface->mImageCount];
- surface->mSurfaces = new sk_sp<SkSurface>[surface->mImageCount];
+ surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount];
for (uint32_t i = 0; i < surface->mImageCount; ++i) {
GrBackendRenderTargetDesc desc;
GrVkImageInfo info;
@@ -306,9 +308,9 @@
desc.fStencilBits = 0;
desc.fRenderTargetHandle = (GrBackendObject) &info;
- surface->mSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
+ VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
+ imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
desc, &props);
- surface->mImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
}
SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -595,7 +597,7 @@
VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
surface->mCurrentBackbufferIndex;
GrVkImageInfo* imageInfo;
- SkSurface* skSurface = surface->mSurfaces[backbuffer->mImageIndex].get();
+ SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
SkSurface::kFlushRead_BackendHandleAccess);
// Check to make sure we never change the actually wrapped image
@@ -632,7 +634,7 @@
0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]);
- surface->mImageLayouts[backbuffer->mImageIndex] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// insert the layout transfer into the queue and wait on the acquire
VkSubmitInfo submitInfo;
@@ -668,6 +670,20 @@
mQueuePresentKHR(mPresentQueue, &presentInfo);
surface->mBackbuffer.reset();
+ surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime;
+ surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false;
+ surface->mCurrentTime++;
+}
+
+int VulkanManager::getAge(VulkanSurface* surface) {
+ VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
+ surface->mCurrentBackbufferIndex;
+ if (mSwapBehavior == SwapBehavior::Discard
+ || surface->mImageInfos[backbuffer->mImageIndex].mInvalid) {
+ return 0;
+ }
+ uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed;
+ return surface->mCurrentTime - lastUsed;
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index f0e3320..d225b3f 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -46,6 +46,13 @@
VkFence mUsageFences[2];
};
+ struct ImageInfo {
+ VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ sk_sp<SkSurface> mSurface;
+ uint16_t mLastUsed = 0;
+ bool mInvalid = true;
+ };
+
sk_sp<SkSurface> mBackbuffer;
VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
@@ -56,8 +63,8 @@
uint32_t mImageCount;
VkImage* mImages;
- VkImageLayout* mImageLayouts;
- sk_sp<SkSurface>* mSurfaces;
+ ImageInfo* mImageInfos;
+ uint16_t mCurrentTime = 0;
};
// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -87,6 +94,8 @@
// VulkanSurface is passed into them so we just return true here.
bool isCurrent(VulkanSurface* surface) { return true; }
+ int getAge(VulkanSurface* surface);
+
// Returns an SkSurface which wraps the next image returned from vkAcquireNextImageKHR. It also
// will transition the VkImage from a present layout to color attachment so that it can be used
// by the client for drawing.
@@ -161,6 +170,12 @@
uint32_t mPresentQueueIndex;
VkQueue mPresentQueue = VK_NULL_HANDLE;
VkCommandPool mCommandPool = VK_NULL_HANDLE;
+
+ enum class SwapBehavior {
+ Discard,
+ BufferAge,
+ };
+ SwapBehavior mSwapBehavior = SwapBehavior::Discard;
};
} /* namespace renderthread */