From cd55852fcd840f7f4c4d7a0a7253a2995c77afa2 Mon Sep 17 00:00:00 2001 From: Greg Daniel Date: Thu, 17 Nov 2016 13:31:40 -0500 Subject: [PATCH] Make buffer age work in Vulkan Test: manual testing in skiavk mode Change-Id: I5b9d8af7d9cecf2f022ef104ec33a5b7477e9e0c --- libs/hwui/Android.mk | 1 + libs/hwui/hwui/Bitmap.cpp | 1 + .../hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 1 + .../hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 5 +- libs/hwui/renderthread/CanvasContext.cpp | 1 + libs/hwui/renderthread/EglManager.cpp | 19 +----- libs/hwui/renderthread/EglManager.h | 30 +--------- libs/hwui/renderthread/Frame.cpp | 44 ++++++++++++++ libs/hwui/renderthread/Frame.h | 60 +++++++++++++++++++ libs/hwui/renderthread/IRenderPipeline.h | 1 - libs/hwui/renderthread/OpenGLPipeline.cpp | 1 + libs/hwui/renderthread/VulkanManager.cpp | 40 +++++++++---- libs/hwui/renderthread/VulkanManager.h | 19 +++++- 13 files changed, 158 insertions(+), 65 deletions(-) create mode 100644 libs/hwui/renderthread/Frame.cpp create mode 100644 libs/hwui/renderthread/Frame.h diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index cf571e97c1f9f..21ebe5f69c871 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -46,6 +46,7 @@ hwui_src_files := \ 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 d3e765dbfbd1b..64ef866631f35 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 7f3474a1bdf34..c8258f748d7ea 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 ca394b2d0b453..0d3f4ef6a11ad 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 @@ Frame SkiaVulkanPipeline::getFrame() { 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 c561c86460c6c..1b3bf96998ddf 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 de95bee39763f..02021fce680b7 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 @@ static struct { 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 b12522e6bd41e..025192511cd98 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -26,36 +26,8 @@ namespace android { 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 0000000000000..126bb093b4eb9 --- /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 + +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 0000000000000..99996fe406267 --- /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 + +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 0e4000b09e1a3..45f6718a68fb2 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 #include diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp index 9dc2b596e3e8a..df085998435b1 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 4d239bc616d02..68c04afb2e080 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 @@ -100,6 +101,10 @@ void VulkanManager::initialize() { 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 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { } // 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 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { // We need to notify Skia that we changed the layout of the wrapped VkImage GrVkImageInfo* imageInfo; - sk_sp skSurface = surface->mSurfaces[backbuffer->mImageIndex]; + sk_sp 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 @@ void VulkanManager::destroyBuffers(VulkanSurface* surface) { 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 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt 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[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 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt 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 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { 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 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { 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 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { 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 f0e33206a5761..d225b3fc4ec03 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -46,6 +46,13 @@ private: VkFence mUsageFences[2]; }; + struct ImageInfo { + VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + sk_sp mSurface; + uint16_t mLastUsed = 0; + bool mInvalid = true; + }; + sk_sp mBackbuffer; VkSurfaceKHR mVkSurface = VK_NULL_HANDLE; @@ -56,8 +63,8 @@ private: uint32_t mImageCount; VkImage* mImages; - VkImageLayout* mImageLayouts; - sk_sp* 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 @@ public: // 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 @@ private: uint32_t mPresentQueueIndex; VkQueue mPresentQueue = VK_NULL_HANDLE; VkCommandPool mCommandPool = VK_NULL_HANDLE; + + enum class SwapBehavior { + Discard, + BufferAge, + }; + SwapBehavior mSwapBehavior = SwapBehavior::Discard; }; } /* namespace renderthread */