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