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/driver_utils.h"
18 #include "libANGLE/renderer/vulkan/ContextVk.h"
19 #include "libANGLE/renderer/vulkan/DisplayVk.h"
20 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
21 #include "libANGLE/renderer/vulkan/OverlayVk.h"
22 #include "libANGLE/renderer/vulkan/RendererVk.h"
23 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
24 #include "libANGLE/trace.h"
25
26 namespace rx
27 {
28
29 namespace
30 {
31 angle::SubjectIndex kAnySurfaceImageSubjectIndex = 0;
32
33 // Special value for currentExtent if surface size is determined by the
34 // swapchain's extent. See VkSurfaceCapabilitiesKHR spec for more details.
35 constexpr uint32_t kSurfaceSizedBySwapchain = 0xFFFFFFFFu;
36
GetSampleCount(const egl::Config * config)37 GLint GetSampleCount(const egl::Config *config)
38 {
39 GLint samples = 1;
40 if (config->sampleBuffers && config->samples > 1)
41 {
42 samples = config->samples;
43 }
44 return samples;
45 }
46
GetDesiredPresentMode(const std::vector<VkPresentModeKHR> & presentModes,EGLint interval)47 VkPresentModeKHR GetDesiredPresentMode(const std::vector<VkPresentModeKHR> &presentModes,
48 EGLint interval)
49 {
50 ASSERT(!presentModes.empty());
51
52 // If v-sync is enabled, use FIFO, which throttles you to the display rate and is guaranteed to
53 // always be supported.
54 if (interval > 0)
55 {
56 return VK_PRESENT_MODE_FIFO_KHR;
57 }
58
59 // Otherwise, choose either of the following, if available, in order specified here:
60 //
61 // - Mailbox is similar to triple-buffering.
62 // - Immediate is similar to single-buffering.
63 //
64 // If neither is supported, we fallback to FIFO.
65
66 bool mailboxAvailable = false;
67 bool immediateAvailable = false;
68
69 for (VkPresentModeKHR presentMode : presentModes)
70 {
71 switch (presentMode)
72 {
73 case VK_PRESENT_MODE_MAILBOX_KHR:
74 mailboxAvailable = true;
75 break;
76 case VK_PRESENT_MODE_IMMEDIATE_KHR:
77 immediateAvailable = true;
78 break;
79 default:
80 break;
81 }
82 }
83
84 if (immediateAvailable)
85 {
86 return VK_PRESENT_MODE_IMMEDIATE_KHR;
87 }
88
89 if (mailboxAvailable)
90 {
91 return VK_PRESENT_MODE_MAILBOX_KHR;
92 }
93
94 // Note again that VK_PRESENT_MODE_FIFO_KHR is guaranteed to be available.
95 return VK_PRESENT_MODE_FIFO_KHR;
96 }
97
98 constexpr VkImageUsageFlags kSurfaceVkImageUsageFlags =
99 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
100 constexpr VkImageUsageFlags kSurfaceVkColorImageUsageFlags =
101 kSurfaceVkImageUsageFlags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
102 constexpr VkImageUsageFlags kSurfaceVkDepthStencilImageUsageFlags =
103 kSurfaceVkImageUsageFlags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
104
105 // If the device is rotated with any of the following transform flags, the swapchain width and
106 // height must be swapped (e.g. make a landscape window portrait). This must also be done for all
107 // attachments used with the swapchain (i.e. depth, stencil, and multisample buffers).
108 constexpr VkSurfaceTransformFlagsKHR k90DegreeRotationVariants =
109 VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
110 VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
111 VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
112
Is90DegreeRotation(VkSurfaceTransformFlagsKHR transform)113 bool Is90DegreeRotation(VkSurfaceTransformFlagsKHR transform)
114 {
115 return ((transform & k90DegreeRotationVariants) != 0);
116 }
117
InitImageHelper(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples,bool isRobustResourceInitEnabled,bool hasProtectedContent,vk::ImageHelper * imageHelper)118 angle::Result InitImageHelper(DisplayVk *displayVk,
119 EGLint width,
120 EGLint height,
121 const vk::Format &vkFormat,
122 GLint samples,
123 bool isRobustResourceInitEnabled,
124 bool hasProtectedContent,
125 vk::ImageHelper *imageHelper)
126 {
127 const angle::Format &textureFormat = vkFormat.actualImageFormat();
128 bool isDepthOrStencilFormat = textureFormat.depthBits > 0 || textureFormat.stencilBits > 0;
129 const VkImageUsageFlags usage = isDepthOrStencilFormat ? kSurfaceVkDepthStencilImageUsageFlags
130 : kSurfaceVkColorImageUsageFlags;
131
132 VkExtent3D extents = {std::max(static_cast<uint32_t>(width), 1u),
133 std::max(static_cast<uint32_t>(height), 1u), 1u};
134
135 VkImageCreateFlags imageCreateFlags =
136 hasProtectedContent ? VK_IMAGE_CREATE_PROTECTED_BIT : vk::kVkImageCreateFlagsNone;
137 ANGLE_TRY(imageHelper->initExternal(displayVk, gl::TextureType::_2D, extents, vkFormat, samples,
138 usage, imageCreateFlags, vk::ImageLayout::Undefined,
139 nullptr, gl::LevelIndex(0), 1, 1,
140 isRobustResourceInitEnabled, nullptr, hasProtectedContent));
141
142 return angle::Result::Continue;
143 }
144 } // namespace
145
146 #if defined(ANGLE_ENABLE_OVERLAY)
147 constexpr bool kEnableOverlay = ANGLE_ENABLE_OVERLAY;
148 #else
149 constexpr bool kEnableOverlay = false;
150 #endif
151
SurfaceVk(const egl::SurfaceState & surfaceState)152 SurfaceVk::SurfaceVk(const egl::SurfaceState &surfaceState) : SurfaceImpl(surfaceState) {}
153
154 SurfaceVk::~SurfaceVk() = default;
155
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)156 angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
157 GLenum binding,
158 const gl::ImageIndex &imageIndex,
159 GLsizei samples,
160 FramebufferAttachmentRenderTarget **rtOut)
161 {
162 ASSERT(samples == 0);
163
164 if (binding == GL_BACK)
165 {
166 *rtOut = &mColorRenderTarget;
167 }
168 else
169 {
170 ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
171 *rtOut = &mDepthStencilRenderTarget;
172 }
173
174 return angle::Result::Continue;
175 }
176
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)177 void SurfaceVk::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
178 {
179 // Forward the notification to parent class that the staging buffer changed.
180 onStateChange(angle::SubjectMessage::SubjectChanged);
181 }
182
AttachmentImage(SurfaceVk * surfaceVk)183 OffscreenSurfaceVk::AttachmentImage::AttachmentImage(SurfaceVk *surfaceVk)
184 : imageObserverBinding(surfaceVk, kAnySurfaceImageSubjectIndex)
185 {
186 imageObserverBinding.bind(&image);
187 }
188
189 OffscreenSurfaceVk::AttachmentImage::~AttachmentImage() = default;
190
initialize(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples,bool isRobustResourceInitEnabled,bool hasProtectedContent)191 angle::Result OffscreenSurfaceVk::AttachmentImage::initialize(DisplayVk *displayVk,
192 EGLint width,
193 EGLint height,
194 const vk::Format &vkFormat,
195 GLint samples,
196 bool isRobustResourceInitEnabled,
197 bool hasProtectedContent)
198 {
199 ANGLE_TRY(InitImageHelper(displayVk, width, height, vkFormat, samples,
200 isRobustResourceInitEnabled, hasProtectedContent, &image));
201
202 RendererVk *renderer = displayVk->getRenderer();
203 VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
204 if (hasProtectedContent)
205 {
206 flags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
207 }
208 ANGLE_TRY(
209 image.initMemory(displayVk, hasProtectedContent, renderer->getMemoryProperties(), flags));
210
211 imageViews.init(renderer);
212
213 return angle::Result::Continue;
214 }
215
initializeWithExternalMemory(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples,void * buffer,bool isRobustResourceInitEnabled,bool hasProtectedContent)216 angle::Result OffscreenSurfaceVk::AttachmentImage::initializeWithExternalMemory(
217 DisplayVk *displayVk,
218 EGLint width,
219 EGLint height,
220 const vk::Format &vkFormat,
221 GLint samples,
222 void *buffer,
223 bool isRobustResourceInitEnabled,
224 bool hasProtectedContent)
225 {
226 RendererVk *renderer = displayVk->getRenderer();
227 ASSERT(renderer->getFeatures().supportsExternalMemoryHost.enabled);
228
229 ANGLE_TRY(InitImageHelper(displayVk, width, height, vkFormat, samples,
230 isRobustResourceInitEnabled, hasProtectedContent, &image));
231
232 VkImportMemoryHostPointerInfoEXT importMemoryHostPointerInfo = {};
233 importMemoryHostPointerInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT;
234 importMemoryHostPointerInfo.pNext = nullptr;
235 importMemoryHostPointerInfo.handleType =
236 VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT;
237 importMemoryHostPointerInfo.pHostPointer = buffer;
238
239 VkMemoryRequirements externalMemoryRequirements;
240 image.getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements);
241
242 VkMemoryPropertyFlags flags = 0;
243 if (hasProtectedContent)
244 {
245 flags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
246 }
247 ANGLE_TRY(image.initExternalMemory(
248 displayVk, renderer->getMemoryProperties(), externalMemoryRequirements, nullptr,
249 &importMemoryHostPointerInfo, VK_QUEUE_FAMILY_EXTERNAL, flags));
250
251 imageViews.init(renderer);
252
253 return angle::Result::Continue;
254 }
255
destroy(const egl::Display * display)256 void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
257 {
258 DisplayVk *displayVk = vk::GetImpl(display);
259 RendererVk *renderer = displayVk->getRenderer();
260 // Front end must ensure all usage has been submitted.
261 image.releaseImage(renderer);
262 image.releaseStagingBuffer(renderer);
263 imageViews.release(renderer);
264 }
265
OffscreenSurfaceVk(const egl::SurfaceState & surfaceState,RendererVk * renderer)266 OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState, RendererVk *renderer)
267 : SurfaceVk(surfaceState),
268 mWidth(mState.attributes.getAsInt(EGL_WIDTH, 0)),
269 mHeight(mState.attributes.getAsInt(EGL_HEIGHT, 0)),
270 mColorAttachment(this),
271 mDepthStencilAttachment(this)
272 {
273 mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr, nullptr,
274 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
275 mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
276 &mDepthStencilAttachment.imageViews, nullptr, nullptr,
277 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
278 }
279
~OffscreenSurfaceVk()280 OffscreenSurfaceVk::~OffscreenSurfaceVk() {}
281
initialize(const egl::Display * display)282 egl::Error OffscreenSurfaceVk::initialize(const egl::Display *display)
283 {
284 DisplayVk *displayVk = vk::GetImpl(display);
285 angle::Result result = initializeImpl(displayVk);
286 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
287 }
288
initializeImpl(DisplayVk * displayVk)289 angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
290 {
291 RendererVk *renderer = displayVk->getRenderer();
292 const egl::Config *config = mState.config;
293
294 renderer->reloadVolkIfNeeded();
295
296 GLint samples = GetSampleCount(mState.config);
297 ANGLE_VK_CHECK(displayVk, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
298
299 bool robustInit = mState.isRobustResourceInitEnabled();
300
301 if (config->renderTargetFormat != GL_NONE)
302 {
303 ANGLE_TRY(mColorAttachment.initialize(displayVk, mWidth, mHeight,
304 renderer->getFormat(config->renderTargetFormat),
305 samples, robustInit, mState.hasProtectedContent()));
306 mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr,
307 nullptr, gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
308 }
309
310 if (config->depthStencilFormat != GL_NONE)
311 {
312 ANGLE_TRY(mDepthStencilAttachment.initialize(
313 displayVk, mWidth, mHeight, renderer->getFormat(config->depthStencilFormat), samples,
314 robustInit, mState.hasProtectedContent()));
315 mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
316 &mDepthStencilAttachment.imageViews, nullptr, nullptr,
317 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
318 }
319
320 return angle::Result::Continue;
321 }
322
destroy(const egl::Display * display)323 void OffscreenSurfaceVk::destroy(const egl::Display *display)
324 {
325 mColorAttachment.destroy(display);
326 mDepthStencilAttachment.destroy(display);
327 }
328
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)329 FramebufferImpl *OffscreenSurfaceVk::createDefaultFramebuffer(const gl::Context *context,
330 const gl::FramebufferState &state)
331 {
332 RendererVk *renderer = vk::GetImpl(context)->getRenderer();
333
334 // Use a user FBO for an offscreen RT.
335 return FramebufferVk::CreateUserFBO(renderer, state);
336 }
337
swap(const gl::Context * context)338 egl::Error OffscreenSurfaceVk::swap(const gl::Context *context)
339 {
340 return egl::NoError();
341 }
342
postSubBuffer(const gl::Context *,EGLint,EGLint,EGLint,EGLint)343 egl::Error OffscreenSurfaceVk::postSubBuffer(const gl::Context * /*context*/,
344 EGLint /*x*/,
345 EGLint /*y*/,
346 EGLint /*width*/,
347 EGLint /*height*/)
348 {
349 return egl::NoError();
350 }
351
querySurfacePointerANGLE(EGLint,void **)352 egl::Error OffscreenSurfaceVk::querySurfacePointerANGLE(EGLint /*attribute*/, void ** /*value*/)
353 {
354 UNREACHABLE();
355 return egl::EglBadCurrentSurface();
356 }
357
bindTexImage(const gl::Context *,gl::Texture *,EGLint)358 egl::Error OffscreenSurfaceVk::bindTexImage(const gl::Context * /*context*/,
359 gl::Texture * /*texture*/,
360 EGLint /*buffer*/)
361 {
362 return egl::NoError();
363 }
364
releaseTexImage(const gl::Context *,EGLint)365 egl::Error OffscreenSurfaceVk::releaseTexImage(const gl::Context * /*context*/, EGLint /*buffer*/)
366 {
367 return egl::NoError();
368 }
369
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)370 egl::Error OffscreenSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
371 EGLuint64KHR * /*msc*/,
372 EGLuint64KHR * /*sbc*/)
373 {
374 UNIMPLEMENTED();
375 return egl::EglBadAccess();
376 }
377
getMscRate(EGLint *,EGLint *)378 egl::Error OffscreenSurfaceVk::getMscRate(EGLint * /*numerator*/, EGLint * /*denominator*/)
379 {
380 UNIMPLEMENTED();
381 return egl::EglBadAccess();
382 }
383
setSwapInterval(EGLint)384 void OffscreenSurfaceVk::setSwapInterval(EGLint /*interval*/) {}
385
getWidth() const386 EGLint OffscreenSurfaceVk::getWidth() const
387 {
388 return mWidth;
389 }
390
getHeight() const391 EGLint OffscreenSurfaceVk::getHeight() const
392 {
393 return mHeight;
394 }
395
isPostSubBufferSupported() const396 EGLint OffscreenSurfaceVk::isPostSubBufferSupported() const
397 {
398 return EGL_FALSE;
399 }
400
getSwapBehavior() const401 EGLint OffscreenSurfaceVk::getSwapBehavior() const
402 {
403 return EGL_BUFFER_DESTROYED;
404 }
405
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)406 angle::Result OffscreenSurfaceVk::initializeContents(const gl::Context *context,
407 const gl::ImageIndex &imageIndex)
408 {
409 ContextVk *contextVk = vk::GetImpl(context);
410
411 if (mColorAttachment.image.valid())
412 {
413 mColorAttachment.image.stageRobustResourceClear(imageIndex);
414 ANGLE_TRY(mColorAttachment.image.flushAllStagedUpdates(contextVk));
415 }
416
417 if (mDepthStencilAttachment.image.valid())
418 {
419 mDepthStencilAttachment.image.stageRobustResourceClear(imageIndex);
420 ANGLE_TRY(mDepthStencilAttachment.image.flushAllStagedUpdates(contextVk));
421 }
422 return angle::Result::Continue;
423 }
424
getColorAttachmentImage()425 vk::ImageHelper *OffscreenSurfaceVk::getColorAttachmentImage()
426 {
427 return &mColorAttachment.image;
428 }
429
430 namespace impl
431 {
432 SwapchainCleanupData::SwapchainCleanupData() = default;
~SwapchainCleanupData()433 SwapchainCleanupData::~SwapchainCleanupData()
434 {
435 ASSERT(swapchain == VK_NULL_HANDLE);
436 ASSERT(semaphores.empty());
437 }
438
SwapchainCleanupData(SwapchainCleanupData && other)439 SwapchainCleanupData::SwapchainCleanupData(SwapchainCleanupData &&other)
440 : swapchain(other.swapchain), semaphores(std::move(other.semaphores))
441 {
442 other.swapchain = VK_NULL_HANDLE;
443 }
444
destroy(VkDevice device,vk::Recycler<vk::Semaphore> * semaphoreRecycler)445 void SwapchainCleanupData::destroy(VkDevice device, vk::Recycler<vk::Semaphore> *semaphoreRecycler)
446 {
447 if (swapchain)
448 {
449 vkDestroySwapchainKHR(device, swapchain, nullptr);
450 swapchain = VK_NULL_HANDLE;
451 }
452
453 for (vk::Semaphore &semaphore : semaphores)
454 {
455 semaphoreRecycler->recycle(std::move(semaphore));
456 }
457 semaphores.clear();
458 }
459
460 ImagePresentHistory::ImagePresentHistory() = default;
~ImagePresentHistory()461 ImagePresentHistory::~ImagePresentHistory()
462 {
463 ASSERT(!semaphore.valid());
464 ASSERT(oldSwapchains.empty());
465 }
466
ImagePresentHistory(ImagePresentHistory && other)467 ImagePresentHistory::ImagePresentHistory(ImagePresentHistory &&other)
468 : semaphore(std::move(other.semaphore)), oldSwapchains(std::move(other.oldSwapchains))
469 {}
470
471 SwapchainImage::SwapchainImage() = default;
472 SwapchainImage::~SwapchainImage() = default;
473
SwapchainImage(SwapchainImage && other)474 SwapchainImage::SwapchainImage(SwapchainImage &&other)
475 : image(std::move(other.image)),
476 imageViews(std::move(other.imageViews)),
477 framebuffer(std::move(other.framebuffer)),
478 presentHistory(std::move(other.presentHistory)),
479 currentPresentHistoryIndex(other.currentPresentHistoryIndex)
480 {}
481 } // namespace impl
482
483 using namespace impl;
484
WindowSurfaceVk(const egl::SurfaceState & surfaceState,EGLNativeWindowType window)485 WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativeWindowType window)
486 : SurfaceVk(surfaceState),
487 mNativeWindowType(window),
488 mSurface(VK_NULL_HANDLE),
489 mSupportsProtectedSwapchain(false),
490 mSwapchain(VK_NULL_HANDLE),
491 mSwapchainPresentMode(VK_PRESENT_MODE_FIFO_KHR),
492 mDesiredSwapchainPresentMode(VK_PRESENT_MODE_FIFO_KHR),
493 mMinImageCount(0),
494 mPreTransform(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR),
495 mEmulatedPreTransform(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR),
496 mCompositeAlpha(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR),
497 mCurrentSwapHistoryIndex(0),
498 mCurrentSwapchainImageIndex(0),
499 mDepthStencilImageBinding(this, kAnySurfaceImageSubjectIndex),
500 mColorImageMSBinding(this, kAnySurfaceImageSubjectIndex),
501 mNeedToAcquireNextSwapchainImage(false),
502 mFrameCount(1)
503 {
504 // Initialize the color render target with the multisampled targets. If not multisampled, the
505 // render target will be updated to refer to a swapchain image on every acquire.
506 mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, nullptr, nullptr,
507 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
508 mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, nullptr, nullptr,
509 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
510 mDepthStencilImageBinding.bind(&mDepthStencilImage);
511 mColorImageMSBinding.bind(&mColorImageMS);
512 }
513
~WindowSurfaceVk()514 WindowSurfaceVk::~WindowSurfaceVk()
515 {
516 ASSERT(mSurface == VK_NULL_HANDLE);
517 ASSERT(mSwapchain == VK_NULL_HANDLE);
518 }
519
destroy(const egl::Display * display)520 void WindowSurfaceVk::destroy(const egl::Display *display)
521 {
522 DisplayVk *displayVk = vk::GetImpl(display);
523 RendererVk *renderer = displayVk->getRenderer();
524 VkDevice device = renderer->getDevice();
525 VkInstance instance = renderer->getInstance();
526
527 // flush the pipe.
528 (void)renderer->finish(displayVk, mState.hasProtectedContent());
529
530 destroySwapChainImages(displayVk);
531
532 if (mSwapchain)
533 {
534 vkDestroySwapchainKHR(device, mSwapchain, nullptr);
535 mSwapchain = VK_NULL_HANDLE;
536 }
537
538 for (SwapchainCleanupData &oldSwapchain : mOldSwapchains)
539 {
540 oldSwapchain.destroy(device, &mPresentSemaphoreRecycler);
541 }
542 mOldSwapchains.clear();
543
544 if (mSurface)
545 {
546 vkDestroySurfaceKHR(instance, mSurface, nullptr);
547 mSurface = VK_NULL_HANDLE;
548 }
549
550 mAcquireImageSemaphore.destroy(device);
551 mPresentSemaphoreRecycler.destroy(device);
552 }
553
initialize(const egl::Display * display)554 egl::Error WindowSurfaceVk::initialize(const egl::Display *display)
555 {
556 DisplayVk *displayVk = vk::GetImpl(display);
557 angle::Result result = initializeImpl(displayVk);
558 if (result == angle::Result::Incomplete)
559 {
560 return angle::ToEGL(result, displayVk, EGL_BAD_MATCH);
561 }
562 else
563 {
564 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
565 }
566 }
567
initializeImpl(DisplayVk * displayVk)568 angle::Result WindowSurfaceVk::initializeImpl(DisplayVk *displayVk)
569 {
570 RendererVk *renderer = displayVk->getRenderer();
571
572 mColorImageMSViews.init(renderer);
573 mDepthStencilImageViews.init(renderer);
574
575 renderer->reloadVolkIfNeeded();
576
577 gl::Extents windowSize;
578 ANGLE_TRY(createSurfaceVk(displayVk, &windowSize));
579
580 uint32_t presentQueue = 0;
581 ANGLE_TRY(renderer->selectPresentQueueForSurface(displayVk, mSurface, &presentQueue));
582 ANGLE_UNUSED_VARIABLE(presentQueue);
583
584 const VkPhysicalDevice &physicalDevice = renderer->getPhysicalDevice();
585
586 if (renderer->getFeatures().supportsSurfaceCapabilities2Extension.enabled)
587 {
588 VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo2 = {};
589 surfaceInfo2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
590 surfaceInfo2.pNext = nullptr;
591 surfaceInfo2.surface = mSurface;
592
593 VkSurfaceCapabilities2KHR surfaceCaps2 = {};
594 surfaceCaps2.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
595 surfaceCaps2.pNext = nullptr;
596
597 VkSurfaceProtectedCapabilitiesKHR surfaceProtectedCaps = {};
598 if (renderer->getFeatures().supportsSurfaceProtectedCapabilitiesExtension.enabled)
599 {
600 surfaceProtectedCaps.sType = VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR;
601 surfaceProtectedCaps.pNext = nullptr;
602
603 surfaceCaps2.pNext = &surfaceProtectedCaps;
604 }
605
606 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceCapabilities2KHR(
607 physicalDevice, &surfaceInfo2, &surfaceCaps2));
608
609 mSurfaceCaps = surfaceCaps2.surfaceCapabilities;
610 mSupportsProtectedSwapchain = surfaceProtectedCaps.supportsProtected;
611 }
612 else
613 {
614 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface,
615 &mSurfaceCaps));
616 }
617
618 if (IsAndroid())
619 {
620 mSupportsProtectedSwapchain = true;
621 }
622
623 ANGLE_VK_CHECK(displayVk, (mState.hasProtectedContent() ? mSupportsProtectedSwapchain : true),
624 VK_ERROR_FEATURE_NOT_PRESENT);
625
626 // Adjust width and height to the swapchain if necessary.
627 uint32_t width = mSurfaceCaps.currentExtent.width;
628 uint32_t height = mSurfaceCaps.currentExtent.height;
629
630 // TODO(jmadill): Support devices which don't support copy. We use this for ReadPixels.
631 ANGLE_VK_CHECK(displayVk,
632 (mSurfaceCaps.supportedUsageFlags & kSurfaceVkColorImageUsageFlags) ==
633 kSurfaceVkColorImageUsageFlags,
634 VK_ERROR_INITIALIZATION_FAILED);
635
636 EGLAttrib attribWidth = mState.attributes.get(EGL_WIDTH, 0);
637 EGLAttrib attribHeight = mState.attributes.get(EGL_HEIGHT, 0);
638
639 if (mSurfaceCaps.currentExtent.width == kSurfaceSizedBySwapchain)
640 {
641 ASSERT(mSurfaceCaps.currentExtent.height == kSurfaceSizedBySwapchain);
642
643 width = (attribWidth != 0) ? static_cast<uint32_t>(attribWidth) : windowSize.width;
644 height = (attribHeight != 0) ? static_cast<uint32_t>(attribHeight) : windowSize.height;
645 }
646
647 gl::Extents extents(static_cast<int>(width), static_cast<int>(height), 1);
648
649 // Introduction to Android rotation and pre-rotation:
650 //
651 // Android devices have one native orientation, but a window may be displayed in a different
652 // orientation. This results in the window being "rotated" relative to the native orientation.
653 // For example, the native orientation of a Pixel 4 is portrait (i.e. height > width).
654 // However, many games want to be landscape (i.e. width > height). Some applications will
655 // adapt to whatever orientation the user places the device in (e.g. auto-rotation).
656 //
657 // A convention is used within ANGLE of referring to the "rotated" and "non-rotated" aspects of
658 // a topic (e.g. a window's extents, a scissor, a viewport):
659 //
660 // - Non-rotated. This refers to the way that the application views the window. Rotation is
661 // an Android concept, not a GL concept. An application may view its window as landscape or
662 // portrait, but not necessarily view its window as being rotated. For example, an
663 // application will set a scissor and viewport in a manner consistent with its view of the
664 // window size (i.e. a non-rotated manner).
665 //
666 // - Rotated. This refers to the way that Vulkan views the window. If the window's
667 // orientation is the same as the native orientation, the rotated view will happen to be
668 // equivalent to the non-rotated view, but regardless of the window's orientation, ANGLE uses
669 // the "rotated" term as whatever the Vulkan view of the window is.
670 //
671 // Most of ANGLE is designed to work with the non-rotated view of the window. This is
672 // certainly true of the ANGLE front-end. It is also true of most of the Vulkan back-end,
673 // which is still translating GL to Vulkan. Only part of the Vulkan back-end needs to
674 // communicate directly to Vulkan in terms of the window's rotation. For example, the viewport
675 // and scissor calculations are done with non-rotated values; and then the final values are
676 // rotated.
677 //
678 // ANGLE learns about the window's rotation from mSurfaceCaps.currentTransform. If
679 // currentTransform is non-IDENTITY, ANGLE must "pre-rotate" various aspects of its work
680 // (e.g. rotate vertices in the vertex shaders, change scissor, viewport, and render-pass
681 // renderArea). The swapchain's transform is given the value of mSurfaceCaps.currentTransform.
682 // That prevents SurfaceFlinger from doing a rotation blit for every frame (which is costly in
683 // terms of performance and power).
684 //
685 // When a window is rotated 90 or 270 degrees, the aspect ratio changes. The width and height
686 // are swapped. The x/y and width/height of various values in ANGLE must also be swapped
687 // before communicating the values to Vulkan.
688 if (renderer->getFeatures().enablePreRotateSurfaces.enabled)
689 {
690 // Use the surface's transform. For many platforms, this will always be identity (ANGLE
691 // does not need to do any pre-rotation). However, when mSurfaceCaps.currentTransform is
692 // not identity, the device has been rotated away from its natural orientation. In such a
693 // case, ANGLE must rotate all rendering in order to avoid the compositor
694 // (e.g. SurfaceFlinger on Android) performing an additional rotation blit. In addition,
695 // ANGLE must create the swapchain with VkSwapchainCreateInfoKHR::preTransform set to the
696 // value of mSurfaceCaps.currentTransform.
697 mPreTransform = mSurfaceCaps.currentTransform;
698 }
699 else
700 {
701 // Default to identity transform.
702 mPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
703
704 if ((mSurfaceCaps.supportedTransforms & mPreTransform) == 0)
705 {
706 mPreTransform = mSurfaceCaps.currentTransform;
707 }
708 }
709
710 // Set emulated pre-transform if any emulated prerotation features are set.
711 if (renderer->getFeatures().emulatedPrerotation90.enabled)
712 {
713 mEmulatedPreTransform = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR;
714 }
715 else if (renderer->getFeatures().emulatedPrerotation180.enabled)
716 {
717 mEmulatedPreTransform = VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR;
718 }
719 else if (renderer->getFeatures().emulatedPrerotation270.enabled)
720 {
721 mEmulatedPreTransform = VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
722 }
723
724 // If prerotation is emulated, the window is physically rotated. With real prerotation, the
725 // surface reports the rotated sizes. With emulated prerotation however, the surface reports
726 // the actual window sizes. Adjust the window extents to match what real prerotation would have
727 // reported.
728 if (Is90DegreeRotation(mEmulatedPreTransform))
729 {
730 ASSERT(mPreTransform == VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR);
731 std::swap(extents.width, extents.height);
732 }
733
734 uint32_t presentModeCount = 0;
735 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, mSurface,
736 &presentModeCount, nullptr));
737 ASSERT(presentModeCount > 0);
738
739 mPresentModes.resize(presentModeCount);
740 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(
741 physicalDevice, mSurface, &presentModeCount, mPresentModes.data()));
742
743 // Select appropriate present mode based on vsync parameter. Default to 1 (FIFO), though it
744 // will get clamped to the min/max values specified at display creation time.
745 setSwapInterval(renderer->getFeatures().disableFifoPresentMode.enabled ? 0 : 1);
746
747 uint32_t surfaceFormatCount = 0;
748 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface,
749 &surfaceFormatCount, nullptr));
750
751 std::vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatCount);
752 ANGLE_VK_TRY(displayVk,
753 vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &surfaceFormatCount,
754 surfaceFormats.data()));
755
756 const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
757 VkFormat nativeFormat = format.actualImageVkFormat();
758
759 if (surfaceFormatCount == 1u && surfaceFormats[0].format == VK_FORMAT_UNDEFINED)
760 {
761 // This is fine.
762 }
763 else
764 {
765 bool foundFormat = false;
766 for (const VkSurfaceFormatKHR &surfaceFormat : surfaceFormats)
767 {
768 if (surfaceFormat.format == nativeFormat)
769 {
770 foundFormat = true;
771 break;
772 }
773 }
774
775 // If a non-linear colorspace was requested but the non-linear format is
776 // not supported as a vulkan surface format, treat it as a non-fatal error
777 if (!foundFormat)
778 {
779 return angle::Result::Incomplete;
780 }
781 }
782
783 mCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
784 if ((mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) == 0)
785 {
786 mCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
787 }
788 ANGLE_VK_CHECK(displayVk, (mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) != 0,
789 VK_ERROR_INITIALIZATION_FAILED);
790
791 ANGLE_TRY(createSwapChain(displayVk, extents, VK_NULL_HANDLE));
792
793 VkResult vkResult = acquireNextSwapchainImage(displayVk);
794 // VK_SUBOPTIMAL_KHR is ok since we still have an Image that can be presented successfully
795 if (ANGLE_UNLIKELY((vkResult != VK_SUCCESS) && (vkResult != VK_SUBOPTIMAL_KHR)))
796 {
797 ANGLE_VK_TRY(displayVk, vkResult);
798 }
799
800 return angle::Result::Continue;
801 }
802
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)803 angle::Result WindowSurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
804 GLenum binding,
805 const gl::ImageIndex &imageIndex,
806 GLsizei samples,
807 FramebufferAttachmentRenderTarget **rtOut)
808 {
809 if (mNeedToAcquireNextSwapchainImage)
810 {
811 // Acquire the next image (previously deferred) before it is drawn to or read from.
812 ANGLE_TRY(doDeferredAcquireNextImage(context, false));
813 }
814 return SurfaceVk::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut);
815 }
816
recreateSwapchain(ContextVk * contextVk,const gl::Extents & extents)817 angle::Result WindowSurfaceVk::recreateSwapchain(ContextVk *contextVk, const gl::Extents &extents)
818 {
819 // If mOldSwapchains is not empty, it means that a new swapchain was created, but before
820 // any of its images were presented, it's asked to be recreated. In this case, we can destroy
821 // the current swapchain immediately (although the old swapchains still need to be kept to be
822 // scheduled for destruction). This can happen for example if vkQueuePresentKHR returns
823 // OUT_OF_DATE, the swapchain is recreated and the following vkAcquireNextImageKHR again
824 // returns OUT_OF_DATE.
825 //
826 // Otherwise, keep the current swapchain as the old swapchain to be scheduled for destruction
827 // and create a new one.
828
829 VkSwapchainKHR swapchainToDestroy = VK_NULL_HANDLE;
830
831 if (!mOldSwapchains.empty())
832 {
833 // Keep the old swapchain, destroy the current (never-used) swapchain.
834 swapchainToDestroy = mSwapchain;
835
836 // Recycle present semaphores.
837 for (SwapchainImage &swapchainImage : mSwapchainImages)
838 {
839 for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
840 {
841 ASSERT(presentHistory.semaphore.valid());
842 ASSERT(presentHistory.oldSwapchains.empty());
843
844 mPresentSemaphoreRecycler.recycle(std::move(presentHistory.semaphore));
845 }
846 }
847 }
848 else
849 {
850 SwapchainCleanupData cleanupData;
851
852 // Remember the current swapchain to be scheduled for destruction later.
853 cleanupData.swapchain = mSwapchain;
854
855 // Accumulate the semaphores to be destroyed at the same time as the swapchain.
856 for (SwapchainImage &swapchainImage : mSwapchainImages)
857 {
858 for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
859 {
860 ASSERT(presentHistory.semaphore.valid());
861 cleanupData.semaphores.emplace_back(std::move(presentHistory.semaphore));
862
863 // Accumulate any previous swapchains that are pending destruction too.
864 for (SwapchainCleanupData &oldSwapchain : presentHistory.oldSwapchains)
865 {
866 mOldSwapchains.emplace_back(std::move(oldSwapchain));
867 }
868 presentHistory.oldSwapchains.clear();
869 }
870 }
871
872 // If too many old swapchains have accumulated, wait idle and destroy them. This is to
873 // prevent failures due to too many swapchains allocated.
874 //
875 // Note: Nvidia has been observed to fail creation of swapchains after 20 are allocated on
876 // desktop, or less than 10 on Quadro P400.
877 static constexpr size_t kMaxOldSwapchains = 5;
878 if (mOldSwapchains.size() > kMaxOldSwapchains)
879 {
880 ANGLE_TRY(
881 contextVk->getRenderer()->finish(contextVk, contextVk->hasProtectedContent()));
882 for (SwapchainCleanupData &oldSwapchain : mOldSwapchains)
883 {
884 oldSwapchain.destroy(contextVk->getDevice(), &mPresentSemaphoreRecycler);
885 }
886 mOldSwapchains.clear();
887 }
888
889 mOldSwapchains.emplace_back(std::move(cleanupData));
890 }
891
892 // Recreate the swapchain based on the most recent one.
893 VkSwapchainKHR lastSwapchain = mSwapchain;
894 mSwapchain = VK_NULL_HANDLE;
895
896 releaseSwapchainImages(contextVk);
897
898 // If prerotation is emulated, adjust the window extents to match what real prerotation would
899 // have reported.
900 gl::Extents swapchainExtents = extents;
901 if (Is90DegreeRotation(mEmulatedPreTransform))
902 {
903 ASSERT(mPreTransform == VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR);
904 std::swap(swapchainExtents.width, swapchainExtents.height);
905 }
906
907 angle::Result result = createSwapChain(contextVk, swapchainExtents, lastSwapchain);
908
909 // Notify the parent classes of the surface's new state.
910 onStateChange(angle::SubjectMessage::SurfaceChanged);
911
912 // If the most recent swapchain was never used, destroy it right now.
913 if (swapchainToDestroy)
914 {
915 vkDestroySwapchainKHR(contextVk->getDevice(), swapchainToDestroy, nullptr);
916 }
917
918 return result;
919 }
920
newPresentSemaphore(vk::Context * context,vk::Semaphore * semaphoreOut)921 angle::Result WindowSurfaceVk::newPresentSemaphore(vk::Context *context,
922 vk::Semaphore *semaphoreOut)
923 {
924 if (mPresentSemaphoreRecycler.empty())
925 {
926 ANGLE_VK_TRY(context, semaphoreOut->init(context->getDevice()));
927 }
928 else
929 {
930 mPresentSemaphoreRecycler.fetch(semaphoreOut);
931 }
932 return angle::Result::Continue;
933 }
934
MapEglColorSpaceToVkColorSpace(EGLenum EGLColorspace)935 static VkColorSpaceKHR MapEglColorSpaceToVkColorSpace(EGLenum EGLColorspace)
936 {
937 switch (EGLColorspace)
938 {
939 case EGL_NONE:
940 case EGL_GL_COLORSPACE_LINEAR:
941 case EGL_GL_COLORSPACE_SRGB_KHR:
942 case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
943 return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
944 case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
945 return VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
946 case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
947 return VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT;
948 case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
949 return VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
950 case EGL_GL_COLORSPACE_SCRGB_EXT:
951 return VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
952 default:
953 UNREACHABLE();
954 return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
955 }
956 }
957
resizeSwapchainImages(vk::Context * context,uint32_t imageCount)958 angle::Result WindowSurfaceVk::resizeSwapchainImages(vk::Context *context, uint32_t imageCount)
959 {
960 if (static_cast<size_t>(imageCount) != mSwapchainImages.size())
961 {
962 mSwapchainImageBindings.clear();
963 mSwapchainImages.resize(imageCount);
964
965 // Update the image bindings. Because the observer binding class uses raw pointers we
966 // need to first ensure the entire image vector is fully allocated before binding the
967 // subject and observer together.
968 for (uint32_t index = 0; index < imageCount; ++index)
969 {
970 mSwapchainImageBindings.push_back(
971 angle::ObserverBinding(this, kAnySurfaceImageSubjectIndex));
972 }
973
974 for (uint32_t index = 0; index < imageCount; ++index)
975 {
976 mSwapchainImageBindings[index].bind(&mSwapchainImages[index].image);
977 }
978 }
979
980 // At this point, if there was a previous swapchain, the previous present semaphores have all
981 // been moved to mOldSwapchains to be scheduled for destruction, so all semaphore handles in
982 // mSwapchainImages should be invalid.
983 for (SwapchainImage &swapchainImage : mSwapchainImages)
984 {
985 for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
986 {
987 ASSERT(!presentHistory.semaphore.valid());
988 ANGLE_TRY(newPresentSemaphore(context, &presentHistory.semaphore));
989 }
990 }
991
992 return angle::Result::Continue;
993 }
994
createSwapChain(vk::Context * context,const gl::Extents & extents,VkSwapchainKHR lastSwapchain)995 angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
996 const gl::Extents &extents,
997 VkSwapchainKHR lastSwapchain)
998 {
999 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::createSwapchain");
1000
1001 ASSERT(mSwapchain == VK_NULL_HANDLE);
1002
1003 RendererVk *renderer = context->getRenderer();
1004 VkDevice device = renderer->getDevice();
1005
1006 const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
1007 VkFormat nativeFormat = format.actualImageVkFormat();
1008
1009 gl::Extents rotatedExtents = extents;
1010 if (Is90DegreeRotation(getPreTransform()))
1011 {
1012 // The Surface is oriented such that its aspect ratio no longer matches that of the
1013 // device. In this case, the width and height of the swapchain images must be swapped to
1014 // match the device's native orientation. This must also be done for other attachments
1015 // used with the swapchain (e.g. depth buffer). The width and height of the viewport,
1016 // scissor, and render-pass render area must also be swapped. Then, when ANGLE rotates
1017 // gl_Position in the vertex shader, the rendering will look the same as if no
1018 // pre-rotation had been done.
1019 std::swap(rotatedExtents.width, rotatedExtents.height);
1020 }
1021
1022 // We need transfer src for reading back from the backbuffer.
1023 VkImageUsageFlags imageUsageFlags = kSurfaceVkColorImageUsageFlags;
1024
1025 // We need storage image for compute writes (debug overlay output).
1026 if (kEnableOverlay)
1027 {
1028 VkFormatFeatureFlags featureBits = renderer->getImageFormatFeatureBits(
1029 format.actualImageFormatID, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT);
1030 if ((featureBits & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0)
1031 {
1032 imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
1033 }
1034 }
1035
1036 VkSwapchainCreateInfoKHR swapchainInfo = {};
1037 swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1038 swapchainInfo.flags = mState.hasProtectedContent() ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR : 0;
1039 swapchainInfo.surface = mSurface;
1040 swapchainInfo.minImageCount = mMinImageCount;
1041 swapchainInfo.imageFormat = nativeFormat;
1042 swapchainInfo.imageColorSpace = MapEglColorSpaceToVkColorSpace(
1043 static_cast<EGLenum>(mState.attributes.get(EGL_GL_COLORSPACE, EGL_NONE)));
1044 // Note: Vulkan doesn't allow 0-width/height swapchains.
1045 swapchainInfo.imageExtent.width = std::max(rotatedExtents.width, 1);
1046 swapchainInfo.imageExtent.height = std::max(rotatedExtents.height, 1);
1047 swapchainInfo.imageArrayLayers = 1;
1048 swapchainInfo.imageUsage = imageUsageFlags;
1049 swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1050 swapchainInfo.queueFamilyIndexCount = 0;
1051 swapchainInfo.pQueueFamilyIndices = nullptr;
1052 swapchainInfo.preTransform = mPreTransform;
1053 swapchainInfo.compositeAlpha = mCompositeAlpha;
1054 swapchainInfo.presentMode = mDesiredSwapchainPresentMode;
1055 swapchainInfo.clipped = VK_TRUE;
1056 swapchainInfo.oldSwapchain = lastSwapchain;
1057
1058 // On Android, vkCreateSwapchainKHR destroys lastSwapchain, which is incorrect. Wait idle in
1059 // that case as a workaround.
1060 if (lastSwapchain && renderer->getFeatures().waitIdleBeforeSwapchainRecreation.enabled)
1061 {
1062 ANGLE_TRY(renderer->finish(context, mState.hasProtectedContent()));
1063 }
1064
1065 // TODO(syoussefi): Once EGL_SWAP_BEHAVIOR_PRESERVED_BIT is supported, the contents of the old
1066 // swapchain need to carry over to the new one. http://anglebug.com/2942
1067 VkSwapchainKHR newSwapChain = VK_NULL_HANDLE;
1068 ANGLE_VK_TRY(context, vkCreateSwapchainKHR(device, &swapchainInfo, nullptr, &newSwapChain));
1069 mSwapchain = newSwapChain;
1070 mSwapchainPresentMode = mDesiredSwapchainPresentMode;
1071
1072 // Intialize the swapchain image views.
1073 uint32_t imageCount = 0;
1074 ANGLE_VK_TRY(context, vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, nullptr));
1075
1076 std::vector<VkImage> swapchainImages(imageCount);
1077 ANGLE_VK_TRY(context,
1078 vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data()));
1079
1080 // If multisampling is enabled, create a multisampled image which gets resolved just prior to
1081 // present.
1082 GLint samples = GetSampleCount(mState.config);
1083 ANGLE_VK_CHECK(context, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
1084
1085 VkExtent3D vkExtents;
1086 gl_vk::GetExtent(rotatedExtents, &vkExtents);
1087
1088 bool robustInit = mState.isRobustResourceInitEnabled();
1089
1090 if (samples > 1)
1091 {
1092 const VkImageUsageFlags usage = kSurfaceVkColorImageUsageFlags;
1093
1094 // Create a multisampled image that will be rendered to, and then resolved to a swapchain
1095 // image. The actual VkImage is created with rotated coordinates to make it easier to do
1096 // the resolve. The ImageHelper::mExtents will have non-rotated extents in order to fit
1097 // with the rest of ANGLE, (e.g. which calculates the Vulkan scissor with non-rotated
1098 // values and then rotates the final rectangle).
1099 ANGLE_TRY(mColorImageMS.initMSAASwapchain(
1100 context, gl::TextureType::_2D, vkExtents, Is90DegreeRotation(getPreTransform()), format,
1101 samples, usage, gl::LevelIndex(0), 1, 1, robustInit, mState.hasProtectedContent()));
1102 ANGLE_TRY(mColorImageMS.initMemory(context, mState.hasProtectedContent(),
1103 renderer->getMemoryProperties(),
1104 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
1105
1106 // Initialize the color render target with the multisampled targets. If not multisampled,
1107 // the render target will be updated to refer to a swapchain image on every acquire.
1108 mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, nullptr, nullptr,
1109 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
1110 }
1111
1112 ANGLE_TRY(resizeSwapchainImages(context, imageCount));
1113
1114 for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex)
1115 {
1116 SwapchainImage &member = mSwapchainImages[imageIndex];
1117 member.image.init2DWeakReference(context, swapchainImages[imageIndex], extents,
1118 Is90DegreeRotation(getPreTransform()), format, 1,
1119 robustInit);
1120 member.imageViews.init(renderer);
1121 member.mFrameNumber = 0;
1122 }
1123
1124 // Initialize depth/stencil if requested.
1125 if (mState.config->depthStencilFormat != GL_NONE)
1126 {
1127 const vk::Format &dsFormat = renderer->getFormat(mState.config->depthStencilFormat);
1128
1129 const VkImageUsageFlags dsUsage = kSurfaceVkDepthStencilImageUsageFlags;
1130
1131 ANGLE_TRY(mDepthStencilImage.init(context, gl::TextureType::_2D, vkExtents, dsFormat,
1132 samples, dsUsage, gl::LevelIndex(0), 1, 1, robustInit,
1133 mState.hasProtectedContent()));
1134 ANGLE_TRY(mDepthStencilImage.initMemory(context, mState.hasProtectedContent(),
1135 renderer->getMemoryProperties(),
1136 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
1137
1138 mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, nullptr,
1139 nullptr, gl::LevelIndex(0), 0, 1,
1140 RenderTargetTransience::Default);
1141
1142 // We will need to pass depth/stencil image views to the RenderTargetVk in the future.
1143 }
1144
1145 return angle::Result::Continue;
1146 }
1147
isMultiSampled() const1148 bool WindowSurfaceVk::isMultiSampled() const
1149 {
1150 return mColorImageMS.valid();
1151 }
1152
queryAndAdjustSurfaceCaps(ContextVk * contextVk,VkSurfaceCapabilitiesKHR * surfaceCaps)1153 angle::Result WindowSurfaceVk::queryAndAdjustSurfaceCaps(ContextVk *contextVk,
1154 VkSurfaceCapabilitiesKHR *surfaceCaps)
1155 {
1156 const VkPhysicalDevice &physicalDevice = contextVk->getRenderer()->getPhysicalDevice();
1157 ANGLE_VK_TRY(contextVk,
1158 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface, surfaceCaps));
1159 if (surfaceCaps->currentExtent.width == kSurfaceSizedBySwapchain)
1160 {
1161 ASSERT(surfaceCaps->currentExtent.height == kSurfaceSizedBySwapchain);
1162 ASSERT(!IsAndroid());
1163
1164 // vkGetPhysicalDeviceSurfaceCapabilitiesKHR does not provide useful extents for some
1165 // platforms (e.g. Fuschia). Therefore, we must query the window size via a
1166 // platform-specific mechanism. Add those extents to the surfaceCaps
1167 gl::Extents currentExtents;
1168 ANGLE_TRY(getCurrentWindowSize(contextVk, ¤tExtents));
1169 surfaceCaps->currentExtent.width = currentExtents.width;
1170 surfaceCaps->currentExtent.height = currentExtents.height;
1171 }
1172
1173 return angle::Result::Continue;
1174 }
1175
checkForOutOfDateSwapchain(ContextVk * contextVk,bool presentOutOfDate)1176 angle::Result WindowSurfaceVk::checkForOutOfDateSwapchain(ContextVk *contextVk,
1177 bool presentOutOfDate)
1178 {
1179 bool swapIntervalChanged = mSwapchainPresentMode != mDesiredSwapchainPresentMode;
1180 presentOutOfDate = presentOutOfDate || swapIntervalChanged;
1181
1182 // If there's no change, early out.
1183 if (!contextVk->getRenderer()->getFeatures().perFrameWindowSizeQuery.enabled &&
1184 !presentOutOfDate)
1185 {
1186 return angle::Result::Continue;
1187 }
1188
1189 // Get the latest surface capabilities.
1190 ANGLE_TRY(queryAndAdjustSurfaceCaps(contextVk, &mSurfaceCaps));
1191
1192 if (contextVk->getRenderer()->getFeatures().perFrameWindowSizeQuery.enabled &&
1193 !presentOutOfDate)
1194 {
1195 // This device generates neither VK_ERROR_OUT_OF_DATE_KHR nor VK_SUBOPTIMAL_KHR. Check for
1196 // whether the size and/or rotation have changed since the swapchain was created.
1197 uint32_t swapchainWidth = getWidth();
1198 uint32_t swapchainHeight = getHeight();
1199 presentOutOfDate = mSurfaceCaps.currentTransform != mPreTransform ||
1200 mSurfaceCaps.currentExtent.width != swapchainWidth ||
1201 mSurfaceCaps.currentExtent.height != swapchainHeight;
1202 }
1203
1204 // If anything has changed, recreate the swapchain.
1205 if (!presentOutOfDate)
1206 {
1207 return angle::Result::Continue;
1208 }
1209
1210 gl::Extents newSwapchainExtents(mSurfaceCaps.currentExtent.width,
1211 mSurfaceCaps.currentExtent.height, 1);
1212
1213 if (contextVk->getFeatures().enablePreRotateSurfaces.enabled)
1214 {
1215 // Update the surface's transform, which can change even if the window size does not.
1216 mPreTransform = mSurfaceCaps.currentTransform;
1217 }
1218
1219 return recreateSwapchain(contextVk, newSwapchainExtents);
1220 }
1221
releaseSwapchainImages(ContextVk * contextVk)1222 void WindowSurfaceVk::releaseSwapchainImages(ContextVk *contextVk)
1223 {
1224 RendererVk *renderer = contextVk->getRenderer();
1225
1226 if (mDepthStencilImage.valid())
1227 {
1228 mDepthStencilImage.releaseImageFromShareContexts(renderer, contextVk);
1229 mDepthStencilImage.releaseStagingBuffer(renderer);
1230 mDepthStencilImageViews.release(renderer);
1231 }
1232
1233 if (mColorImageMS.valid())
1234 {
1235 mColorImageMS.releaseImageFromShareContexts(renderer, contextVk);
1236 mColorImageMS.releaseStagingBuffer(renderer);
1237 mColorImageMSViews.release(renderer);
1238 contextVk->addGarbage(&mFramebufferMS);
1239 }
1240
1241 mSwapchainImageBindings.clear();
1242
1243 for (SwapchainImage &swapchainImage : mSwapchainImages)
1244 {
1245 // We don't own the swapchain image handles, so we just remove our reference to it.
1246 swapchainImage.image.resetImageWeakReference();
1247 swapchainImage.image.destroy(renderer);
1248
1249 swapchainImage.imageViews.release(renderer);
1250 contextVk->addGarbage(&swapchainImage.framebuffer);
1251
1252 // present history must have already been taken care of.
1253 for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
1254 {
1255 ASSERT(!presentHistory.semaphore.valid());
1256 ASSERT(presentHistory.oldSwapchains.empty());
1257 }
1258 }
1259
1260 mSwapchainImages.clear();
1261 }
1262
destroySwapChainImages(DisplayVk * displayVk)1263 void WindowSurfaceVk::destroySwapChainImages(DisplayVk *displayVk)
1264 {
1265 RendererVk *renderer = displayVk->getRenderer();
1266 VkDevice device = displayVk->getDevice();
1267
1268 mDepthStencilImage.destroy(renderer);
1269 mDepthStencilImageViews.destroy(device);
1270 mColorImageMS.destroy(renderer);
1271 mColorImageMSViews.destroy(device);
1272 mFramebufferMS.destroy(device);
1273
1274 for (SwapchainImage &swapchainImage : mSwapchainImages)
1275 {
1276 // We don't own the swapchain image handles, so we just remove our reference to it.
1277 swapchainImage.image.resetImageWeakReference();
1278 swapchainImage.image.destroy(renderer);
1279 swapchainImage.imageViews.destroy(device);
1280 swapchainImage.framebuffer.destroy(device);
1281
1282 for (ImagePresentHistory &presentHistory : swapchainImage.presentHistory)
1283 {
1284 ASSERT(presentHistory.semaphore.valid());
1285
1286 mPresentSemaphoreRecycler.recycle(std::move(presentHistory.semaphore));
1287 for (SwapchainCleanupData &oldSwapchain : presentHistory.oldSwapchains)
1288 {
1289 oldSwapchain.destroy(device, &mPresentSemaphoreRecycler);
1290 }
1291 presentHistory.oldSwapchains.clear();
1292 }
1293 }
1294
1295 mSwapchainImages.clear();
1296 }
1297
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)1298 FramebufferImpl *WindowSurfaceVk::createDefaultFramebuffer(const gl::Context *context,
1299 const gl::FramebufferState &state)
1300 {
1301 RendererVk *renderer = vk::GetImpl(context)->getRenderer();
1302 return FramebufferVk::CreateDefaultFBO(renderer, state, this);
1303 }
1304
swapWithDamage(const gl::Context * context,const EGLint * rects,EGLint n_rects)1305 egl::Error WindowSurfaceVk::swapWithDamage(const gl::Context *context,
1306 const EGLint *rects,
1307 EGLint n_rects)
1308 {
1309 DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1310 angle::Result result;
1311 result = swapImpl(context, rects, n_rects, nullptr);
1312 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
1313 }
1314
swap(const gl::Context * context)1315 egl::Error WindowSurfaceVk::swap(const gl::Context *context)
1316 {
1317 DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1318 angle::Result result = swapImpl(context, nullptr, 0, nullptr);
1319 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
1320 }
1321
computePresentOutOfDate(vk::Context * context,VkResult result,bool * presentOutOfDate)1322 angle::Result WindowSurfaceVk::computePresentOutOfDate(vk::Context *context,
1323 VkResult result,
1324 bool *presentOutOfDate)
1325 {
1326 // If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
1327 // continuing.
1328 // If VK_SUBOPTIMAL_KHR is returned it's because the device orientation changed and we should
1329 // recreate the swapchain with a new window orientation.
1330 if (context->getRenderer()->getFeatures().enablePreRotateSurfaces.enabled)
1331 {
1332 // Also check for VK_SUBOPTIMAL_KHR.
1333 *presentOutOfDate = ((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR));
1334 if (!*presentOutOfDate)
1335 {
1336 ANGLE_VK_TRY(context, result);
1337 }
1338 }
1339 else
1340 {
1341 // We aren't quite ready for that so just ignore for now.
1342 *presentOutOfDate = result == VK_ERROR_OUT_OF_DATE_KHR;
1343 if (!*presentOutOfDate && result != VK_SUBOPTIMAL_KHR)
1344 {
1345 ANGLE_VK_TRY(context, result);
1346 }
1347 }
1348 return angle::Result::Continue;
1349 }
1350
present(ContextVk * contextVk,const EGLint * rects,EGLint n_rects,const void * pNextChain,bool * presentOutOfDate)1351 angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
1352 const EGLint *rects,
1353 EGLint n_rects,
1354 const void *pNextChain,
1355 bool *presentOutOfDate)
1356 {
1357 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present");
1358 RendererVk *renderer = contextVk->getRenderer();
1359
1360 // Throttle the submissions to avoid getting too far ahead of the GPU.
1361 Serial *swapSerial = &mSwapHistory[mCurrentSwapHistoryIndex];
1362 {
1363 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present: Throttle CPU");
1364 ANGLE_TRY(renderer->finishToSerial(contextVk, *swapSerial));
1365 }
1366
1367 SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
1368 vk::Framebuffer ¤tFramebuffer = mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
1369 updateOverlay(contextVk);
1370 bool overlayHasWidget = overlayHasEnabledWidget(contextVk);
1371
1372 // Make sure deferred clears are applied, if any.
1373 ANGLE_TRY(
1374 image.image.flushStagedUpdates(contextVk, gl::LevelIndex(0), gl::LevelIndex(1), 0, 1, {}));
1375
1376 // We can only do present related optimization if this is the last renderpass that touches the
1377 // swapchain image. MSAA resolve and overlay will insert another renderpass which disqualifies
1378 // the optimization.
1379 if (!mColorImageMS.valid() && !overlayHasWidget && currentFramebuffer.valid())
1380 {
1381 contextVk->optimizeRenderPassForPresent(currentFramebuffer.getHandle());
1382 }
1383
1384 // Because the color attachment defers layout changes until endRenderPass time, we must call
1385 // finalize the layout transition in the renderpass before we insert layout change to
1386 // ImageLayout::Present bellow.
1387 contextVk->finalizeImageLayout(&image.image);
1388
1389 vk::CommandBuffer *commandBuffer;
1390 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
1391
1392 if (mColorImageMS.valid())
1393 {
1394 // Transition the multisampled image to TRANSFER_SRC for resolve.
1395 vk::CommandBufferAccess access;
1396 access.onImageTransferRead(VK_IMAGE_ASPECT_COLOR_BIT, &mColorImageMS);
1397 access.onImageTransferWrite(gl::LevelIndex(0), 1, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
1398 &image.image);
1399
1400 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
1401
1402 VkImageResolve resolveRegion = {};
1403 resolveRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1404 resolveRegion.srcSubresource.mipLevel = 0;
1405 resolveRegion.srcSubresource.baseArrayLayer = 0;
1406 resolveRegion.srcSubresource.layerCount = 1;
1407 resolveRegion.srcOffset = {};
1408 resolveRegion.dstSubresource = resolveRegion.srcSubresource;
1409 resolveRegion.dstOffset = {};
1410 resolveRegion.extent = image.image.getRotatedExtents();
1411
1412 mColorImageMS.resolve(&image.image, resolveRegion, commandBuffer);
1413 }
1414
1415 if (overlayHasWidget)
1416 {
1417 ANGLE_TRY(drawOverlay(contextVk, &image));
1418 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer({}, &commandBuffer));
1419 }
1420
1421 // This does nothing if it's already in the requested layout
1422 image.image.recordReadBarrier(contextVk, VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::Present,
1423 commandBuffer);
1424
1425 // Knowing that the kSwapHistorySize'th submission ago has finished, we can know that the
1426 // (kSwapHistorySize+1)'th present ago of this image is definitely finished and so its wait
1427 // semaphore can be reused. See doc/PresentSemaphores.md for details.
1428 //
1429 // This also means the swapchain(s) scheduled to be deleted at the same time can be deleted.
1430 ImagePresentHistory &presentHistory = image.presentHistory[image.currentPresentHistoryIndex];
1431 vk::Semaphore *presentSemaphore = &presentHistory.semaphore;
1432 ASSERT(presentSemaphore->valid());
1433
1434 for (SwapchainCleanupData &oldSwapchain : presentHistory.oldSwapchains)
1435 {
1436 oldSwapchain.destroy(contextVk->getDevice(), &mPresentSemaphoreRecycler);
1437 }
1438 presentHistory.oldSwapchains.clear();
1439
1440 // Schedule pending old swapchains to be destroyed at the same time the semaphore for this
1441 // present can be destroyed.
1442 presentHistory.oldSwapchains = std::move(mOldSwapchains);
1443
1444 image.currentPresentHistoryIndex =
1445 (image.currentPresentHistoryIndex + 1) % image.presentHistory.size();
1446
1447 ANGLE_TRY(contextVk->flushImpl(presentSemaphore));
1448
1449 VkPresentInfoKHR presentInfo = {};
1450 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1451 presentInfo.pNext = pNextChain;
1452 presentInfo.waitSemaphoreCount = 1;
1453 presentInfo.pWaitSemaphores = presentSemaphore->ptr();
1454 presentInfo.swapchainCount = 1;
1455 presentInfo.pSwapchains = &mSwapchain;
1456 presentInfo.pImageIndices = &mCurrentSwapchainImageIndex;
1457 presentInfo.pResults = nullptr;
1458
1459 VkPresentRegionKHR presentRegion = {};
1460 VkPresentRegionsKHR presentRegions = {};
1461 std::vector<VkRectLayerKHR> vkRects;
1462 if (contextVk->getFeatures().supportsIncrementalPresent.enabled && (n_rects > 0))
1463 {
1464 EGLint width = getWidth();
1465 EGLint height = getHeight();
1466
1467 const EGLint *eglRects = rects;
1468 presentRegion.rectangleCount = n_rects;
1469 vkRects.resize(n_rects);
1470 for (EGLint i = 0; i < n_rects; i++)
1471 {
1472 VkRectLayerKHR &rect = vkRects[i];
1473
1474 // Make sure the damage rects are within swapchain bounds.
1475 rect.offset.x = gl::clamp(*eglRects++, 0, width);
1476 rect.offset.y = gl::clamp(*eglRects++, 0, height);
1477 rect.extent.width = gl::clamp(*eglRects++, 0, width - rect.offset.x);
1478 rect.extent.height = gl::clamp(*eglRects++, 0, height - rect.offset.y);
1479 rect.layer = 0;
1480 // No rotation is done to these damage rectangles per the Vulkan spec.
1481 }
1482 presentRegion.pRectangles = vkRects.data();
1483
1484 presentRegions.sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
1485 presentRegions.pNext = presentInfo.pNext;
1486 presentRegions.swapchainCount = 1;
1487 presentRegions.pRegions = &presentRegion;
1488
1489 presentInfo.pNext = &presentRegions;
1490 }
1491
1492 // TODO(jmadill): Fix potential serial race. b/172704839
1493 *swapSerial = renderer->getLastSubmittedQueueSerial();
1494 ASSERT(!mAcquireImageSemaphore.valid());
1495
1496 ++mCurrentSwapHistoryIndex;
1497 mCurrentSwapHistoryIndex =
1498 mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex;
1499
1500 VkResult result = renderer->queuePresent(contextVk, contextVk->getPriority(), presentInfo);
1501
1502 // Set FrameNumber for the presented image.
1503 mSwapchainImages[mCurrentSwapchainImageIndex].mFrameNumber = mFrameCount++;
1504
1505 ANGLE_TRY(computePresentOutOfDate(contextVk, result, presentOutOfDate));
1506
1507 return angle::Result::Continue;
1508 }
1509
swapImpl(const gl::Context * context,const EGLint * rects,EGLint n_rects,const void * pNextChain)1510 angle::Result WindowSurfaceVk::swapImpl(const gl::Context *context,
1511 const EGLint *rects,
1512 EGLint n_rects,
1513 const void *pNextChain)
1514 {
1515 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::swapImpl");
1516
1517 ContextVk *contextVk = vk::GetImpl(context);
1518
1519 if (mNeedToAcquireNextSwapchainImage)
1520 {
1521 // Acquire the next image (previously deferred). The image may not have been already
1522 // acquired if there was no rendering done at all to the default framebuffer in this frame,
1523 // for example if all rendering was done to FBOs.
1524 ANGLE_TRY(doDeferredAcquireNextImage(context, false));
1525 }
1526
1527 bool presentOutOfDate = false;
1528 ANGLE_TRY(present(contextVk, rects, n_rects, pNextChain, &presentOutOfDate));
1529
1530 if (!presentOutOfDate)
1531 {
1532 // Defer acquiring the next swapchain image since the swapchain is not out-of-date.
1533 deferAcquireNextImage(context);
1534 }
1535 else
1536 {
1537 // Immediately try to acquire the next image, which will recognize the out-of-date
1538 // swapchain (potentially because of a rotation change), and recreate it.
1539 ANGLE_TRY(doDeferredAcquireNextImage(context, presentOutOfDate));
1540 }
1541
1542 return angle::Result::Continue;
1543 }
1544
deferAcquireNextImage(const gl::Context * context)1545 void WindowSurfaceVk::deferAcquireNextImage(const gl::Context *context)
1546 {
1547 mNeedToAcquireNextSwapchainImage = true;
1548
1549 // Set gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 via subject-observer message-passing
1550 // to the front-end Surface, Framebuffer, and Context classes. The DIRTY_BIT_COLOR_ATTACHMENT_0
1551 // is processed before all other dirty bits. However, since the attachments of the default
1552 // framebuffer cannot change, this bit will be processed before all others. It will cause
1553 // WindowSurfaceVk::getAttachmentRenderTarget() to be called (which will acquire the next image)
1554 // before any RenderTargetVk accesses. The processing of other dirty bits as well as other
1555 // setup for draws and reads will then access a properly-updated RenderTargetVk.
1556 onStateChange(angle::SubjectMessage::SubjectChanged);
1557 }
1558
doDeferredAcquireNextImage(const gl::Context * context,bool presentOutOfDate)1559 angle::Result WindowSurfaceVk::doDeferredAcquireNextImage(const gl::Context *context,
1560 bool presentOutOfDate)
1561 {
1562 ContextVk *contextVk = vk::GetImpl(context);
1563 DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1564
1565 // TODO(jmadill): Expose in CommandQueueInterface, or manage in CommandQueue. b/172704839
1566 if (contextVk->getFeatures().asyncCommandQueue.enabled)
1567 {
1568 VkResult result = contextVk->getRenderer()->getLastPresentResult(mSwapchain);
1569
1570 // Now that we have the result from the last present need to determine if it's out of date
1571 // or not.
1572 ANGLE_TRY(computePresentOutOfDate(contextVk, result, &presentOutOfDate));
1573 }
1574
1575 ANGLE_TRY(checkForOutOfDateSwapchain(contextVk, presentOutOfDate));
1576
1577 {
1578 // Note: TRACE_EVENT0 is put here instead of inside the function to workaround this issue:
1579 // http://anglebug.com/2927
1580 ANGLE_TRACE_EVENT0("gpu.angle", "acquireNextSwapchainImage");
1581 // Get the next available swapchain image.
1582
1583 VkResult result = acquireNextSwapchainImage(contextVk);
1584 // If SUBOPTIMAL/OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain
1585 // before continuing.
1586 if (ANGLE_UNLIKELY((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)))
1587 {
1588 ANGLE_TRY(checkForOutOfDateSwapchain(contextVk, true));
1589 // Try one more time and bail if we fail
1590 result = acquireNextSwapchainImage(contextVk);
1591 }
1592 ANGLE_VK_TRY(contextVk, result);
1593 }
1594
1595 RendererVk *renderer = contextVk->getRenderer();
1596 ANGLE_TRY(renderer->syncPipelineCacheVk(displayVk, context));
1597
1598 return angle::Result::Continue;
1599 }
1600
acquireNextSwapchainImage(vk::Context * context)1601 VkResult WindowSurfaceVk::acquireNextSwapchainImage(vk::Context *context)
1602 {
1603 VkDevice device = context->getDevice();
1604
1605 vk::DeviceScoped<vk::Semaphore> acquireImageSemaphore(device);
1606 VkResult result = acquireImageSemaphore.get().init(device);
1607 if (ANGLE_UNLIKELY(result != VK_SUCCESS))
1608 {
1609 return result;
1610 }
1611
1612 result = vkAcquireNextImageKHR(device, mSwapchain, UINT64_MAX,
1613 acquireImageSemaphore.get().getHandle(), VK_NULL_HANDLE,
1614 &mCurrentSwapchainImageIndex);
1615 if (ANGLE_UNLIKELY(result != VK_SUCCESS))
1616 {
1617 return result;
1618 }
1619
1620 // The semaphore will be waited on in the next flush.
1621 mAcquireImageSemaphore = acquireImageSemaphore.release();
1622
1623 SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
1624
1625 // Update RenderTarget pointers to this swapchain image if not multisampling. Note: a possible
1626 // optimization is to defer the |vkAcquireNextImageKHR| call itself to |present()| if
1627 // multisampling, as the swapchain image is essentially unused until then.
1628 if (!mColorImageMS.valid())
1629 {
1630 mColorRenderTarget.updateSwapchainImage(&image.image, &image.imageViews, nullptr, nullptr);
1631 }
1632
1633 // Notify the owning framebuffer there may be staged updates.
1634 if (image.image.hasStagedUpdatesInAllocatedLevels())
1635 {
1636 onStateChange(angle::SubjectMessage::SubjectChanged);
1637 }
1638
1639 // Note that an acquire is no longer needed.
1640 mNeedToAcquireNextSwapchainImage = false;
1641
1642 return VK_SUCCESS;
1643 }
1644
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)1645 egl::Error WindowSurfaceVk::postSubBuffer(const gl::Context *context,
1646 EGLint x,
1647 EGLint y,
1648 EGLint width,
1649 EGLint height)
1650 {
1651 // TODO(jmadill)
1652 return egl::NoError();
1653 }
1654
querySurfacePointerANGLE(EGLint attribute,void ** value)1655 egl::Error WindowSurfaceVk::querySurfacePointerANGLE(EGLint attribute, void **value)
1656 {
1657 UNREACHABLE();
1658 return egl::EglBadCurrentSurface();
1659 }
1660
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)1661 egl::Error WindowSurfaceVk::bindTexImage(const gl::Context *context,
1662 gl::Texture *texture,
1663 EGLint buffer)
1664 {
1665 return egl::NoError();
1666 }
1667
releaseTexImage(const gl::Context * context,EGLint buffer)1668 egl::Error WindowSurfaceVk::releaseTexImage(const gl::Context *context, EGLint buffer)
1669 {
1670 return egl::NoError();
1671 }
1672
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)1673 egl::Error WindowSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
1674 EGLuint64KHR * /*msc*/,
1675 EGLuint64KHR * /*sbc*/)
1676 {
1677 UNIMPLEMENTED();
1678 return egl::EglBadAccess();
1679 }
1680
getMscRate(EGLint *,EGLint *)1681 egl::Error WindowSurfaceVk::getMscRate(EGLint * /*numerator*/, EGLint * /*denominator*/)
1682 {
1683 UNIMPLEMENTED();
1684 return egl::EglBadAccess();
1685 }
1686
setSwapInterval(EGLint interval)1687 void WindowSurfaceVk::setSwapInterval(EGLint interval)
1688 {
1689 const EGLint minSwapInterval = mState.config->minSwapInterval;
1690 const EGLint maxSwapInterval = mState.config->maxSwapInterval;
1691 ASSERT(minSwapInterval == 0 || minSwapInterval == 1);
1692 ASSERT(maxSwapInterval == 0 || maxSwapInterval == 1);
1693
1694 interval = gl::clamp(interval, minSwapInterval, maxSwapInterval);
1695
1696 mDesiredSwapchainPresentMode = GetDesiredPresentMode(mPresentModes, interval);
1697
1698 // - On mailbox, we need at least three images; one is being displayed to the user until the
1699 // next v-sync, and the application alternatingly renders to the other two, one being
1700 // recorded, and the other queued for presentation if v-sync happens in the meantime.
1701 // - On immediate, we need at least two images; the application alternates between the two
1702 // images.
1703 // - On fifo, we use at least three images. Triple-buffering allows us to present an image,
1704 // have one in the queue, and record in another. Note: on certain configurations (windows +
1705 // nvidia + windowed mode), we could get away with a smaller number.
1706 //
1707 // For simplicity, we always allocate at least three images.
1708 mMinImageCount = std::max(3u, mSurfaceCaps.minImageCount);
1709
1710 // Make sure we don't exceed maxImageCount.
1711 if (mSurfaceCaps.maxImageCount > 0 && mMinImageCount > mSurfaceCaps.maxImageCount)
1712 {
1713 mMinImageCount = mSurfaceCaps.maxImageCount;
1714 }
1715
1716 // On the next swap, if the desired present mode is different from the current one, the
1717 // swapchain will be recreated.
1718 }
1719
getWidth() const1720 EGLint WindowSurfaceVk::getWidth() const
1721 {
1722 return static_cast<EGLint>(mColorRenderTarget.getExtents().width);
1723 }
1724
getRotatedWidth() const1725 EGLint WindowSurfaceVk::getRotatedWidth() const
1726 {
1727 return static_cast<EGLint>(mColorRenderTarget.getRotatedExtents().width);
1728 }
1729
getHeight() const1730 EGLint WindowSurfaceVk::getHeight() const
1731 {
1732 return static_cast<EGLint>(mColorRenderTarget.getExtents().height);
1733 }
1734
getRotatedHeight() const1735 EGLint WindowSurfaceVk::getRotatedHeight() const
1736 {
1737 return static_cast<EGLint>(mColorRenderTarget.getRotatedExtents().height);
1738 }
1739
getUserWidth(const egl::Display * display,EGLint * value) const1740 egl::Error WindowSurfaceVk::getUserWidth(const egl::Display *display, EGLint *value) const
1741 {
1742 DisplayVk *displayVk = vk::GetImpl(display);
1743
1744 if (mSurfaceCaps.currentExtent.width == kSurfaceSizedBySwapchain)
1745 {
1746 // Surface has no intrinsic size; use current size.
1747 *value = getWidth();
1748 return egl::NoError();
1749 }
1750
1751 VkSurfaceCapabilitiesKHR surfaceCaps;
1752 angle::Result result = getUserExtentsImpl(displayVk, &surfaceCaps);
1753 if (result == angle::Result::Continue)
1754 {
1755 // The EGL spec states that value is not written if there is an error
1756 ASSERT(surfaceCaps.currentExtent.width != kSurfaceSizedBySwapchain);
1757 *value = static_cast<EGLint>(surfaceCaps.currentExtent.width);
1758 }
1759 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
1760 }
1761
getUserHeight(const egl::Display * display,EGLint * value) const1762 egl::Error WindowSurfaceVk::getUserHeight(const egl::Display *display, EGLint *value) const
1763 {
1764 DisplayVk *displayVk = vk::GetImpl(display);
1765
1766 if (mSurfaceCaps.currentExtent.height == kSurfaceSizedBySwapchain)
1767 {
1768 // Surface has no intrinsic size; use current size.
1769 *value = getHeight();
1770 return egl::NoError();
1771 }
1772
1773 VkSurfaceCapabilitiesKHR surfaceCaps;
1774 angle::Result result = getUserExtentsImpl(displayVk, &surfaceCaps);
1775 if (result == angle::Result::Continue)
1776 {
1777 // The EGL spec states that value is not written if there is an error
1778 ASSERT(surfaceCaps.currentExtent.height != kSurfaceSizedBySwapchain);
1779 *value = static_cast<EGLint>(surfaceCaps.currentExtent.height);
1780 }
1781 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
1782 }
1783
getUserExtentsImpl(DisplayVk * displayVk,VkSurfaceCapabilitiesKHR * surfaceCaps) const1784 angle::Result WindowSurfaceVk::getUserExtentsImpl(DisplayVk *displayVk,
1785 VkSurfaceCapabilitiesKHR *surfaceCaps) const
1786 {
1787 const VkPhysicalDevice &physicalDevice = displayVk->getRenderer()->getPhysicalDevice();
1788
1789 ANGLE_VK_TRY(displayVk,
1790 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface, surfaceCaps));
1791
1792 // With real prerotation, the surface reports the rotated sizes. With emulated prerotation,
1793 // adjust the window extents to match what real pre-rotation would have reported.
1794 if (Is90DegreeRotation(mEmulatedPreTransform))
1795 {
1796 std::swap(surfaceCaps->currentExtent.width, surfaceCaps->currentExtent.height);
1797 }
1798
1799 return angle::Result::Continue;
1800 }
1801
isPostSubBufferSupported() const1802 EGLint WindowSurfaceVk::isPostSubBufferSupported() const
1803 {
1804 // TODO(jmadill)
1805 return EGL_FALSE;
1806 }
1807
getSwapBehavior() const1808 EGLint WindowSurfaceVk::getSwapBehavior() const
1809 {
1810 // TODO(jmadill)
1811 return EGL_BUFFER_DESTROYED;
1812 }
1813
getCurrentFramebuffer(ContextVk * contextVk,const vk::RenderPass & compatibleRenderPass,vk::Framebuffer ** framebufferOut)1814 angle::Result WindowSurfaceVk::getCurrentFramebuffer(ContextVk *contextVk,
1815 const vk::RenderPass &compatibleRenderPass,
1816 vk::Framebuffer **framebufferOut)
1817 {
1818 // FramebufferVk dirty-bit processing should ensure that a new image was acquired.
1819 ASSERT(!mNeedToAcquireNextSwapchainImage);
1820
1821 vk::Framebuffer ¤tFramebuffer =
1822 isMultiSampled() ? mFramebufferMS
1823 : mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
1824
1825 if (currentFramebuffer.valid())
1826 {
1827 // Validation layers should detect if the render pass is really compatible.
1828 *framebufferOut = ¤tFramebuffer;
1829 return angle::Result::Continue;
1830 }
1831
1832 VkFramebufferCreateInfo framebufferInfo = {};
1833
1834 const gl::Extents rotatedExtents = mColorRenderTarget.getRotatedExtents();
1835 std::array<VkImageView, 2> imageViews = {};
1836
1837 if (mDepthStencilImage.valid())
1838 {
1839 const vk::ImageView *imageView = nullptr;
1840 ANGLE_TRY(mDepthStencilRenderTarget.getImageView(contextVk, &imageView));
1841 imageViews[1] = imageView->getHandle();
1842 }
1843
1844 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1845 framebufferInfo.flags = 0;
1846 framebufferInfo.renderPass = compatibleRenderPass.getHandle();
1847 framebufferInfo.attachmentCount = (mDepthStencilImage.valid() ? 2u : 1u);
1848 framebufferInfo.pAttachments = imageViews.data();
1849 framebufferInfo.width = static_cast<uint32_t>(rotatedExtents.width);
1850 framebufferInfo.height = static_cast<uint32_t>(rotatedExtents.height);
1851 framebufferInfo.layers = 1;
1852
1853 if (isMultiSampled())
1854 {
1855 // If multisampled, there is only a single color image and framebuffer.
1856 const vk::ImageView *imageView = nullptr;
1857 ANGLE_TRY(mColorRenderTarget.getImageView(contextVk, &imageView));
1858 imageViews[0] = imageView->getHandle();
1859 ANGLE_VK_TRY(contextVk, mFramebufferMS.init(contextVk->getDevice(), framebufferInfo));
1860 }
1861 else
1862 {
1863 for (SwapchainImage &swapchainImage : mSwapchainImages)
1864 {
1865 const vk::ImageView *imageView = nullptr;
1866 ANGLE_TRY(swapchainImage.imageViews.getLevelLayerDrawImageView(
1867 contextVk, swapchainImage.image, vk::LevelIndex(0), 0,
1868 gl::SrgbWriteControlMode::Default, &imageView));
1869
1870 imageViews[0] = imageView->getHandle();
1871 ANGLE_VK_TRY(contextVk,
1872 swapchainImage.framebuffer.init(contextVk->getDevice(), framebufferInfo));
1873 }
1874 }
1875
1876 ASSERT(currentFramebuffer.valid());
1877 *framebufferOut = ¤tFramebuffer;
1878 return angle::Result::Continue;
1879 }
1880
getAcquireImageSemaphore()1881 vk::Semaphore WindowSurfaceVk::getAcquireImageSemaphore()
1882 {
1883 return std::move(mAcquireImageSemaphore);
1884 }
1885
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)1886 angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context,
1887 const gl::ImageIndex &imageIndex)
1888 {
1889 ContextVk *contextVk = vk::GetImpl(context);
1890
1891 if (mNeedToAcquireNextSwapchainImage)
1892 {
1893 // Acquire the next image (previously deferred). Some tests (e.g.
1894 // GenerateMipmapWithRedefineBenchmark.Run/vulkan_webgl) cause this path to be taken,
1895 // because of dirty-object processing.
1896 ANGLE_TRY(doDeferredAcquireNextImage(context, false));
1897 }
1898
1899 ASSERT(mSwapchainImages.size() > 0);
1900 ASSERT(mCurrentSwapchainImageIndex < mSwapchainImages.size());
1901
1902 vk::ImageHelper *image =
1903 isMultiSampled() ? &mColorImageMS : &mSwapchainImages[mCurrentSwapchainImageIndex].image;
1904 image->stageRobustResourceClear(imageIndex);
1905 ANGLE_TRY(image->flushAllStagedUpdates(contextVk));
1906
1907 if (mDepthStencilImage.valid())
1908 {
1909 mDepthStencilImage.stageRobustResourceClear(gl::ImageIndex::Make2D(0));
1910 ANGLE_TRY(mDepthStencilImage.flushAllStagedUpdates(contextVk));
1911 }
1912
1913 return angle::Result::Continue;
1914 }
1915
updateOverlay(ContextVk * contextVk) const1916 void WindowSurfaceVk::updateOverlay(ContextVk *contextVk) const
1917 {
1918 const gl::OverlayType *overlay = contextVk->getOverlay();
1919
1920 // If overlay is disabled, nothing to do.
1921 if (!overlay->isEnabled())
1922 {
1923 return;
1924 }
1925
1926 RendererVk *renderer = contextVk->getRenderer();
1927
1928 uint32_t validationMessageCount = 0;
1929 std::string lastValidationMessage =
1930 renderer->getAndClearLastValidationMessage(&validationMessageCount);
1931 if (validationMessageCount)
1932 {
1933 overlay->getTextWidget(gl::WidgetId::VulkanLastValidationMessage)
1934 ->set(std::move(lastValidationMessage));
1935 overlay->getCountWidget(gl::WidgetId::VulkanValidationMessageCount)
1936 ->add(validationMessageCount);
1937 }
1938
1939 contextVk->updateOverlayOnPresent();
1940 }
1941
overlayHasEnabledWidget(ContextVk * contextVk) const1942 ANGLE_INLINE bool WindowSurfaceVk::overlayHasEnabledWidget(ContextVk *contextVk) const
1943 {
1944 const gl::OverlayType *overlay = contextVk->getOverlay();
1945 OverlayVk *overlayVk = vk::GetImpl(overlay);
1946 return overlayVk && overlayVk->getEnabledWidgetCount() > 0;
1947 }
1948
drawOverlay(ContextVk * contextVk,SwapchainImage * image) const1949 angle::Result WindowSurfaceVk::drawOverlay(ContextVk *contextVk, SwapchainImage *image) const
1950 {
1951 const gl::OverlayType *overlay = contextVk->getOverlay();
1952 OverlayVk *overlayVk = vk::GetImpl(overlay);
1953
1954 // Draw overlay
1955 const vk::ImageView *imageView = nullptr;
1956 ANGLE_TRY(image->imageViews.getLevelLayerDrawImageView(
1957 contextVk, image->image, vk::LevelIndex(0), 0, gl::SrgbWriteControlMode::Default,
1958 &imageView));
1959 ANGLE_TRY(overlayVk->onPresent(contextVk, &image->image, imageView,
1960 Is90DegreeRotation(getPreTransform())));
1961
1962 return angle::Result::Continue;
1963 }
1964
getBufferAge(const gl::Context * context,EGLint * age)1965 egl::Error WindowSurfaceVk::getBufferAge(const gl::Context *context, EGLint *age)
1966 {
1967 if (mNeedToAcquireNextSwapchainImage)
1968 {
1969 // Acquire the current image if needed.
1970 DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1971 egl::Error result =
1972 angle::ToEGL(doDeferredAcquireNextImage(context, false), displayVk, EGL_BAD_SURFACE);
1973 if (result.isError())
1974 {
1975 return result;
1976 }
1977 }
1978
1979 if (age != nullptr)
1980 {
1981 uint64_t frameNumber = mSwapchainImages[mCurrentSwapchainImageIndex].mFrameNumber;
1982 if (frameNumber == 0)
1983 {
1984 *age = 0; // Has not been used for rendering yet, no age.
1985 }
1986 else
1987 {
1988 *age = static_cast<EGLint>(mFrameCount - frameNumber);
1989 }
1990 }
1991 return egl::NoError();
1992 }
1993
1994 } // namespace rx
1995