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, ¤tExtents));
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 ¤tFramebuffer = 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 ¤tFramebuffer =
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 = ¤tFramebuffer;
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 = ¤tFramebuffer;
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