• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // SurfaceVk.cpp:
7 //    Implements the class methods for SurfaceVk.
8 //
9 
10 #include "libANGLE/renderer/vulkan/SurfaceVk.h"
11 
12 #include "common/debug.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/Display.h"
15 #include "libANGLE/Overlay.h"
16 #include "libANGLE/Surface.h"
17 #include "libANGLE/renderer/vulkan/ContextVk.h"
18 #include "libANGLE/renderer/vulkan/DisplayVk.h"
19 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
20 #include "libANGLE/renderer/vulkan/OverlayVk.h"
21 #include "libANGLE/renderer/vulkan/RendererVk.h"
22 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
23 #include "libANGLE/trace.h"
24 
25 namespace rx
26 {
27 
28 namespace
29 {
GetSampleCount(const egl::Config * config)30 GLint GetSampleCount(const egl::Config *config)
31 {
32     GLint samples = 1;
33     if (config->sampleBuffers && config->samples > 1)
34     {
35         samples = config->samples;
36     }
37     return samples;
38 }
39 
GetDesiredPresentMode(const std::vector<VkPresentModeKHR> & presentModes,EGLint interval)40 VkPresentModeKHR GetDesiredPresentMode(const std::vector<VkPresentModeKHR> &presentModes,
41                                        EGLint interval)
42 {
43     ASSERT(!presentModes.empty());
44 
45     // If v-sync is enabled, use FIFO, which throttles you to the display rate and is guaranteed to
46     // always be supported.
47     if (interval > 0)
48     {
49         return VK_PRESENT_MODE_FIFO_KHR;
50     }
51 
52     // Otherwise, choose either of the following, if available, in order specified here:
53     //
54     // - Mailbox is similar to triple-buffering.
55     // - Immediate is similar to single-buffering.
56     //
57     // If neither is supported, we fallback to FIFO.
58 
59     bool mailboxAvailable   = false;
60     bool immediateAvailable = false;
61 
62     for (VkPresentModeKHR presentMode : presentModes)
63     {
64         switch (presentMode)
65         {
66             case VK_PRESENT_MODE_MAILBOX_KHR:
67                 mailboxAvailable = true;
68                 break;
69             case VK_PRESENT_MODE_IMMEDIATE_KHR:
70                 immediateAvailable = true;
71                 break;
72             default:
73                 break;
74         }
75     }
76 
77     if (immediateAvailable)
78     {
79         return VK_PRESENT_MODE_IMMEDIATE_KHR;
80     }
81 
82     if (mailboxAvailable)
83     {
84         return VK_PRESENT_MODE_MAILBOX_KHR;
85     }
86 
87     // Note again that VK_PRESENT_MODE_FIFO_KHR is guaranteed to be available.
88     return VK_PRESENT_MODE_FIFO_KHR;
89 }
90 
91 constexpr VkImageUsageFlags kSurfaceVKImageUsageFlags =
92     VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
93 constexpr VkImageUsageFlags kSurfaceVKColorImageUsageFlags =
94     kSurfaceVKImageUsageFlags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
95 constexpr VkImageUsageFlags kSurfaceVKDepthStencilImageUsageFlags =
96     kSurfaceVKImageUsageFlags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
97 
98 // If the device is rotated with any of the following transform flags, the swapchain width and
99 // height must be swapped (e.g. make a landscape window portrait).  This must also be done for all
100 // attachments used with the swapchain (i.e. depth, stencil, and multisample buffers).
101 constexpr VkSurfaceTransformFlagsKHR k90DegreeRotationVariants =
102     VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
103     VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
104     VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
105 
Is90DegreeRotation(VkSurfaceTransformFlagsKHR transform)106 bool Is90DegreeRotation(VkSurfaceTransformFlagsKHR transform)
107 {
108     return ((transform & k90DegreeRotationVariants) != 0);
109 }
110 
111 }  // namespace
112 
SurfaceVk(const egl::SurfaceState & surfaceState)113 SurfaceVk::SurfaceVk(const egl::SurfaceState &surfaceState) : SurfaceImpl(surfaceState) {}
114 
115 SurfaceVk::~SurfaceVk() = default;
116 
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)117 angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
118                                                    GLenum binding,
119                                                    const gl::ImageIndex &imageIndex,
120                                                    GLsizei samples,
121                                                    FramebufferAttachmentRenderTarget **rtOut)
122 {
123     ContextVk *contextVk = vk::GetImpl(context);
124 
125     if (binding == GL_BACK)
126     {
127         ANGLE_TRY(mColorRenderTarget.flushStagedUpdates(contextVk));
128         *rtOut = &mColorRenderTarget;
129     }
130     else
131     {
132         ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
133         ANGLE_TRY(mDepthStencilRenderTarget.flushStagedUpdates(contextVk));
134         *rtOut = &mDepthStencilRenderTarget;
135     }
136 
137     return angle::Result::Continue;
138 }
139 
AttachmentImage()140 OffscreenSurfaceVk::AttachmentImage::AttachmentImage() {}
141 
142 OffscreenSurfaceVk::AttachmentImage::~AttachmentImage() = default;
143 
initialize(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples)144 angle::Result OffscreenSurfaceVk::AttachmentImage::initialize(DisplayVk *displayVk,
145                                                               EGLint width,
146                                                               EGLint height,
147                                                               const vk::Format &vkFormat,
148                                                               GLint samples)
149 {
150     RendererVk *renderer = displayVk->getRenderer();
151 
152     const angle::Format &textureFormat = vkFormat.actualImageFormat();
153     bool isDepthOrStencilFormat   = textureFormat.depthBits > 0 || textureFormat.stencilBits > 0;
154     const VkImageUsageFlags usage = isDepthOrStencilFormat ? kSurfaceVKDepthStencilImageUsageFlags
155                                                            : kSurfaceVKColorImageUsageFlags;
156 
157     VkExtent3D extents = {std::max(static_cast<uint32_t>(width), 1u),
158                           std::max(static_cast<uint32_t>(height), 1u), 1u};
159     ANGLE_TRY(
160         image.init(displayVk, gl::TextureType::_2D, extents, vkFormat, samples, usage, 0, 0, 1, 1));
161 
162     VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
163     ANGLE_TRY(image.initMemory(displayVk, renderer->getMemoryProperties(), flags));
164 
165     return angle::Result::Continue;
166 }
167 
initializeWithExternalMemory(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples,void * buffer)168 angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory(
169     DisplayVk *displayVk,
170     EGLint width,
171     EGLint height,
172     const vk::Format &vkFormat,
173     GLint samples,
174     void *buffer)
175 {
176     RendererVk *renderer = displayVk->getRenderer();
177 
178     ASSERT(renderer->getFeatures().supportsExternalMemoryHost.enabled);
179 
180     const angle::Format &textureFormat = vkFormat.actualImageFormat();
181     bool isDepthOrStencilFormat   = textureFormat.depthBits > 0 || textureFormat.stencilBits > 0;
182     const VkImageUsageFlags usage = isDepthOrStencilFormat ? kSurfaceVKDepthStencilImageUsageFlags
183                                                            : kSurfaceVKColorImageUsageFlags;
184 
185     VkExtent3D extents = {std::max(static_cast<uint32_t>(width), 1u),
186                           std::max(static_cast<uint32_t>(height), 1u), 1u};
187     ANGLE_TRY(
188         image.init(displayVk, gl::TextureType::_2D, extents, vkFormat, samples, usage, 0, 0, 1, 1));
189 
190     VkImportMemoryHostPointerInfoEXT importMemoryHostPointerInfo = {};
191     importMemoryHostPointerInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT;
192     importMemoryHostPointerInfo.pNext = nullptr;
193     importMemoryHostPointerInfo.handleType =
194         VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT;
195     importMemoryHostPointerInfo.pHostPointer = buffer;
196 
197     VkMemoryRequirements externalMemoryRequirements;
198     image.getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements);
199 
200     VkMemoryPropertyFlags flags = 0;
201     ANGLE_TRY(image.initExternalMemory(displayVk, renderer->getMemoryProperties(),
202                                        externalMemoryRequirements, &importMemoryHostPointerInfo,
203                                        VK_QUEUE_FAMILY_EXTERNAL, flags));
204 
205     return angle::Result::Continue;
206 }
207 
destroy(const egl::Display * display)208 void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
209 {
210     DisplayVk *displayVk = vk::GetImpl(display);
211     RendererVk *renderer = displayVk->getRenderer();
212     image.releaseImage(renderer);
213     image.releaseStagingBuffer(renderer);
214     imageViews.release(renderer);
215 }
216 
OffscreenSurfaceVk(const egl::SurfaceState & surfaceState)217 OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState)
218     : SurfaceVk(surfaceState),
219       mWidth(mState.attributes.getAsInt(EGL_WIDTH, 0)),
220       mHeight(mState.attributes.getAsInt(EGL_HEIGHT, 0))
221 {
222     mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, 0, 0);
223     mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
224                                    &mDepthStencilAttachment.imageViews, 0, 0);
225 }
226 
~OffscreenSurfaceVk()227 OffscreenSurfaceVk::~OffscreenSurfaceVk() {}
228 
initialize(const egl::Display * display)229 egl::Error OffscreenSurfaceVk::initialize(const egl::Display *display)
230 {
231     DisplayVk *displayVk = vk::GetImpl(display);
232     angle::Result result = initializeImpl(displayVk);
233     return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
234 }
235 
initializeImpl(DisplayVk * displayVk)236 angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
237 {
238     RendererVk *renderer      = displayVk->getRenderer();
239     const egl::Config *config = mState.config;
240 
241     renderer->reloadVolkIfNeeded();
242 
243     GLint samples = GetSampleCount(mState.config);
244     ANGLE_VK_CHECK(displayVk, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
245 
246     if (config->renderTargetFormat != GL_NONE)
247     {
248         ANGLE_TRY(mColorAttachment.initialize(
249             displayVk, mWidth, mHeight, renderer->getFormat(config->renderTargetFormat), samples));
250         mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, 0, 0);
251     }
252 
253     if (config->depthStencilFormat != GL_NONE)
254     {
255         ANGLE_TRY(mDepthStencilAttachment.initialize(
256             displayVk, mWidth, mHeight, renderer->getFormat(config->depthStencilFormat), samples));
257         mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
258                                        &mDepthStencilAttachment.imageViews, 0, 0);
259     }
260 
261     return angle::Result::Continue;
262 }
263 
destroy(const egl::Display * display)264 void OffscreenSurfaceVk::destroy(const egl::Display *display)
265 {
266     mColorAttachment.destroy(display);
267     mDepthStencilAttachment.destroy(display);
268 }
269 
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)270 FramebufferImpl *OffscreenSurfaceVk::createDefaultFramebuffer(const gl::Context *context,
271                                                               const gl::FramebufferState &state)
272 {
273     RendererVk *renderer = vk::GetImpl(context)->getRenderer();
274 
275     // Use a user FBO for an offscreen RT.
276     return FramebufferVk::CreateUserFBO(renderer, state);
277 }
278 
swap(const gl::Context * context)279 egl::Error OffscreenSurfaceVk::swap(const gl::Context *context)
280 {
281     return egl::NoError();
282 }
283 
postSubBuffer(const gl::Context *,EGLint,EGLint,EGLint,EGLint)284 egl::Error OffscreenSurfaceVk::postSubBuffer(const gl::Context * /*context*/,
285                                              EGLint /*x*/,
286                                              EGLint /*y*/,
287                                              EGLint /*width*/,
288                                              EGLint /*height*/)
289 {
290     return egl::NoError();
291 }
292 
querySurfacePointerANGLE(EGLint,void **)293 egl::Error OffscreenSurfaceVk::querySurfacePointerANGLE(EGLint /*attribute*/, void ** /*value*/)
294 {
295     UNREACHABLE();
296     return egl::EglBadCurrentSurface();
297 }
298 
bindTexImage(const gl::Context *,gl::Texture *,EGLint)299 egl::Error OffscreenSurfaceVk::bindTexImage(const gl::Context * /*context*/,
300                                             gl::Texture * /*texture*/,
301                                             EGLint /*buffer*/)
302 {
303     return egl::NoError();
304 }
305 
releaseTexImage(const gl::Context *,EGLint)306 egl::Error OffscreenSurfaceVk::releaseTexImage(const gl::Context * /*context*/, EGLint /*buffer*/)
307 {
308     return egl::NoError();
309 }
310 
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)311 egl::Error OffscreenSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
312                                              EGLuint64KHR * /*msc*/,
313                                              EGLuint64KHR * /*sbc*/)
314 {
315     UNIMPLEMENTED();
316     return egl::EglBadAccess();
317 }
318 
getMscRate(EGLint *,EGLint *)319 egl::Error OffscreenSurfaceVk::getMscRate(EGLint * /*numerator*/, EGLint * /*denominator*/)
320 {
321     UNIMPLEMENTED();
322     return egl::EglBadAccess();
323 }
324 
setSwapInterval(EGLint)325 void OffscreenSurfaceVk::setSwapInterval(EGLint /*interval*/) {}
326 
getWidth() const327 EGLint OffscreenSurfaceVk::getWidth() const
328 {
329     return mWidth;
330 }
331 
getHeight() const332 EGLint OffscreenSurfaceVk::getHeight() const
333 {
334     return mHeight;
335 }
336 
isPostSubBufferSupported() const337 EGLint OffscreenSurfaceVk::isPostSubBufferSupported() const
338 {
339     return EGL_FALSE;
340 }
341 
getSwapBehavior() const342 EGLint OffscreenSurfaceVk::getSwapBehavior() const
343 {
344     return EGL_BUFFER_DESTROYED;
345 }
346 
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)347 angle::Result OffscreenSurfaceVk::initializeContents(const gl::Context *context,
348                                                      const gl::ImageIndex &imageIndex)
349 {
350     ContextVk *contextVk = vk::GetImpl(context);
351 
352     if (mColorAttachment.image.valid())
353     {
354         mColorAttachment.image.stageSubresourceClear(imageIndex);
355         ANGLE_TRY(mColorAttachment.image.flushAllStagedUpdates(contextVk));
356     }
357 
358     if (mDepthStencilAttachment.image.valid())
359     {
360         mDepthStencilAttachment.image.stageSubresourceClear(imageIndex);
361         ANGLE_TRY(mDepthStencilAttachment.image.flushAllStagedUpdates(contextVk));
362     }
363     return angle::Result::Continue;
364 }
365 
getColorAttachmentImage()366 vk::ImageHelper *OffscreenSurfaceVk::getColorAttachmentImage()
367 {
368     return &mColorAttachment.image;
369 }
370 
371 namespace impl
372 {
373 SwapchainCleanupData::SwapchainCleanupData() = default;
~SwapchainCleanupData()374 SwapchainCleanupData::~SwapchainCleanupData()
375 {
376     ASSERT(swapchain == VK_NULL_HANDLE);
377     ASSERT(semaphores.empty());
378 }
379 
SwapchainCleanupData(SwapchainCleanupData && other)380 SwapchainCleanupData::SwapchainCleanupData(SwapchainCleanupData &&other)
381     : swapchain(other.swapchain), semaphores(std::move(other.semaphores))
382 {
383     other.swapchain = VK_NULL_HANDLE;
384 }
385 
destroy(VkDevice device,vk::Recycler<vk::Semaphore> * semaphoreRecycler)386 void SwapchainCleanupData::destroy(VkDevice device, vk::Recycler<vk::Semaphore> *semaphoreRecycler)
387 {
388     if (swapchain)
389     {
390         vkDestroySwapchainKHR(device, swapchain, nullptr);
391         swapchain = VK_NULL_HANDLE;
392     }
393 
394     for (vk::Semaphore &semaphore : semaphores)
395     {
396         semaphoreRecycler->recycle(std::move(semaphore));
397     }
398     semaphores.clear();
399 }
400 
401 ImagePresentHistory::ImagePresentHistory() = default;
~ImagePresentHistory()402 ImagePresentHistory::~ImagePresentHistory()
403 {
404     ASSERT(!semaphore.valid());
405     ASSERT(oldSwapchains.empty());
406 }
407 
ImagePresentHistory(ImagePresentHistory && other)408 ImagePresentHistory::ImagePresentHistory(ImagePresentHistory &&other)
409     : semaphore(std::move(other.semaphore)), oldSwapchains(std::move(other.oldSwapchains))
410 {}
411 
412 SwapchainImage::SwapchainImage()  = default;
413 SwapchainImage::~SwapchainImage() = default;
414 
SwapchainImage(SwapchainImage && other)415 SwapchainImage::SwapchainImage(SwapchainImage &&other)
416     : image(std::move(other.image)),
417       imageViews(std::move(other.imageViews)),
418       framebuffer(std::move(other.framebuffer)),
419       presentHistory(std::move(other.presentHistory)),
420       currentPresentHistoryIndex(other.currentPresentHistoryIndex)
421 {}
422 
423 SwapHistory::SwapHistory() = default;
424 
425 SwapHistory::~SwapHistory() = default;
426 
destroy(RendererVk * renderer)427 void SwapHistory::destroy(RendererVk *renderer)
428 {
429     renderer->resetSharedFence(&sharedFence);
430 }
431 
waitFence(ContextVk * contextVk)432 angle::Result SwapHistory::waitFence(ContextVk *contextVk)
433 {
434     ASSERT(sharedFence.isReferenced());
435     ANGLE_VK_TRY(contextVk, sharedFence.get().wait(contextVk->getDevice(),
436                                                    std::numeric_limits<uint64_t>::max()));
437     return angle::Result::Continue;
438 }
439 }  // namespace impl
440 
441 using namespace impl;
442 
WindowSurfaceVk(const egl::SurfaceState & surfaceState,EGLNativeWindowType window)443 WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativeWindowType window)
444     : SurfaceVk(surfaceState),
445       mNativeWindowType(window),
446       mSurface(VK_NULL_HANDLE),
447       mSwapchain(VK_NULL_HANDLE),
448       mSwapchainPresentMode(VK_PRESENT_MODE_FIFO_KHR),
449       mDesiredSwapchainPresentMode(VK_PRESENT_MODE_FIFO_KHR),
450       mMinImageCount(0),
451       mPreTransform(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR),
452       mCompositeAlpha(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR),
453       mCurrentSwapHistoryIndex(0),
454       mCurrentSwapchainImageIndex(0)
455 {
456     // Initialize the color render target with the multisampled targets.  If not multisampled, the
457     // render target will be updated to refer to a swapchain image on every acquire.
458     mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, 0, 0);
459     mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, 0, 0);
460 }
461 
~WindowSurfaceVk()462 WindowSurfaceVk::~WindowSurfaceVk()
463 {
464     ASSERT(mSurface == VK_NULL_HANDLE);
465     ASSERT(mSwapchain == VK_NULL_HANDLE);
466 }
467 
destroy(const egl::Display * display)468 void WindowSurfaceVk::destroy(const egl::Display *display)
469 {
470     DisplayVk *displayVk = vk::GetImpl(display);
471     RendererVk *renderer = displayVk->getRenderer();
472     VkDevice device      = renderer->getDevice();
473     VkInstance instance  = renderer->getInstance();
474 
475     // flush the pipe.
476     (void)renderer->deviceWaitIdle(displayVk);
477 
478     destroySwapChainImages(displayVk);
479 
480     for (SwapHistory &swap : mSwapHistory)
481     {
482         swap.destroy(renderer);
483     }
484 
485     if (mSwapchain)
486     {
487         vkDestroySwapchainKHR(device, mSwapchain, nullptr);
488         mSwapchain = VK_NULL_HANDLE;
489     }
490 
491     for (SwapchainCleanupData &oldSwapchain : mOldSwapchains)
492     {
493         oldSwapchain.destroy(device, &mPresentSemaphoreRecycler);
494     }
495     mOldSwapchains.clear();
496 
497     if (mSurface)
498     {
499         vkDestroySurfaceKHR(instance, mSurface, nullptr);
500         mSurface = VK_NULL_HANDLE;
501     }
502 
503     mAcquireImageSemaphore.destroy(device);
504     mPresentSemaphoreRecycler.destroy(device);
505 }
506 
initialize(const egl::Display * display)507 egl::Error WindowSurfaceVk::initialize(const egl::Display *display)
508 {
509     DisplayVk *displayVk = vk::GetImpl(display);
510     angle::Result result = initializeImpl(displayVk);
511     if (result == angle::Result::Incomplete)
512     {
513         return angle::ToEGL(result, displayVk, EGL_BAD_MATCH);
514     }
515     else
516     {
517         return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
518     }
519 }
520 
initializeImpl(DisplayVk * displayVk)521 angle::Result WindowSurfaceVk::initializeImpl(DisplayVk *displayVk)
522 {
523     RendererVk *renderer = displayVk->getRenderer();
524 
525     renderer->reloadVolkIfNeeded();
526 
527     gl::Extents windowSize;
528     ANGLE_TRY(createSurfaceVk(displayVk, &windowSize));
529 
530     uint32_t presentQueue = 0;
531     ANGLE_TRY(renderer->selectPresentQueueForSurface(displayVk, mSurface, &presentQueue));
532     ANGLE_UNUSED_VARIABLE(presentQueue);
533 
534     const VkPhysicalDevice &physicalDevice = renderer->getPhysicalDevice();
535 
536     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface,
537                                                                       &mSurfaceCaps));
538 
539     // Adjust width and height to the swapchain if necessary.
540     uint32_t width  = mSurfaceCaps.currentExtent.width;
541     uint32_t height = mSurfaceCaps.currentExtent.height;
542 
543     // TODO(jmadill): Support devices which don't support copy. We use this for ReadPixels.
544     ANGLE_VK_CHECK(displayVk,
545                    (mSurfaceCaps.supportedUsageFlags & kSurfaceVKColorImageUsageFlags) ==
546                        kSurfaceVKColorImageUsageFlags,
547                    VK_ERROR_INITIALIZATION_FAILED);
548 
549     EGLAttrib attribWidth  = mState.attributes.get(EGL_WIDTH, 0);
550     EGLAttrib attribHeight = mState.attributes.get(EGL_HEIGHT, 0);
551 
552     if (mSurfaceCaps.currentExtent.width == 0xFFFFFFFFu)
553     {
554         ASSERT(mSurfaceCaps.currentExtent.height == 0xFFFFFFFFu);
555 
556         width  = (attribWidth != 0) ? static_cast<uint32_t>(attribWidth) : windowSize.width;
557         height = (attribHeight != 0) ? static_cast<uint32_t>(attribHeight) : windowSize.height;
558     }
559 
560     gl::Extents extents(static_cast<int>(width), static_cast<int>(height), 1);
561 
562     if (renderer->getFeatures().enablePreRotateSurfaces.enabled)
563     {
564         // Use the surface's transform.  For many platforms, this will always be identity (ANGLE
565         // does not need to do any pre-rotation).  However, when mSurfaceCaps.currentTransform is
566         // not identity, the device has been rotated away from its natural orientation.  In such a
567         // case, ANGLE must rotate all rendering in order to avoid the compositor
568         // (e.g. SurfaceFlinger on Android) performing an additional rotation blit.  In addition,
569         // ANGLE must create the swapchain with VkSwapchainCreateInfoKHR::preTransform set to the
570         // value of mSurfaceCaps.currentTransform.
571         mPreTransform = mSurfaceCaps.currentTransform;
572     }
573     else
574     {
575         // Default to identity transform.
576         mPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
577         if ((mSurfaceCaps.supportedTransforms & mPreTransform) == 0)
578         {
579             mPreTransform = mSurfaceCaps.currentTransform;
580         }
581     }
582 
583     uint32_t presentModeCount = 0;
584     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, mSurface,
585                                                                       &presentModeCount, nullptr));
586     ASSERT(presentModeCount > 0);
587 
588     mPresentModes.resize(presentModeCount);
589     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(
590                                 physicalDevice, mSurface, &presentModeCount, mPresentModes.data()));
591 
592     // Select appropriate present mode based on vsync parameter.  Default to 1 (FIFO), though it
593     // will get clamped to the min/max values specified at display creation time.
594     setSwapInterval(renderer->getFeatures().disableFifoPresentMode.enabled ? 0 : 1);
595 
596     uint32_t surfaceFormatCount = 0;
597     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface,
598                                                                  &surfaceFormatCount, nullptr));
599 
600     std::vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatCount);
601     ANGLE_VK_TRY(displayVk,
602                  vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &surfaceFormatCount,
603                                                       surfaceFormats.data()));
604 
605     const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
606     VkFormat nativeFormat    = format.vkImageFormat;
607 
608     if (surfaceFormatCount == 1u && surfaceFormats[0].format == VK_FORMAT_UNDEFINED)
609     {
610         // This is fine.
611     }
612     else
613     {
614         bool foundFormat = false;
615         for (const VkSurfaceFormatKHR &surfaceFormat : surfaceFormats)
616         {
617             if (surfaceFormat.format == nativeFormat)
618             {
619                 foundFormat = true;
620                 break;
621             }
622         }
623 
624         // If a non-linear colorspace was requested but the non-linear format is
625         // not supported as a vulkan surface format, treat it as a non-fatal error
626         if (!foundFormat)
627         {
628             return angle::Result::Incomplete;
629         }
630     }
631 
632     mCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
633     if ((mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) == 0)
634     {
635         mCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
636     }
637     ANGLE_VK_CHECK(displayVk, (mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) != 0,
638                    VK_ERROR_INITIALIZATION_FAILED);
639 
640     ANGLE_TRY(createSwapChain(displayVk, extents, VK_NULL_HANDLE));
641 
642     VkResult vkResult = nextSwapchainImage(displayVk);
643     // VK_SUBOPTIMAL_KHR is ok since we still have an Image that can be presented successfully
644     if (ANGLE_UNLIKELY((vkResult != VK_SUCCESS) && (vkResult != VK_SUBOPTIMAL_KHR)))
645     {
646         ANGLE_VK_TRY(displayVk, vkResult);
647     }
648 
649     return angle::Result::Continue;
650 }
651 
recreateSwapchain(ContextVk * contextVk,const gl::Extents & extents,uint32_t swapHistoryIndex)652 angle::Result WindowSurfaceVk::recreateSwapchain(ContextVk *contextVk,
653                                                  const gl::Extents &extents,
654                                                  uint32_t swapHistoryIndex)
655 {
656     // If mOldSwapchains is not empty, it means that a new swapchain was created, but before
657     // any of its images were presented, it's asked to be recreated.  In this case, we can destroy
658     // the current swapchain immediately (although the old swapchains still need to be kept to be
659     // scheduled for destruction).  This can happen for example if vkQueuePresentKHR returns
660     // OUT_OF_DATE, the swapchain is recreated and the following vkAcquireNextImageKHR again
661     // returns OUT_OF_DATE.
662     //
663     // Otherwise, keep the current swapchain as the old swapchain to be scheduled for destruction
664     // and create a new one.
665 
666     VkSwapchainKHR swapchainToDestroy = VK_NULL_HANDLE;
667 
668     if (!mOldSwapchains.empty())
669     {
670         // Keep the old swapchain, destroy the current (never-used) swapchain.
671         swapchainToDestroy = mSwapchain;
672 
673         // Recycle present semaphores.
674         for (SwapchainImage &swapchainImage : mSwapchainImages)
675         {
676             for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
677             {
678                 ASSERT(presentHistory.semaphore.valid());
679                 ASSERT(presentHistory.oldSwapchains.empty());
680 
681                 mPresentSemaphoreRecycler.recycle(std::move(presentHistory.semaphore));
682             }
683         }
684     }
685     else
686     {
687         SwapchainCleanupData cleanupData;
688 
689         // Remember the current swapchain to be scheduled for destruction later.
690         cleanupData.swapchain = mSwapchain;
691 
692         // Accumulate the semaphores to be destroyed at the same time as the swapchain.
693         for (SwapchainImage &swapchainImage : mSwapchainImages)
694         {
695             for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
696             {
697                 ASSERT(presentHistory.semaphore.valid());
698                 cleanupData.semaphores.emplace_back(std::move(presentHistory.semaphore));
699 
700                 // Accumulate any previous swapchains that are pending destruction too.
701                 for (SwapchainCleanupData &oldSwapchain : presentHistory.oldSwapchains)
702                 {
703                     mOldSwapchains.emplace_back(std::move(oldSwapchain));
704                 }
705                 presentHistory.oldSwapchains.clear();
706             }
707         }
708 
709         // If too many old swapchains have accumulated, wait idle and destroy them.  This is to
710         // prevent failures due to too many swapchains allocated.
711         //
712         // Note: Nvidia has been observed to fail creation of swapchains after 20 are allocated on
713         // desktop, or less than 10 on Quadro P400.
714         static constexpr size_t kMaxOldSwapchains = 5;
715         if (mOldSwapchains.size() > kMaxOldSwapchains)
716         {
717             ANGLE_TRY(contextVk->getRenderer()->queueWaitIdle(contextVk, contextVk->getPriority()));
718             for (SwapchainCleanupData &oldSwapchain : mOldSwapchains)
719             {
720                 oldSwapchain.destroy(contextVk->getDevice(), &mPresentSemaphoreRecycler);
721             }
722             mOldSwapchains.clear();
723         }
724 
725         mOldSwapchains.emplace_back(std::move(cleanupData));
726     }
727 
728     // Recreate the swapchain based on the most recent one.
729     VkSwapchainKHR lastSwapchain = mSwapchain;
730     mSwapchain                   = VK_NULL_HANDLE;
731 
732     releaseSwapchainImages(contextVk);
733 
734     angle::Result result = createSwapChain(contextVk, extents, lastSwapchain);
735 
736     // If the most recent swapchain was never used, destroy it right now.
737     if (swapchainToDestroy)
738     {
739         vkDestroySwapchainKHR(contextVk->getDevice(), swapchainToDestroy, nullptr);
740     }
741 
742     return result;
743 }
744 
newPresentSemaphore(vk::Context * context,vk::Semaphore * semaphoreOut)745 angle::Result WindowSurfaceVk::newPresentSemaphore(vk::Context *context,
746                                                    vk::Semaphore *semaphoreOut)
747 {
748     if (mPresentSemaphoreRecycler.empty())
749     {
750         ANGLE_VK_TRY(context, semaphoreOut->init(context->getDevice()));
751     }
752     else
753     {
754         mPresentSemaphoreRecycler.fetch(semaphoreOut);
755     }
756     return angle::Result::Continue;
757 }
758 
MapEglColorSpaceToVkColorSpace(EGLenum EGLColorspace)759 static VkColorSpaceKHR MapEglColorSpaceToVkColorSpace(EGLenum EGLColorspace)
760 {
761     switch (EGLColorspace)
762     {
763         case EGL_NONE:
764         case EGL_GL_COLORSPACE_LINEAR:
765         case EGL_GL_COLORSPACE_SRGB_KHR:
766         case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
767             return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
768         case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
769             return VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
770         case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
771             return VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT;
772         case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
773             return VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
774         case EGL_GL_COLORSPACE_SCRGB_EXT:
775             return VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
776         default:
777             UNREACHABLE();
778             return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
779     }
780 }
781 
resizeSwapchainImages(vk::Context * context,uint32_t imageCount)782 angle::Result WindowSurfaceVk::resizeSwapchainImages(vk::Context *context, uint32_t imageCount)
783 {
784     mSwapchainImages.resize(imageCount);
785 
786     // At this point, if there was a previous swapchain, the previous present semaphores have all
787     // been moved to mOldSwapchains to be scheduled for destruction, so all semaphore handles in
788     // mSwapchainImages should be invalid.
789     for (SwapchainImage &swapchainImage : mSwapchainImages)
790     {
791         for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
792         {
793             ASSERT(!presentHistory.semaphore.valid());
794             ANGLE_TRY(newPresentSemaphore(context, &presentHistory.semaphore));
795         }
796     }
797 
798     return angle::Result::Continue;
799 }
800 
createSwapChain(vk::Context * context,const gl::Extents & extents,VkSwapchainKHR lastSwapchain)801 angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
802                                                const gl::Extents &extents,
803                                                VkSwapchainKHR lastSwapchain)
804 {
805     ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::createSwapchain");
806 
807     ASSERT(mSwapchain == VK_NULL_HANDLE);
808 
809     RendererVk *renderer = context->getRenderer();
810     VkDevice device      = renderer->getDevice();
811 
812     const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
813     VkFormat nativeFormat    = format.vkImageFormat;
814 
815     gl::Extents rotatedExtents = extents;
816     if (Is90DegreeRotation(mPreTransform))
817     {
818         // The Surface is oriented such that its aspect ratio no longer matches that of the
819         // device.  In this case, the width and height of the swapchain images must be swapped to
820         // match the device's native orientation.  This must also be done for other attachments
821         // used with the swapchain (e.g. depth buffer).  The width and height of the viewport,
822         // scissor, and render-pass render area must also be swapped.  Then, when ANGLE rotates
823         // gl_Position in the vertex shader, the rendering will look the same as if no
824         // pre-rotation had been done.
825         std::swap(rotatedExtents.width, rotatedExtents.height);
826     }
827 
828     // We need transfer src for reading back from the backbuffer.
829     VkImageUsageFlags imageUsageFlags = kSurfaceVKColorImageUsageFlags;
830 
831     // We need storage image for compute writes (debug overlay output).
832     VkFormatFeatureFlags featureBits =
833         renderer->getImageFormatFeatureBits(nativeFormat, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
834     if ((featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0)
835     {
836         imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
837     }
838 
839     VkSwapchainCreateInfoKHR swapchainInfo = {};
840     swapchainInfo.sType                    = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
841     swapchainInfo.flags                    = 0;
842     swapchainInfo.surface                  = mSurface;
843     swapchainInfo.minImageCount            = mMinImageCount;
844     swapchainInfo.imageFormat              = nativeFormat;
845     swapchainInfo.imageColorSpace          = MapEglColorSpaceToVkColorSpace(
846         static_cast<EGLenum>(mState.attributes.get(EGL_GL_COLORSPACE, EGL_NONE)));
847     // Note: Vulkan doesn't allow 0-width/height swapchains.
848     swapchainInfo.imageExtent.width     = std::max(rotatedExtents.width, 1);
849     swapchainInfo.imageExtent.height    = std::max(rotatedExtents.height, 1);
850     swapchainInfo.imageArrayLayers      = 1;
851     swapchainInfo.imageUsage            = imageUsageFlags;
852     swapchainInfo.imageSharingMode      = VK_SHARING_MODE_EXCLUSIVE;
853     swapchainInfo.queueFamilyIndexCount = 0;
854     swapchainInfo.pQueueFamilyIndices   = nullptr;
855     swapchainInfo.preTransform          = mPreTransform;
856     swapchainInfo.compositeAlpha        = mCompositeAlpha;
857     swapchainInfo.presentMode           = mDesiredSwapchainPresentMode;
858     swapchainInfo.clipped               = VK_TRUE;
859     swapchainInfo.oldSwapchain          = lastSwapchain;
860 
861     // TODO(syoussefi): Once EGL_SWAP_BEHAVIOR_PRESERVED_BIT is supported, the contents of the old
862     // swapchain need to carry over to the new one.  http://anglebug.com/2942
863     ANGLE_VK_TRY(context, vkCreateSwapchainKHR(device, &swapchainInfo, nullptr, &mSwapchain));
864     mSwapchainPresentMode = mDesiredSwapchainPresentMode;
865 
866     // Intialize the swapchain image views.
867     uint32_t imageCount = 0;
868     ANGLE_VK_TRY(context, vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, nullptr));
869 
870     std::vector<VkImage> swapchainImages(imageCount);
871     ANGLE_VK_TRY(context,
872                  vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data()));
873 
874     // If multisampling is enabled, create a multisampled image which gets resolved just prior to
875     // present.
876     GLint samples = GetSampleCount(mState.config);
877     ANGLE_VK_CHECK(context, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
878 
879     VkExtent3D vkExtents;
880     gl_vk::GetExtent(rotatedExtents, &vkExtents);
881 
882     if (samples > 1)
883     {
884         const VkImageUsageFlags usage = kSurfaceVKColorImageUsageFlags;
885 
886         ANGLE_TRY(mColorImageMS.init(context, gl::TextureType::_2D, vkExtents, format, samples,
887                                      usage, 0, 0, 1, 1));
888         ANGLE_TRY(mColorImageMS.initMemory(context, renderer->getMemoryProperties(),
889                                            VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
890 
891         // Initialize the color render target with the multisampled targets.  If not multisampled,
892         // the render target will be updated to refer to a swapchain image on every acquire.
893         mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, 0, 0);
894     }
895 
896     ANGLE_TRY(resizeSwapchainImages(context, imageCount));
897 
898     for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex)
899     {
900         SwapchainImage &member = mSwapchainImages[imageIndex];
901         member.image.init2DWeakReference(context, swapchainImages[imageIndex], extents, format, 1);
902     }
903 
904     // Initialize depth/stencil if requested.
905     if (mState.config->depthStencilFormat != GL_NONE)
906     {
907         const vk::Format &dsFormat = renderer->getFormat(mState.config->depthStencilFormat);
908 
909         const VkImageUsageFlags dsUsage = kSurfaceVKDepthStencilImageUsageFlags;
910 
911         ANGLE_TRY(mDepthStencilImage.init(context, gl::TextureType::_2D, vkExtents, dsFormat,
912                                           samples, dsUsage, 0, 0, 1, 1));
913         ANGLE_TRY(mDepthStencilImage.initMemory(context, renderer->getMemoryProperties(),
914                                                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
915 
916         mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, 0, 0);
917 
918         // We will need to pass depth/stencil image views to the RenderTargetVk in the future.
919     }
920 
921     return angle::Result::Continue;
922 }
923 
isMultiSampled() const924 bool WindowSurfaceVk::isMultiSampled() const
925 {
926     return mColorImageMS.valid();
927 }
928 
checkForOutOfDateSwapchain(ContextVk * contextVk,uint32_t swapHistoryIndex,bool presentOutOfDate)929 angle::Result WindowSurfaceVk::checkForOutOfDateSwapchain(ContextVk *contextVk,
930                                                           uint32_t swapHistoryIndex,
931                                                           bool presentOutOfDate)
932 {
933     bool swapIntervalChanged = mSwapchainPresentMode != mDesiredSwapchainPresentMode;
934 
935     // If anything has changed, recreate the swapchain.
936     if (swapIntervalChanged || presentOutOfDate ||
937         contextVk->getRenderer()->getFeatures().perFrameWindowSizeQuery.enabled)
938     {
939         gl::Extents swapchainExtents(getWidth(), getHeight(), 1);
940 
941         gl::Extents currentExtents;
942         ANGLE_TRY(getCurrentWindowSize(contextVk, &currentExtents));
943 
944         // If window size has changed, check with surface capabilities.  It has been observed on
945         // Android that `getCurrentWindowSize()` returns 1920x1080 for example, while surface
946         // capabilities returns the size the surface was created with.
947         const VkPhysicalDevice &physicalDevice = contextVk->getRenderer()->getPhysicalDevice();
948         ANGLE_VK_TRY(contextVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface,
949                                                                           &mSurfaceCaps));
950         if (contextVk->getFeatures().enablePreRotateSurfaces.enabled)
951         {
952             // Update the surface's transform, which can change even if the window size does not.
953             mPreTransform = mSurfaceCaps.currentTransform;
954         }
955 
956         if (currentExtents != swapchainExtents)
957         {
958             uint32_t width  = mSurfaceCaps.currentExtent.width;
959             uint32_t height = mSurfaceCaps.currentExtent.height;
960 
961             if (width != 0xFFFFFFFFu)
962             {
963                 ASSERT(height != 0xFFFFFFFFu);
964                 currentExtents.width  = width;
965                 currentExtents.height = height;
966             }
967         }
968 
969         // Check for window resize and recreate swapchain if necessary.
970         // Work-around for some device which does not return OUT_OF_DATE after window resizing
971         if (swapIntervalChanged || presentOutOfDate || currentExtents != swapchainExtents)
972         {
973             ANGLE_TRY(recreateSwapchain(contextVk, currentExtents, swapHistoryIndex));
974         }
975     }
976 
977     return angle::Result::Continue;
978 }
979 
releaseSwapchainImages(ContextVk * contextVk)980 void WindowSurfaceVk::releaseSwapchainImages(ContextVk *contextVk)
981 {
982     RendererVk *renderer = contextVk->getRenderer();
983 
984     if (mDepthStencilImage.valid())
985     {
986         mDepthStencilImage.releaseImage(renderer);
987         mDepthStencilImage.releaseStagingBuffer(renderer);
988         mDepthStencilImageViews.release(renderer);
989     }
990 
991     if (mColorImageMS.valid())
992     {
993         mColorImageMS.releaseImage(renderer);
994         mColorImageMS.releaseStagingBuffer(renderer);
995         mColorImageMSViews.release(renderer);
996         contextVk->addGarbage(&mFramebufferMS);
997     }
998 
999     for (SwapchainImage &swapchainImage : mSwapchainImages)
1000     {
1001         // We don't own the swapchain image handles, so we just remove our reference to it.
1002         swapchainImage.image.resetImageWeakReference();
1003         swapchainImage.image.destroy(renderer);
1004 
1005         swapchainImage.imageViews.release(renderer);
1006         contextVk->addGarbage(&swapchainImage.framebuffer);
1007 
1008         // present history must have already been taken care of.
1009         for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
1010         {
1011             ASSERT(!presentHistory.semaphore.valid());
1012             ASSERT(presentHistory.oldSwapchains.empty());
1013         }
1014     }
1015 
1016     mSwapchainImages.clear();
1017 }
1018 
destroySwapChainImages(DisplayVk * displayVk)1019 void WindowSurfaceVk::destroySwapChainImages(DisplayVk *displayVk)
1020 {
1021     RendererVk *renderer = displayVk->getRenderer();
1022     VkDevice device      = displayVk->getDevice();
1023 
1024     mDepthStencilImage.destroy(renderer);
1025     mDepthStencilImageViews.destroy(device);
1026     mColorImageMS.destroy(renderer);
1027     mColorImageMSViews.destroy(device);
1028     mFramebufferMS.destroy(device);
1029 
1030     for (SwapchainImage &swapchainImage : mSwapchainImages)
1031     {
1032         // We don't own the swapchain image handles, so we just remove our reference to it.
1033         swapchainImage.image.resetImageWeakReference();
1034         swapchainImage.image.destroy(renderer);
1035         swapchainImage.imageViews.destroy(device);
1036         swapchainImage.framebuffer.destroy(device);
1037 
1038         for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
1039         {
1040             ASSERT(presentHistory.semaphore.valid());
1041 
1042             mPresentSemaphoreRecycler.recycle(std::move(presentHistory.semaphore));
1043             for (SwapchainCleanupData &oldSwapchain : presentHistory.oldSwapchains)
1044             {
1045                 oldSwapchain.destroy(device, &mPresentSemaphoreRecycler);
1046             }
1047             presentHistory.oldSwapchains.clear();
1048         }
1049     }
1050 
1051     mSwapchainImages.clear();
1052 }
1053 
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)1054 FramebufferImpl *WindowSurfaceVk::createDefaultFramebuffer(const gl::Context *context,
1055                                                            const gl::FramebufferState &state)
1056 {
1057     RendererVk *renderer = vk::GetImpl(context)->getRenderer();
1058     return FramebufferVk::CreateDefaultFBO(renderer, state, this);
1059 }
1060 
swapWithDamage(const gl::Context * context,EGLint * rects,EGLint n_rects)1061 egl::Error WindowSurfaceVk::swapWithDamage(const gl::Context *context,
1062                                            EGLint *rects,
1063                                            EGLint n_rects)
1064 {
1065     DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1066     angle::Result result = swapImpl(context, rects, n_rects, nullptr);
1067     return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
1068 }
1069 
swap(const gl::Context * context)1070 egl::Error WindowSurfaceVk::swap(const gl::Context *context)
1071 {
1072     DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1073     angle::Result result = swapImpl(context, nullptr, 0, nullptr);
1074     return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
1075 }
1076 
present(ContextVk * contextVk,EGLint * rects,EGLint n_rects,const void * pNextChain,bool * presentOutOfDate)1077 angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
1078                                        EGLint *rects,
1079                                        EGLint n_rects,
1080                                        const void *pNextChain,
1081                                        bool *presentOutOfDate)
1082 {
1083     ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present");
1084 
1085     // Throttle the submissions to avoid getting too far ahead of the GPU.
1086     SwapHistory &swap = mSwapHistory[mCurrentSwapHistoryIndex];
1087     {
1088         ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present: Throttle CPU");
1089         if (swap.sharedFence.isReferenced())
1090         {
1091             ANGLE_TRY(swap.waitFence(contextVk));
1092             swap.destroy(contextVk->getRenderer());
1093         }
1094     }
1095 
1096     SwapchainImage &image               = mSwapchainImages[mCurrentSwapchainImageIndex];
1097     vk::Framebuffer &currentFramebuffer = mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
1098     updateOverlay(contextVk);
1099     bool overlayHasWidget = overlayHasEnabledWidget(contextVk);
1100 
1101     // We can only do present related optimization if this is the last renderpass that touches the
1102     // swapchain image. MSAA resolve and overlay will insert another renderpass which disqualifies
1103     // the optimization.
1104     if (!mColorImageMS.valid() && !overlayHasWidget && currentFramebuffer.valid())
1105     {
1106         contextVk->optimizeRenderPassForPresent(currentFramebuffer.getHandle());
1107     }
1108 
1109     vk::CommandBuffer *commandBuffer = nullptr;
1110     ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
1111 
1112     if (mColorImageMS.valid())
1113     {
1114         // Transition the multisampled image to TRANSFER_SRC for resolve.
1115         ANGLE_TRY(contextVk->onImageRead(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferSrc,
1116                                          &mColorImageMS));
1117         ANGLE_TRY(contextVk->onImageWrite(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferDst,
1118                                           &image.image));
1119         ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
1120 
1121         VkImageResolve resolveRegion                = {};
1122         resolveRegion.srcSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;
1123         resolveRegion.srcSubresource.mipLevel       = 0;
1124         resolveRegion.srcSubresource.baseArrayLayer = 0;
1125         resolveRegion.srcSubresource.layerCount     = 1;
1126         resolveRegion.srcOffset                     = {};
1127         resolveRegion.dstSubresource                = resolveRegion.srcSubresource;
1128         resolveRegion.dstOffset                     = {};
1129         resolveRegion.extent                        = image.image.getExtents();
1130 
1131         mColorImageMS.resolve(&image.image, resolveRegion, commandBuffer);
1132     }
1133 
1134     if (overlayHasWidget)
1135     {
1136         ANGLE_TRY(drawOverlay(contextVk, &image));
1137     }
1138 
1139     // This does nothing if it already in the requested layout
1140     image.image.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::Present, commandBuffer);
1141 
1142     // Knowing that the kSwapHistorySize'th submission ago has finished, we can know that the
1143     // (kSwapHistorySize+1)'th present ago of this image is definitely finished and so its wait
1144     // semaphore can be reused.  See doc/PresentSemaphores.md for details.
1145     //
1146     // This also means the swapchain(s) scheduled to be deleted at the same time can be deleted.
1147     ImagePresentHistory &presentHistory = image.presentHistory[image.currentPresentHistoryIndex];
1148     vk::Semaphore *presentSemaphore     = &presentHistory.semaphore;
1149     ASSERT(presentSemaphore->valid());
1150 
1151     for (SwapchainCleanupData &oldSwapchain : presentHistory.oldSwapchains)
1152     {
1153         oldSwapchain.destroy(contextVk->getDevice(), &mPresentSemaphoreRecycler);
1154     }
1155     presentHistory.oldSwapchains.clear();
1156 
1157     // Schedule pending old swapchains to be destroyed at the same time the semaphore for this
1158     // present can be destroyed.
1159     presentHistory.oldSwapchains = std::move(mOldSwapchains);
1160 
1161     image.currentPresentHistoryIndex =
1162         (image.currentPresentHistoryIndex + 1) % image.presentHistory.size();
1163 
1164     ANGLE_TRY(contextVk->flushImpl(presentSemaphore));
1165 
1166     VkPresentInfoKHR presentInfo   = {};
1167     presentInfo.sType              = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1168     presentInfo.pNext              = pNextChain;
1169     presentInfo.waitSemaphoreCount = 1;
1170     presentInfo.pWaitSemaphores    = presentSemaphore->ptr();
1171     presentInfo.swapchainCount     = 1;
1172     presentInfo.pSwapchains        = &mSwapchain;
1173     presentInfo.pImageIndices      = &mCurrentSwapchainImageIndex;
1174     presentInfo.pResults           = nullptr;
1175 
1176     VkPresentRegionKHR presentRegion   = {};
1177     VkPresentRegionsKHR presentRegions = {};
1178     std::vector<VkRectLayerKHR> vkRects;
1179     if (contextVk->getFeatures().supportsIncrementalPresent.enabled && (n_rects > 0))
1180     {
1181         EGLint width  = getWidth();
1182         EGLint height = getHeight();
1183 
1184         EGLint *eglRects             = rects;
1185         presentRegion.rectangleCount = n_rects;
1186         vkRects.resize(n_rects);
1187         for (EGLint i = 0; i < n_rects; i++)
1188         {
1189             VkRectLayerKHR &rect = vkRects[i];
1190 
1191             // Make sure the damage rects are within swapchain bounds.
1192             rect.offset.x      = gl::clamp(*eglRects++, 0, width);
1193             rect.offset.y      = gl::clamp(*eglRects++, 0, height);
1194             rect.extent.width  = gl::clamp(*eglRects++, 0, width - rect.offset.x);
1195             rect.extent.height = gl::clamp(*eglRects++, 0, height - rect.offset.y);
1196             rect.layer         = 0;
1197         }
1198         presentRegion.pRectangles = vkRects.data();
1199 
1200         presentRegions.sType          = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
1201         presentRegions.pNext          = nullptr;
1202         presentRegions.swapchainCount = 1;
1203         presentRegions.pRegions       = &presentRegion;
1204 
1205         presentInfo.pNext = &presentRegions;
1206     }
1207 
1208     // Update the swap history for this presentation
1209     swap.sharedFence = contextVk->getLastSubmittedFence();
1210     ASSERT(!mAcquireImageSemaphore.valid());
1211 
1212     ++mCurrentSwapHistoryIndex;
1213     mCurrentSwapHistoryIndex =
1214         mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex;
1215 
1216     VkResult result = contextVk->getRenderer()->queuePresent(contextVk->getPriority(), presentInfo);
1217 
1218     // If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
1219     // continuing.
1220     // If VK_SUBOPTIMAL_KHR is returned it's because the device orientation changed and we should
1221     // recreate the swapchain with a new window orientation.
1222     if (contextVk->getFeatures().enablePreRotateSurfaces.enabled)
1223     {
1224         // Also check for VK_SUBOPTIMAL_KHR.
1225         *presentOutOfDate = ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR));
1226         if (!*presentOutOfDate)
1227         {
1228             ANGLE_VK_TRY(contextVk, result);
1229         }
1230     }
1231     else
1232     {
1233         // We aren't quite ready for that so just ignore for now.
1234         *presentOutOfDate = result == VK_ERROR_OUT_OF_DATE_KHR;
1235         if (!*presentOutOfDate && result != VK_SUBOPTIMAL_KHR)
1236         {
1237             ANGLE_VK_TRY(contextVk, result);
1238         }
1239     }
1240 
1241     return angle::Result::Continue;
1242 }
1243 
swapImpl(const gl::Context * context,EGLint * rects,EGLint n_rects,const void * pNextChain)1244 angle::Result WindowSurfaceVk::swapImpl(const gl::Context *context,
1245                                         EGLint *rects,
1246                                         EGLint n_rects,
1247                                         const void *pNextChain)
1248 {
1249     ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::swapImpl");
1250 
1251     ContextVk *contextVk = vk::GetImpl(context);
1252     DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1253 
1254     bool presentOutOfDate = false;
1255     // Save this now, since present() will increment the value.
1256     uint32_t currentSwapHistoryIndex = static_cast<uint32_t>(mCurrentSwapHistoryIndex);
1257 
1258     ANGLE_TRY(present(contextVk, rects, n_rects, pNextChain, &presentOutOfDate));
1259 
1260     ANGLE_TRY(checkForOutOfDateSwapchain(contextVk, currentSwapHistoryIndex, presentOutOfDate));
1261 
1262     {
1263         // Note: TRACE_EVENT0 is put here instead of inside the function to workaround this issue:
1264         // http://anglebug.com/2927
1265         ANGLE_TRACE_EVENT0("gpu.angle", "nextSwapchainImage");
1266         // Get the next available swapchain image.
1267 
1268         VkResult result = nextSwapchainImage(contextVk);
1269         // If SUBOPTIMAL/OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain
1270         // before continuing.
1271         if (ANGLE_UNLIKELY((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)))
1272         {
1273             ANGLE_TRY(checkForOutOfDateSwapchain(contextVk, currentSwapHistoryIndex, true));
1274             // Try one more time and bail if we fail
1275             result = nextSwapchainImage(contextVk);
1276         }
1277         ANGLE_VK_TRY(contextVk, result);
1278     }
1279 
1280     RendererVk *renderer = contextVk->getRenderer();
1281     ANGLE_TRY(renderer->syncPipelineCacheVk(displayVk));
1282 
1283     return angle::Result::Continue;
1284 }
1285 
nextSwapchainImage(vk::Context * context)1286 VkResult WindowSurfaceVk::nextSwapchainImage(vk::Context *context)
1287 {
1288     VkDevice device = context->getDevice();
1289 
1290     vk::DeviceScoped<vk::Semaphore> acquireImageSemaphore(device);
1291     VkResult result = acquireImageSemaphore.get().init(device);
1292     if (ANGLE_UNLIKELY(result != VK_SUCCESS))
1293     {
1294         return result;
1295     }
1296 
1297     result = vkAcquireNextImageKHR(device, mSwapchain, UINT64_MAX,
1298                                    acquireImageSemaphore.get().getHandle(), VK_NULL_HANDLE,
1299                                    &mCurrentSwapchainImageIndex);
1300     if (ANGLE_UNLIKELY(result != VK_SUCCESS))
1301     {
1302         return result;
1303     }
1304 
1305     // The semaphore will be waited on in the next flush.
1306     mAcquireImageSemaphore = acquireImageSemaphore.release();
1307 
1308     SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
1309 
1310     // Update RenderTarget pointers to this swapchain image if not multisampling.  Note: a possible
1311     // optimization is to defer the |vkAcquireNextImageKHR| call itself to |present()| if
1312     // multisampling, as the swapchain image is essentially unused until then.
1313     if (!mColorImageMS.valid())
1314     {
1315         mColorRenderTarget.updateSwapchainImage(&image.image, &image.imageViews);
1316     }
1317 
1318     return VK_SUCCESS;
1319 }
1320 
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)1321 egl::Error WindowSurfaceVk::postSubBuffer(const gl::Context *context,
1322                                           EGLint x,
1323                                           EGLint y,
1324                                           EGLint width,
1325                                           EGLint height)
1326 {
1327     // TODO(jmadill)
1328     return egl::NoError();
1329 }
1330 
querySurfacePointerANGLE(EGLint attribute,void ** value)1331 egl::Error WindowSurfaceVk::querySurfacePointerANGLE(EGLint attribute, void **value)
1332 {
1333     UNREACHABLE();
1334     return egl::EglBadCurrentSurface();
1335 }
1336 
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)1337 egl::Error WindowSurfaceVk::bindTexImage(const gl::Context *context,
1338                                          gl::Texture *texture,
1339                                          EGLint buffer)
1340 {
1341     return egl::NoError();
1342 }
1343 
releaseTexImage(const gl::Context * context,EGLint buffer)1344 egl::Error WindowSurfaceVk::releaseTexImage(const gl::Context *context, EGLint buffer)
1345 {
1346     return egl::NoError();
1347 }
1348 
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)1349 egl::Error WindowSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
1350                                           EGLuint64KHR * /*msc*/,
1351                                           EGLuint64KHR * /*sbc*/)
1352 {
1353     UNIMPLEMENTED();
1354     return egl::EglBadAccess();
1355 }
1356 
getMscRate(EGLint *,EGLint *)1357 egl::Error WindowSurfaceVk::getMscRate(EGLint * /*numerator*/, EGLint * /*denominator*/)
1358 {
1359     UNIMPLEMENTED();
1360     return egl::EglBadAccess();
1361 }
1362 
setSwapInterval(EGLint interval)1363 void WindowSurfaceVk::setSwapInterval(EGLint interval)
1364 {
1365     const EGLint minSwapInterval = mState.config->minSwapInterval;
1366     const EGLint maxSwapInterval = mState.config->maxSwapInterval;
1367     ASSERT(minSwapInterval == 0 || minSwapInterval == 1);
1368     ASSERT(maxSwapInterval == 0 || maxSwapInterval == 1);
1369 
1370     interval = gl::clamp(interval, minSwapInterval, maxSwapInterval);
1371 
1372     mDesiredSwapchainPresentMode = GetDesiredPresentMode(mPresentModes, interval);
1373 
1374     // - On mailbox, we need at least three images; one is being displayed to the user until the
1375     //   next v-sync, and the application alternatingly renders to the other two, one being
1376     //   recorded, and the other queued for presentation if v-sync happens in the meantime.
1377     // - On immediate, we need at least two images; the application alternates between the two
1378     //   images.
1379     // - On fifo, we use at least three images.  Triple-buffering allows us to present an image,
1380     //   have one in the queue, and record in another.  Note: on certain configurations (windows +
1381     //   nvidia + windowed mode), we could get away with a smaller number.
1382     //
1383     // For simplicity, we always allocate at least three images.
1384     mMinImageCount = std::max(3u, mSurfaceCaps.minImageCount);
1385 
1386     // Make sure we don't exceed maxImageCount.
1387     if (mSurfaceCaps.maxImageCount > 0 && mMinImageCount > mSurfaceCaps.maxImageCount)
1388     {
1389         mMinImageCount = mSurfaceCaps.maxImageCount;
1390     }
1391 
1392     // On the next swap, if the desired present mode is different from the current one, the
1393     // swapchain will be recreated.
1394 }
1395 
getWidth() const1396 EGLint WindowSurfaceVk::getWidth() const
1397 {
1398     return static_cast<EGLint>(mColorRenderTarget.getExtents().width);
1399 }
1400 
getHeight() const1401 EGLint WindowSurfaceVk::getHeight() const
1402 {
1403     return static_cast<EGLint>(mColorRenderTarget.getExtents().height);
1404 }
1405 
isPostSubBufferSupported() const1406 EGLint WindowSurfaceVk::isPostSubBufferSupported() const
1407 {
1408     // TODO(jmadill)
1409     return EGL_FALSE;
1410 }
1411 
getSwapBehavior() const1412 EGLint WindowSurfaceVk::getSwapBehavior() const
1413 {
1414     // TODO(jmadill)
1415     return EGL_BUFFER_DESTROYED;
1416 }
1417 
getCurrentFramebuffer(ContextVk * contextVk,const vk::RenderPass & compatibleRenderPass,vk::Framebuffer ** framebufferOut)1418 angle::Result WindowSurfaceVk::getCurrentFramebuffer(ContextVk *contextVk,
1419                                                      const vk::RenderPass &compatibleRenderPass,
1420                                                      vk::Framebuffer **framebufferOut)
1421 {
1422     vk::Framebuffer &currentFramebuffer =
1423         isMultiSampled() ? mFramebufferMS
1424                          : mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
1425 
1426     if (currentFramebuffer.valid())
1427     {
1428         // Validation layers should detect if the render pass is really compatible.
1429         *framebufferOut = &currentFramebuffer;
1430         return angle::Result::Continue;
1431     }
1432 
1433     VkFramebufferCreateInfo framebufferInfo = {};
1434 
1435     const gl::Extents extents             = mColorRenderTarget.getExtents();
1436     std::array<VkImageView, 2> imageViews = {};
1437 
1438     if (mDepthStencilImage.valid())
1439     {
1440         const vk::ImageView *imageView = nullptr;
1441         ANGLE_TRY(mDepthStencilRenderTarget.getImageView(contextVk, &imageView));
1442         imageViews[1] = imageView->getHandle();
1443     }
1444 
1445     framebufferInfo.sType           = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1446     framebufferInfo.flags           = 0;
1447     framebufferInfo.renderPass      = compatibleRenderPass.getHandle();
1448     framebufferInfo.attachmentCount = (mDepthStencilImage.valid() ? 2u : 1u);
1449     framebufferInfo.pAttachments    = imageViews.data();
1450     framebufferInfo.width           = static_cast<uint32_t>(extents.width);
1451     framebufferInfo.height          = static_cast<uint32_t>(extents.height);
1452     if (Is90DegreeRotation(mPreTransform))
1453     {
1454         std::swap(framebufferInfo.width, framebufferInfo.height);
1455     }
1456     framebufferInfo.layers = 1;
1457 
1458     if (isMultiSampled())
1459     {
1460         // If multisampled, there is only a single color image and framebuffer.
1461         const vk::ImageView *imageView = nullptr;
1462         ANGLE_TRY(mColorRenderTarget.getImageView(contextVk, &imageView));
1463         imageViews[0] = imageView->getHandle();
1464         ANGLE_VK_TRY(contextVk, mFramebufferMS.init(contextVk->getDevice(), framebufferInfo));
1465     }
1466     else
1467     {
1468         for (SwapchainImage &swapchainImage : mSwapchainImages)
1469         {
1470             const vk::ImageView *imageView = nullptr;
1471             ANGLE_TRY(swapchainImage.imageViews.getLevelLayerDrawImageView(
1472                 contextVk, swapchainImage.image, 0, 0, &imageView));
1473 
1474             imageViews[0] = imageView->getHandle();
1475             ANGLE_VK_TRY(contextVk,
1476                          swapchainImage.framebuffer.init(contextVk->getDevice(), framebufferInfo));
1477         }
1478     }
1479 
1480     ASSERT(currentFramebuffer.valid());
1481     *framebufferOut = &currentFramebuffer;
1482     return angle::Result::Continue;
1483 }
1484 
getAcquireImageSemaphore()1485 vk::Semaphore WindowSurfaceVk::getAcquireImageSemaphore()
1486 {
1487     return std::move(mAcquireImageSemaphore);
1488 }
1489 
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)1490 angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context,
1491                                                   const gl::ImageIndex &imageIndex)
1492 {
1493     ContextVk *contextVk = vk::GetImpl(context);
1494 
1495     ASSERT(mSwapchainImages.size() > 0);
1496     ASSERT(mCurrentSwapchainImageIndex < mSwapchainImages.size());
1497 
1498     vk::ImageHelper *image =
1499         isMultiSampled() ? &mColorImageMS : &mSwapchainImages[mCurrentSwapchainImageIndex].image;
1500     image->stageSubresourceClear(imageIndex);
1501     ANGLE_TRY(image->flushAllStagedUpdates(contextVk));
1502 
1503     if (mDepthStencilImage.valid())
1504     {
1505         mDepthStencilImage.stageSubresourceClear(gl::ImageIndex::Make2D(0));
1506         ANGLE_TRY(mDepthStencilImage.flushAllStagedUpdates(contextVk));
1507     }
1508 
1509     return angle::Result::Continue;
1510 }
1511 
updateOverlay(ContextVk * contextVk) const1512 void WindowSurfaceVk::updateOverlay(ContextVk *contextVk) const
1513 {
1514     const gl::OverlayType *overlay = contextVk->getOverlay();
1515     OverlayVk *overlayVk           = vk::GetImpl(overlay);
1516 
1517     // If overlay is disabled, nothing to do.
1518     if (overlayVk == nullptr)
1519     {
1520         return;
1521     }
1522 
1523     RendererVk *rendererVk = contextVk->getRenderer();
1524 
1525     uint32_t validationMessageCount = 0;
1526     std::string lastValidationMessage =
1527         rendererVk->getAndClearLastValidationMessage(&validationMessageCount);
1528     if (validationMessageCount)
1529     {
1530         overlay->getTextWidget(gl::WidgetId::VulkanLastValidationMessage)
1531             ->set(std::move(lastValidationMessage));
1532         overlay->getCountWidget(gl::WidgetId::VulkanValidationMessageCount)
1533             ->add(validationMessageCount);
1534     }
1535 }
1536 
overlayHasEnabledWidget(ContextVk * contextVk) const1537 ANGLE_INLINE bool WindowSurfaceVk::overlayHasEnabledWidget(ContextVk *contextVk) const
1538 {
1539     const gl::OverlayType *overlay = contextVk->getOverlay();
1540     OverlayVk *overlayVk           = vk::GetImpl(overlay);
1541     return overlayVk && overlayVk->getEnabledWidgetCount() > 0;
1542 }
1543 
drawOverlay(ContextVk * contextVk,SwapchainImage * image) const1544 angle::Result WindowSurfaceVk::drawOverlay(ContextVk *contextVk, SwapchainImage *image) const
1545 {
1546     const gl::OverlayType *overlay = contextVk->getOverlay();
1547     OverlayVk *overlayVk           = vk::GetImpl(overlay);
1548 
1549     // Draw overlay
1550     const vk::ImageView *imageView = nullptr;
1551     ANGLE_TRY(
1552         image->imageViews.getLevelLayerDrawImageView(contextVk, image->image, 0, 0, &imageView));
1553     ANGLE_TRY(overlayVk->onPresent(contextVk, &image->image, imageView));
1554 
1555     return angle::Result::Continue;
1556 }
1557 
1558 }  // namespace rx
1559