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 swapchain's extent. See
34 // the VkSurfaceCapabilitiesKHR spec for more details.
35 constexpr uint32_t kSurfaceSizedBySwapchain = 0xFFFFFFFFu;
36
37 // Special value for ImagePresentOperation::imageIndex meaning that VK_EXT_swapchain_maintenance1 is
38 // supported and fence is used instead of queueSerial.
39 constexpr uint32_t kInvalidImageIndex = std::numeric_limits<uint32_t>::max();
40
GetSampleCount(const egl::Config * config)41 GLint GetSampleCount(const egl::Config *config)
42 {
43 GLint samples = 1;
44 if (config->sampleBuffers && config->samples > 1)
45 {
46 samples = config->samples;
47 }
48 return samples;
49 }
50
GetDesiredPresentMode(const std::vector<vk::PresentMode> & presentModes,EGLint interval)51 vk::PresentMode GetDesiredPresentMode(const std::vector<vk::PresentMode> &presentModes,
52 EGLint interval)
53 {
54 ASSERT(!presentModes.empty());
55
56 // If v-sync is enabled, use FIFO, which throttles you to the display rate and is guaranteed to
57 // always be supported.
58 if (interval > 0)
59 {
60 return vk::PresentMode::FifoKHR;
61 }
62
63 // Otherwise, choose either of the following, if available, in order specified here:
64 //
65 // - Mailbox is similar to triple-buffering.
66 // - Immediate is similar to single-buffering.
67 //
68 // If neither is supported, we fallback to FIFO.
69
70 bool mailboxAvailable = false;
71 bool immediateAvailable = false;
72 bool sharedPresent = false;
73
74 for (vk::PresentMode presentMode : presentModes)
75 {
76 switch (presentMode)
77 {
78 case vk::PresentMode::MailboxKHR:
79 mailboxAvailable = true;
80 break;
81 case vk::PresentMode::ImmediateKHR:
82 immediateAvailable = true;
83 break;
84 case vk::PresentMode::SharedDemandRefreshKHR:
85 sharedPresent = true;
86 break;
87 default:
88 break;
89 }
90 }
91
92 if (mailboxAvailable)
93 {
94 return vk::PresentMode::MailboxKHR;
95 }
96
97 if (immediateAvailable)
98 {
99 return vk::PresentMode::ImmediateKHR;
100 }
101
102 if (sharedPresent)
103 {
104 return vk::PresentMode::SharedDemandRefreshKHR;
105 }
106
107 // Note again that VK_PRESENT_MODE_FIFO_KHR is guaranteed to be available.
108 return vk::PresentMode::FifoKHR;
109 }
110
111 constexpr VkImageUsageFlags kSurfaceVkImageUsageFlags =
112 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
113 constexpr VkImageUsageFlags kSurfaceVkColorImageUsageFlags =
114 kSurfaceVkImageUsageFlags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
115 constexpr VkImageUsageFlags kSurfaceVkDepthStencilImageUsageFlags =
116 kSurfaceVkImageUsageFlags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
117
118 // If the device is rotated with any of the following transform flags, the swapchain width and
119 // height must be swapped (e.g. make a landscape window portrait). This must also be done for all
120 // attachments used with the swapchain (i.e. depth, stencil, and multisample buffers).
121 constexpr VkSurfaceTransformFlagsKHR k90DegreeRotationVariants =
122 VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
123 VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
124 VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
125
Is90DegreeRotation(VkSurfaceTransformFlagsKHR transform)126 bool Is90DegreeRotation(VkSurfaceTransformFlagsKHR transform)
127 {
128 return ((transform & k90DegreeRotationVariants) != 0);
129 }
130
NeedsInputAttachmentUsage(const angle::FeaturesVk & features)131 bool NeedsInputAttachmentUsage(const angle::FeaturesVk &features)
132 {
133 return features.supportsShaderFramebufferFetch.enabled ||
134 features.supportsShaderFramebufferFetchNonCoherent.enabled ||
135 features.emulateAdvancedBlendEquations.enabled;
136 }
137
InitImageHelper(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples,bool isRobustResourceInitEnabled,bool hasProtectedContent,vk::ImageHelper * imageHelper)138 angle::Result InitImageHelper(DisplayVk *displayVk,
139 EGLint width,
140 EGLint height,
141 const vk::Format &vkFormat,
142 GLint samples,
143 bool isRobustResourceInitEnabled,
144 bool hasProtectedContent,
145 vk::ImageHelper *imageHelper)
146 {
147 const angle::Format &textureFormat = vkFormat.getActualRenderableImageFormat();
148 bool isDepthOrStencilFormat = textureFormat.hasDepthOrStencilBits();
149 VkImageUsageFlags usage = isDepthOrStencilFormat ? kSurfaceVkDepthStencilImageUsageFlags
150 : kSurfaceVkColorImageUsageFlags;
151
152 RendererVk *rendererVk = displayVk->getRenderer();
153 // If shaders may be fetching from this, we need this image to be an input
154 if (NeedsInputAttachmentUsage(rendererVk->getFeatures()))
155 {
156 usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
157 }
158
159 VkExtent3D extents = {std::max(static_cast<uint32_t>(width), 1u),
160 std::max(static_cast<uint32_t>(height), 1u), 1u};
161
162 angle::FormatID renderableFormatId = vkFormat.getActualRenderableImageFormatID();
163 // For devices that don't support creating swapchain images with RGB8, emulate with RGBA8.
164 if (rendererVk->getFeatures().overrideSurfaceFormatRGB8ToRGBA8.enabled &&
165 renderableFormatId == angle::FormatID::R8G8B8_UNORM)
166 {
167 renderableFormatId = angle::FormatID::R8G8B8A8_UNORM;
168 }
169
170 VkImageCreateFlags imageCreateFlags =
171 hasProtectedContent ? VK_IMAGE_CREATE_PROTECTED_BIT : vk::kVkImageCreateFlagsNone;
172 ANGLE_TRY(imageHelper->initExternal(
173 displayVk, gl::TextureType::_2D, extents, vkFormat.getIntendedFormatID(),
174 renderableFormatId, samples, usage, imageCreateFlags, vk::ImageLayout::Undefined, nullptr,
175 gl::LevelIndex(0), 1, 1, isRobustResourceInitEnabled, hasProtectedContent));
176
177 return angle::Result::Continue;
178 }
179
MapEglColorSpaceToVkColorSpace(RendererVk * renderer,EGLenum EGLColorspace)180 VkColorSpaceKHR MapEglColorSpaceToVkColorSpace(RendererVk *renderer, EGLenum EGLColorspace)
181 {
182 switch (EGLColorspace)
183 {
184 case EGL_NONE:
185 if (renderer->getFeatures().mapUnspecifiedColorSpaceToPassThrough.enabled)
186 {
187 return VK_COLOR_SPACE_PASS_THROUGH_EXT;
188 }
189 return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
190 case EGL_GL_COLORSPACE_LINEAR:
191 case EGL_GL_COLORSPACE_SRGB_KHR:
192 return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
193 case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
194 return VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT;
195 case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
196 case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
197 return VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT;
198 case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
199 return VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
200 case EGL_GL_COLORSPACE_SCRGB_EXT:
201 return VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
202 case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
203 return VK_COLOR_SPACE_BT2020_LINEAR_EXT;
204 case EGL_GL_COLORSPACE_BT2020_PQ_EXT:
205 return VK_COLOR_SPACE_HDR10_ST2084_EXT;
206 default:
207 UNREACHABLE();
208 return VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
209 }
210 }
211
LockSurfaceImpl(DisplayVk * displayVk,vk::ImageHelper * image,vk::BufferHelper & lockBufferHelper,EGLint width,EGLint height,EGLint usageHint,bool preservePixels,uint8_t ** bufferPtrOut,EGLint * bufferPitchOut)212 angle::Result LockSurfaceImpl(DisplayVk *displayVk,
213 vk::ImageHelper *image,
214 vk::BufferHelper &lockBufferHelper,
215 EGLint width,
216 EGLint height,
217 EGLint usageHint,
218 bool preservePixels,
219 uint8_t **bufferPtrOut,
220 EGLint *bufferPitchOut)
221 {
222 const gl::InternalFormat &internalFormat =
223 gl::GetSizedInternalFormatInfo(image->getActualFormat().glInternalFormat);
224 GLuint rowStride = image->getActualFormat().pixelBytes * width;
225 VkDeviceSize bufferSize =
226 (static_cast<VkDeviceSize>(rowStride) * static_cast<VkDeviceSize>(height));
227
228 if (!lockBufferHelper.valid() || (lockBufferHelper.getSize() != bufferSize))
229 {
230 lockBufferHelper.destroy(displayVk->getRenderer());
231
232 VkBufferCreateInfo bufferCreateInfo = {};
233 bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
234 bufferCreateInfo.pNext = nullptr;
235 bufferCreateInfo.flags = 0;
236 bufferCreateInfo.size = bufferSize;
237 bufferCreateInfo.usage =
238 (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
239 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
240 bufferCreateInfo.queueFamilyIndexCount = 0;
241 bufferCreateInfo.pQueueFamilyIndices = 0;
242
243 VkMemoryPropertyFlags memoryFlags =
244 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
245
246 ANGLE_TRY(lockBufferHelper.init(displayVk, bufferCreateInfo, memoryFlags));
247
248 uint8_t *bufferPtr = nullptr;
249 ANGLE_TRY(lockBufferHelper.map(displayVk, &bufferPtr));
250 }
251
252 if (lockBufferHelper.valid())
253 {
254 if (preservePixels)
255 {
256 gl::LevelIndex sourceLevelGL(0);
257 const VkClearColorValue *clearColor;
258 if (image->removeStagedClearUpdatesAndReturnColor(sourceLevelGL, &clearColor))
259 {
260 ASSERT(!image->hasStagedUpdatesForSubresource(sourceLevelGL, 0, 1));
261 angle::Color<uint8_t> color((uint8_t)(clearColor->float32[0] * 255.0),
262 (uint8_t)(clearColor->float32[1] * 255.0),
263 (uint8_t)(clearColor->float32[2] * 255.0),
264 (uint8_t)(clearColor->float32[3] * 255.0));
265 lockBufferHelper.fillWithColor(color, internalFormat);
266 }
267 else
268 {
269 gl::Box sourceArea(0, 0, 0, width, height, 1);
270 ANGLE_TRY(image->copySurfaceImageToBuffer(displayVk, sourceLevelGL, 1, 0,
271 sourceArea, &lockBufferHelper));
272 }
273 }
274
275 *bufferPitchOut = rowStride;
276 *bufferPtrOut = lockBufferHelper.getMappedMemory();
277 }
278 return angle::Result::Continue;
279 }
280
UnlockSurfaceImpl(DisplayVk * displayVk,vk::ImageHelper * image,vk::BufferHelper & lockBufferHelper,EGLint width,EGLint height,bool preservePixels)281 angle::Result UnlockSurfaceImpl(DisplayVk *displayVk,
282 vk::ImageHelper *image,
283 vk::BufferHelper &lockBufferHelper,
284 EGLint width,
285 EGLint height,
286 bool preservePixels)
287 {
288 if (preservePixels)
289 {
290 ASSERT(image->valid());
291
292 gl::Box destArea(0, 0, 0, width, height, 1);
293 gl::LevelIndex destLevelGL(0);
294
295 ANGLE_TRY(image->copyBufferToSurfaceImage(displayVk, destLevelGL, 1, 0, destArea,
296 &lockBufferHelper));
297 }
298
299 return angle::Result::Continue;
300 }
301
302 // Converts an EGL rectangle, which is relative to the bottom-left of the surface,
303 // to a VkRectLayerKHR, relative to Vulkan framebuffer-space, with top-left origin.
304 // No rotation is done to these damage rectangles per the Vulkan spec.
305 // The bottomLeftOrigin parameter is true on Android which assumes VkRectLayerKHR to
306 // have a bottom-left origin.
ToVkRectLayer(const EGLint * eglRect,EGLint width,EGLint height,bool bottomLeftOrigin)307 VkRectLayerKHR ToVkRectLayer(const EGLint *eglRect,
308 EGLint width,
309 EGLint height,
310 bool bottomLeftOrigin)
311 {
312 VkRectLayerKHR rect;
313 // Make sure the damage rects are within swapchain bounds.
314 rect.offset.x = gl::clamp(eglRect[0], 0, width);
315
316 if (bottomLeftOrigin)
317 {
318 // EGL rectangles are already specified with a bottom-left origin, therefore the conversion
319 // is trivial as we just get its Y coordinate as it is
320 rect.offset.y = gl::clamp(eglRect[1], 0, height);
321 }
322 else
323 {
324 rect.offset.y =
325 gl::clamp(height - gl::clamp(eglRect[1], 0, height) - gl::clamp(eglRect[3], 0, height),
326 0, height);
327 }
328 rect.extent.width = gl::clamp(eglRect[2], 0, width - rect.offset.x);
329 rect.extent.height = gl::clamp(eglRect[3], 0, height - rect.offset.y);
330 rect.layer = 0;
331 return rect;
332 }
333
GetPresentModes(DisplayVk * displayVk,VkPhysicalDevice physicalDevice,VkSurfaceKHR surface,std::vector<vk::PresentMode> * outPresentModes)334 angle::Result GetPresentModes(DisplayVk *displayVk,
335 VkPhysicalDevice physicalDevice,
336 VkSurfaceKHR surface,
337 std::vector<vk::PresentMode> *outPresentModes)
338 {
339
340 uint32_t presentModeCount = 0;
341 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface,
342 &presentModeCount, nullptr));
343 ASSERT(presentModeCount > 0);
344
345 std::vector<VkPresentModeKHR> vkPresentModes(presentModeCount);
346 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(
347 physicalDevice, surface, &presentModeCount, vkPresentModes.data()));
348
349 outPresentModes->resize(presentModeCount);
350 std::transform(begin(vkPresentModes), end(vkPresentModes), begin(*outPresentModes),
351 vk::ConvertVkPresentModeToPresentMode);
352
353 return angle::Result::Continue;
354 }
355
NewSemaphore(vk::Context * context,vk::Recycler<vk::Semaphore> * semaphoreRecycler,vk::Semaphore * semaphoreOut)356 angle::Result NewSemaphore(vk::Context *context,
357 vk::Recycler<vk::Semaphore> *semaphoreRecycler,
358 vk::Semaphore *semaphoreOut)
359 {
360 if (semaphoreRecycler->empty())
361 {
362 ANGLE_VK_TRY(context, semaphoreOut->init(context->getDevice()));
363 }
364 else
365 {
366 semaphoreRecycler->fetch(semaphoreOut);
367 }
368 return angle::Result::Continue;
369 }
370
NewFence(VkDevice device,vk::Recycler<vk::Fence> * fenceRecycler,vk::Fence * fenceOut)371 VkResult NewFence(VkDevice device, vk::Recycler<vk::Fence> *fenceRecycler, vk::Fence *fenceOut)
372 {
373 VkResult result = VK_SUCCESS;
374 if (fenceRecycler->empty())
375 {
376 VkFenceCreateInfo fenceCreateInfo = {};
377 fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
378 fenceCreateInfo.flags = 0;
379 result = fenceOut->init(device, fenceCreateInfo);
380 }
381 else
382 {
383 fenceRecycler->fetch(fenceOut);
384 ASSERT(fenceOut->getStatus(device) == VK_NOT_READY);
385 }
386 return result;
387 }
388
RecycleUsedFence(VkDevice device,vk::Recycler<vk::Fence> * fenceRecycler,vk::Fence && fence)389 void RecycleUsedFence(VkDevice device, vk::Recycler<vk::Fence> *fenceRecycler, vk::Fence &&fence)
390 {
391 // Reset fence now to mitigate Intel driver bug, when accessing fence after Swapchain
392 // destruction causes crash.
393 VkResult result = fence.reset(device);
394 if (result != VK_SUCCESS)
395 {
396 ERR() << "Fence reset failed: " << result << "! Destroying fence...";
397 fence.destroy(device);
398 return;
399 }
400 fenceRecycler->recycle(std::move(fence));
401 }
402
AssociateQueueSerialWithPresentHistory(uint32_t imageIndex,QueueSerial queueSerial,std::deque<impl::ImagePresentOperation> * presentHistory)403 void AssociateQueueSerialWithPresentHistory(uint32_t imageIndex,
404 QueueSerial queueSerial,
405 std::deque<impl::ImagePresentOperation> *presentHistory)
406 {
407 // Walk the list backwards and find the entry for the given image index. That's the last
408 // present with that image. Associate the QueueSerial with that present operation.
409 for (size_t historyIndex = 0; historyIndex < presentHistory->size(); ++historyIndex)
410 {
411 impl::ImagePresentOperation &presentOperation =
412 (*presentHistory)[presentHistory->size() - historyIndex - 1];
413 // Must not use this function when VK_EXT_swapchain_maintenance1 is supported.
414 ASSERT(!presentOperation.fence.valid());
415 ASSERT(presentOperation.imageIndex != kInvalidImageIndex);
416
417 if (presentOperation.imageIndex == imageIndex)
418 {
419 ASSERT(!presentOperation.queueSerial.valid());
420 presentOperation.queueSerial = queueSerial;
421 return;
422 }
423 }
424 }
425
HasAnyOldSwapchains(const std::deque<impl::ImagePresentOperation> & presentHistory)426 bool HasAnyOldSwapchains(const std::deque<impl::ImagePresentOperation> &presentHistory)
427 {
428 // Used to validate that swapchain clean up data can only be carried by the first present
429 // operation of a swapchain. That operation is already removed from history when this call is
430 // made, so this verifies that no clean up data exists in the history.
431 for (const impl::ImagePresentOperation &presentOperation : presentHistory)
432 {
433 if (!presentOperation.oldSwapchains.empty())
434 {
435 return true;
436 }
437 }
438
439 return false;
440 }
441
IsCompatiblePresentMode(vk::PresentMode mode,VkPresentModeKHR * compatibleModes,size_t compatibleModesCount)442 bool IsCompatiblePresentMode(vk::PresentMode mode,
443 VkPresentModeKHR *compatibleModes,
444 size_t compatibleModesCount)
445 {
446 VkPresentModeKHR vkMode = vk::ConvertPresentModeToVkPresentMode(mode);
447 VkPresentModeKHR *compatibleModesEnd = compatibleModes + compatibleModesCount;
448 return std::find(compatibleModes, compatibleModesEnd, vkMode) != compatibleModesEnd;
449 }
450
TryAcquireNextImageUnlocked(VkDevice device,VkSwapchainKHR swapchain,impl::ImageAcquireOperation * acquire)451 void TryAcquireNextImageUnlocked(VkDevice device,
452 VkSwapchainKHR swapchain,
453 impl::ImageAcquireOperation *acquire)
454 {
455 // Check if need to acquire before taking the lock, in case it's unnecessary.
456 if (!acquire->needToAcquireNextSwapchainImage)
457 {
458 return;
459 }
460
461 impl::UnlockedTryAcquireData *tryAcquire = &acquire->unlockedTryAcquireData;
462 impl::UnlockedTryAcquireResult *result = &acquire->unlockedTryAcquireResult;
463
464 std::lock_guard<std::mutex> lock(tryAcquire->mutex);
465
466 // Check again under lock if acquire is still needed. Another thread might have done it before
467 // the lock is taken.
468 if (!acquire->needToAcquireNextSwapchainImage)
469 {
470 return;
471 }
472
473 result->result = VK_SUCCESS;
474 result->imageIndex = std::numeric_limits<uint32_t>::max();
475
476 // Get a semaphore to signal.
477 result->acquireSemaphore = tryAcquire->acquireImageSemaphores.front().getHandle();
478
479 // Try to acquire an image.
480 if (result->result == VK_SUCCESS)
481 {
482 result->result =
483 vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, result->acquireSemaphore,
484 VK_NULL_HANDLE, &result->imageIndex);
485 }
486
487 // Don't process the results. It will be done later when the share group lock is held.
488
489 // The contents of *result can now be used by any thread.
490 acquire->needToAcquireNextSwapchainImage = false;
491 }
492
493 // Checks whether a call to TryAcquireNextImageUnlocked has been made whose result is pending
494 // processing. This function is called when the share group lock is taken, so no need for
495 // UnlockedTryAcquireData::mutex.
NeedToProcessAcquireNextImageResult(const impl::UnlockedTryAcquireResult & result)496 bool NeedToProcessAcquireNextImageResult(const impl::UnlockedTryAcquireResult &result)
497 {
498 // TryAcquireNextImageUnlocked always creates a new acquire semaphore, use that as indication
499 // that there's something to process.
500 return result.acquireSemaphore != VK_NULL_HANDLE;
501 }
502
AreAllFencesSignaled(VkDevice device,const std::vector<vk::Fence> & fences)503 bool AreAllFencesSignaled(VkDevice device, const std::vector<vk::Fence> &fences)
504 {
505 for (const vk::Fence &fence : fences)
506 {
507 if (fence.getStatus(device) != VK_SUCCESS)
508 {
509 return false;
510 }
511 }
512 return true;
513 }
514 } // namespace
515
SurfaceVk(const egl::SurfaceState & surfaceState)516 SurfaceVk::SurfaceVk(const egl::SurfaceState &surfaceState) : SurfaceImpl(surfaceState) {}
517
~SurfaceVk()518 SurfaceVk::~SurfaceVk() {}
519
destroy(const egl::Display * display)520 void SurfaceVk::destroy(const egl::Display *display)
521 {
522 DisplayVk *displayVk = vk::GetImpl(display);
523 RendererVk *renderer = displayVk->getRenderer();
524
525 mColorRenderTarget.destroy(renderer);
526 mDepthStencilRenderTarget.destroy(renderer);
527 }
528
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)529 angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
530 GLenum binding,
531 const gl::ImageIndex &imageIndex,
532 GLsizei samples,
533 FramebufferAttachmentRenderTarget **rtOut)
534 {
535 ASSERT(samples == 0);
536
537 if (binding == GL_BACK)
538 {
539 *rtOut = &mColorRenderTarget;
540 }
541 else
542 {
543 ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
544 *rtOut = &mDepthStencilRenderTarget;
545 }
546
547 return angle::Result::Continue;
548 }
549
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)550 void SurfaceVk::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
551 {
552 // Forward the notification to parent class that the staging buffer changed.
553 onStateChange(angle::SubjectMessage::SubjectChanged);
554 }
555
AttachmentImage(SurfaceVk * surfaceVk)556 OffscreenSurfaceVk::AttachmentImage::AttachmentImage(SurfaceVk *surfaceVk)
557 : imageObserverBinding(surfaceVk, kAnySurfaceImageSubjectIndex)
558 {
559 imageObserverBinding.bind(&image);
560 }
561
562 OffscreenSurfaceVk::AttachmentImage::~AttachmentImage() = default;
563
initialize(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples,bool isRobustResourceInitEnabled,bool hasProtectedContent)564 angle::Result OffscreenSurfaceVk::AttachmentImage::initialize(DisplayVk *displayVk,
565 EGLint width,
566 EGLint height,
567 const vk::Format &vkFormat,
568 GLint samples,
569 bool isRobustResourceInitEnabled,
570 bool hasProtectedContent)
571 {
572 ANGLE_TRY(InitImageHelper(displayVk, width, height, vkFormat, samples,
573 isRobustResourceInitEnabled, hasProtectedContent, &image));
574
575 RendererVk *renderer = displayVk->getRenderer();
576 VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
577 if (hasProtectedContent)
578 {
579 flags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
580 }
581 ANGLE_TRY(image.initMemory(displayVk, hasProtectedContent, renderer->getMemoryProperties(),
582 flags, vk::MemoryAllocationType::OffscreenSurfaceAttachmentImage));
583
584 imageViews.init(renderer);
585
586 return angle::Result::Continue;
587 }
588
destroy(const egl::Display * display)589 void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
590 {
591 DisplayVk *displayVk = vk::GetImpl(display);
592 RendererVk *renderer = displayVk->getRenderer();
593 // Front end must ensure all usage has been submitted.
594 imageViews.release(renderer, image.getResourceUse());
595 image.releaseImage(renderer);
596 image.releaseStagedUpdates(renderer);
597 }
598
OffscreenSurfaceVk(const egl::SurfaceState & surfaceState,RendererVk * renderer)599 OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState, RendererVk *renderer)
600 : SurfaceVk(surfaceState),
601 mWidth(mState.attributes.getAsInt(EGL_WIDTH, 0)),
602 mHeight(mState.attributes.getAsInt(EGL_HEIGHT, 0)),
603 mColorAttachment(this),
604 mDepthStencilAttachment(this)
605 {
606 mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr, nullptr,
607 {}, gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
608 mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
609 &mDepthStencilAttachment.imageViews, nullptr, nullptr, {},
610 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
611 }
612
~OffscreenSurfaceVk()613 OffscreenSurfaceVk::~OffscreenSurfaceVk() {}
614
initialize(const egl::Display * display)615 egl::Error OffscreenSurfaceVk::initialize(const egl::Display *display)
616 {
617 DisplayVk *displayVk = vk::GetImpl(display);
618 angle::Result result = initializeImpl(displayVk);
619 return angle::ToEGL(result, EGL_BAD_SURFACE);
620 }
621
initializeImpl(DisplayVk * displayVk)622 angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
623 {
624 RendererVk *renderer = displayVk->getRenderer();
625 const egl::Config *config = mState.config;
626
627 renderer->reloadVolkIfNeeded();
628
629 GLint samples = GetSampleCount(mState.config);
630 ANGLE_VK_CHECK(displayVk, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
631
632 bool robustInit = mState.isRobustResourceInitEnabled();
633
634 if (config->renderTargetFormat != GL_NONE)
635 {
636 ANGLE_TRY(mColorAttachment.initialize(displayVk, mWidth, mHeight,
637 renderer->getFormat(config->renderTargetFormat),
638 samples, robustInit, mState.hasProtectedContent()));
639 mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr,
640 nullptr, {}, gl::LevelIndex(0), 0, 1,
641 RenderTargetTransience::Default);
642 }
643
644 if (config->depthStencilFormat != GL_NONE)
645 {
646 ANGLE_TRY(mDepthStencilAttachment.initialize(
647 displayVk, mWidth, mHeight, renderer->getFormat(config->depthStencilFormat), samples,
648 robustInit, mState.hasProtectedContent()));
649 mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
650 &mDepthStencilAttachment.imageViews, nullptr, nullptr, {},
651 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
652 }
653
654 return angle::Result::Continue;
655 }
656
destroy(const egl::Display * display)657 void OffscreenSurfaceVk::destroy(const egl::Display *display)
658 {
659 mColorAttachment.destroy(display);
660 mDepthStencilAttachment.destroy(display);
661
662 if (mLockBufferHelper.valid())
663 {
664 mLockBufferHelper.destroy(vk::GetImpl(display)->getRenderer());
665 }
666
667 // Call parent class to destroy any resources parent owns.
668 SurfaceVk::destroy(display);
669 }
670
unMakeCurrent(const gl::Context * context)671 egl::Error OffscreenSurfaceVk::unMakeCurrent(const gl::Context *context)
672 {
673 ContextVk *contextVk = vk::GetImpl(context);
674
675 angle::Result result = contextVk->onSurfaceUnMakeCurrent(this);
676
677 return angle::ToEGL(result, EGL_BAD_CURRENT_SURFACE);
678 }
679
swap(const gl::Context * context)680 egl::Error OffscreenSurfaceVk::swap(const gl::Context *context)
681 {
682 return egl::NoError();
683 }
684
postSubBuffer(const gl::Context *,EGLint,EGLint,EGLint,EGLint)685 egl::Error OffscreenSurfaceVk::postSubBuffer(const gl::Context * /*context*/,
686 EGLint /*x*/,
687 EGLint /*y*/,
688 EGLint /*width*/,
689 EGLint /*height*/)
690 {
691 return egl::NoError();
692 }
693
querySurfacePointerANGLE(EGLint,void **)694 egl::Error OffscreenSurfaceVk::querySurfacePointerANGLE(EGLint /*attribute*/, void ** /*value*/)
695 {
696 UNREACHABLE();
697 return egl::EglBadCurrentSurface();
698 }
699
bindTexImage(const gl::Context *,gl::Texture *,EGLint)700 egl::Error OffscreenSurfaceVk::bindTexImage(const gl::Context * /*context*/,
701 gl::Texture * /*texture*/,
702 EGLint /*buffer*/)
703 {
704 return egl::NoError();
705 }
706
releaseTexImage(const gl::Context *,EGLint)707 egl::Error OffscreenSurfaceVk::releaseTexImage(const gl::Context * /*context*/, EGLint /*buffer*/)
708 {
709 return egl::NoError();
710 }
711
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)712 egl::Error OffscreenSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
713 EGLuint64KHR * /*msc*/,
714 EGLuint64KHR * /*sbc*/)
715 {
716 UNIMPLEMENTED();
717 return egl::EglBadAccess();
718 }
719
getMscRate(EGLint *,EGLint *)720 egl::Error OffscreenSurfaceVk::getMscRate(EGLint * /*numerator*/, EGLint * /*denominator*/)
721 {
722 UNIMPLEMENTED();
723 return egl::EglBadAccess();
724 }
725
setSwapInterval(EGLint)726 void OffscreenSurfaceVk::setSwapInterval(EGLint /*interval*/) {}
727
getWidth() const728 EGLint OffscreenSurfaceVk::getWidth() const
729 {
730 return mWidth;
731 }
732
getHeight() const733 EGLint OffscreenSurfaceVk::getHeight() const
734 {
735 return mHeight;
736 }
737
isPostSubBufferSupported() const738 EGLint OffscreenSurfaceVk::isPostSubBufferSupported() const
739 {
740 return EGL_FALSE;
741 }
742
getSwapBehavior() const743 EGLint OffscreenSurfaceVk::getSwapBehavior() const
744 {
745 return EGL_BUFFER_DESTROYED;
746 }
747
initializeContents(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex)748 angle::Result OffscreenSurfaceVk::initializeContents(const gl::Context *context,
749 GLenum binding,
750 const gl::ImageIndex &imageIndex)
751 {
752 ContextVk *contextVk = vk::GetImpl(context);
753
754 switch (binding)
755 {
756 case GL_BACK:
757 ASSERT(mColorAttachment.image.valid());
758 mColorAttachment.image.stageRobustResourceClear(imageIndex);
759 ANGLE_TRY(mColorAttachment.image.flushAllStagedUpdates(contextVk));
760 break;
761
762 case GL_DEPTH:
763 case GL_STENCIL:
764 ASSERT(mDepthStencilAttachment.image.valid());
765 mDepthStencilAttachment.image.stageRobustResourceClear(imageIndex);
766 ANGLE_TRY(mDepthStencilAttachment.image.flushAllStagedUpdates(contextVk));
767 break;
768
769 default:
770 UNREACHABLE();
771 break;
772 }
773 return angle::Result::Continue;
774 }
775
getColorAttachmentImage()776 vk::ImageHelper *OffscreenSurfaceVk::getColorAttachmentImage()
777 {
778 return &mColorAttachment.image;
779 }
780
lockSurface(const egl::Display * display,EGLint usageHint,bool preservePixels,uint8_t ** bufferPtrOut,EGLint * bufferPitchOut)781 egl::Error OffscreenSurfaceVk::lockSurface(const egl::Display *display,
782 EGLint usageHint,
783 bool preservePixels,
784 uint8_t **bufferPtrOut,
785 EGLint *bufferPitchOut)
786 {
787 ANGLE_TRACE_EVENT0("gpu.angle", "OffscreenSurfaceVk::lockSurface");
788
789 vk::ImageHelper *image = &mColorAttachment.image;
790 ASSERT(image->valid());
791
792 angle::Result result =
793 LockSurfaceImpl(vk::GetImpl(display), image, mLockBufferHelper, getWidth(), getHeight(),
794 usageHint, preservePixels, bufferPtrOut, bufferPitchOut);
795 return angle::ToEGL(result, EGL_BAD_ACCESS);
796 }
797
unlockSurface(const egl::Display * display,bool preservePixels)798 egl::Error OffscreenSurfaceVk::unlockSurface(const egl::Display *display, bool preservePixels)
799 {
800 vk::ImageHelper *image = &mColorAttachment.image;
801 ASSERT(image->valid());
802 ASSERT(mLockBufferHelper.valid());
803
804 return angle::ToEGL(UnlockSurfaceImpl(vk::GetImpl(display), image, mLockBufferHelper,
805 getWidth(), getHeight(), preservePixels),
806 EGL_BAD_ACCESS);
807 }
808
origin() const809 EGLint OffscreenSurfaceVk::origin() const
810 {
811 return EGL_UPPER_LEFT_KHR;
812 }
813
attachToFramebuffer(const gl::Context * context,gl::Framebuffer * framebuffer)814 egl::Error OffscreenSurfaceVk::attachToFramebuffer(const gl::Context *context,
815 gl::Framebuffer *framebuffer)
816 {
817 return egl::NoError();
818 }
819
detachFromFramebuffer(const gl::Context * context,gl::Framebuffer * framebuffer)820 egl::Error OffscreenSurfaceVk::detachFromFramebuffer(const gl::Context *context,
821 gl::Framebuffer *framebuffer)
822 {
823 return egl::NoError();
824 }
825
826 namespace impl
827 {
828 SwapchainCleanupData::SwapchainCleanupData() = default;
~SwapchainCleanupData()829 SwapchainCleanupData::~SwapchainCleanupData()
830 {
831 ASSERT(swapchain == VK_NULL_HANDLE);
832 ASSERT(fences.empty());
833 ASSERT(semaphores.empty());
834 }
835
SwapchainCleanupData(SwapchainCleanupData && other)836 SwapchainCleanupData::SwapchainCleanupData(SwapchainCleanupData &&other)
837 : swapchain(other.swapchain),
838 fences(std::move(other.fences)),
839 semaphores(std::move(other.semaphores))
840 {
841 other.swapchain = VK_NULL_HANDLE;
842 }
843
getFencesStatus(VkDevice device) const844 VkResult SwapchainCleanupData::getFencesStatus(VkDevice device) const
845 {
846 // From VkSwapchainPresentFenceInfoEXT documentation:
847 // Fences associated with presentations to the same swapchain on the same VkQueue must be
848 // signaled in the same order as the present operations.
849 ASSERT(!fences.empty());
850 VkResult result = fences.back().getStatus(device);
851 ASSERT(result != VK_SUCCESS || AreAllFencesSignaled(device, fences));
852 return result;
853 }
854
waitFences(VkDevice device,uint64_t timeout) const855 void SwapchainCleanupData::waitFences(VkDevice device, uint64_t timeout) const
856 {
857 if (!fences.empty())
858 {
859 VkResult result = fences.back().wait(device, timeout);
860 ASSERT(result != VK_SUCCESS || AreAllFencesSignaled(device, fences));
861 }
862 }
863
destroy(VkDevice device,vk::Recycler<vk::Fence> * fenceRecycler,vk::Recycler<vk::Semaphore> * semaphoreRecycler)864 void SwapchainCleanupData::destroy(VkDevice device,
865 vk::Recycler<vk::Fence> *fenceRecycler,
866 vk::Recycler<vk::Semaphore> *semaphoreRecycler)
867 {
868 for (vk::Fence &fence : fences)
869 {
870 RecycleUsedFence(device, fenceRecycler, std::move(fence));
871 }
872 fences.clear();
873
874 for (vk::Semaphore &semaphore : semaphores)
875 {
876 semaphoreRecycler->recycle(std::move(semaphore));
877 }
878 semaphores.clear();
879
880 if (swapchain)
881 {
882 vkDestroySwapchainKHR(device, swapchain, nullptr);
883 swapchain = VK_NULL_HANDLE;
884 }
885 }
886
ImagePresentOperation()887 ImagePresentOperation::ImagePresentOperation() : imageIndex(kInvalidImageIndex) {}
~ImagePresentOperation()888 ImagePresentOperation::~ImagePresentOperation()
889 {
890 ASSERT(!fence.valid());
891 ASSERT(!semaphore.valid());
892 ASSERT(oldSwapchains.empty());
893 }
894
ImagePresentOperation(ImagePresentOperation && other)895 ImagePresentOperation::ImagePresentOperation(ImagePresentOperation &&other)
896 : fence(std::move(other.fence)),
897 semaphore(std::move(other.semaphore)),
898 imageIndex(other.imageIndex),
899 queueSerial(other.queueSerial),
900 oldSwapchains(std::move(other.oldSwapchains))
901 {}
902
operator =(ImagePresentOperation && other)903 ImagePresentOperation &ImagePresentOperation::operator=(ImagePresentOperation &&other)
904 {
905 std::swap(fence, other.fence);
906 std::swap(semaphore, other.semaphore);
907 std::swap(imageIndex, other.imageIndex);
908 std::swap(queueSerial, other.queueSerial);
909 std::swap(oldSwapchains, other.oldSwapchains);
910 return *this;
911 }
912
destroy(VkDevice device,vk::Recycler<vk::Fence> * fenceRecycler,vk::Recycler<vk::Semaphore> * semaphoreRecycler)913 void ImagePresentOperation::destroy(VkDevice device,
914 vk::Recycler<vk::Fence> *fenceRecycler,
915 vk::Recycler<vk::Semaphore> *semaphoreRecycler)
916 {
917 // fence is only used when VK_EXT_swapchain_maintenance1 is supported.
918 if (fence.valid())
919 {
920 RecycleUsedFence(device, fenceRecycler, std::move(fence));
921 }
922
923 ASSERT(semaphore.valid());
924 semaphoreRecycler->recycle(std::move(semaphore));
925
926 // Destroy old swapchains (relevant only when VK_EXT_swapchain_maintenance1 is not supported).
927 for (SwapchainCleanupData &oldSwapchain : oldSwapchains)
928 {
929 oldSwapchain.destroy(device, fenceRecycler, semaphoreRecycler);
930 }
931 oldSwapchains.clear();
932 }
933
934 SwapchainImage::SwapchainImage() = default;
935 SwapchainImage::~SwapchainImage() = default;
936
SwapchainImage(SwapchainImage && other)937 SwapchainImage::SwapchainImage(SwapchainImage &&other)
938 : image(std::move(other.image)),
939 imageViews(std::move(other.imageViews)),
940 framebuffer(std::move(other.framebuffer)),
941 fetchFramebuffer(std::move(other.fetchFramebuffer)),
942 framebufferResolveMS(std::move(other.framebufferResolveMS)),
943 frameNumber(other.frameNumber)
944 {}
945
ImageAcquireOperation()946 ImageAcquireOperation::ImageAcquireOperation() : needToAcquireNextSwapchainImage(false) {}
947 } // namespace impl
948
949 using namespace impl;
950
WindowSurfaceVk(const egl::SurfaceState & surfaceState,EGLNativeWindowType window)951 WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativeWindowType window)
952 : SurfaceVk(surfaceState),
953 mNativeWindowType(window),
954 mSurface(VK_NULL_HANDLE),
955 mSupportsProtectedSwapchain(false),
956 mSwapchain(VK_NULL_HANDLE),
957 mSwapchainPresentMode(vk::PresentMode::FifoKHR),
958 mDesiredSwapchainPresentMode(vk::PresentMode::FifoKHR),
959 mMinImageCount(0),
960 mPreTransform(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR),
961 mEmulatedPreTransform(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR),
962 mCompositeAlpha(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR),
963 mCurrentSwapchainImageIndex(0),
964 mDepthStencilImageBinding(this, kAnySurfaceImageSubjectIndex),
965 mColorImageMSBinding(this, kAnySurfaceImageSubjectIndex),
966 mFrameCount(1),
967 mBufferAgeQueryFrameNumber(0)
968 {
969 // Initialize the color render target with the multisampled targets. If not multisampled, the
970 // render target will be updated to refer to a swapchain image on every acquire.
971 mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, nullptr, nullptr, {},
972 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
973 mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, nullptr, nullptr,
974 {}, gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
975 mDepthStencilImageBinding.bind(&mDepthStencilImage);
976 mColorImageMSBinding.bind(&mColorImageMS);
977 mSwapchainStatus.isPending = false;
978 }
979
~WindowSurfaceVk()980 WindowSurfaceVk::~WindowSurfaceVk()
981 {
982 ASSERT(mSurface == VK_NULL_HANDLE);
983 ASSERT(mSwapchain == VK_NULL_HANDLE);
984 }
985
destroy(const egl::Display * display)986 void WindowSurfaceVk::destroy(const egl::Display *display)
987 {
988 DisplayVk *displayVk = vk::GetImpl(display);
989 RendererVk *renderer = displayVk->getRenderer();
990 VkDevice device = renderer->getDevice();
991 VkInstance instance = renderer->getInstance();
992
993 // flush the pipe.
994 (void)renderer->waitForPresentToBeSubmitted(&mSwapchainStatus);
995 (void)finish(displayVk);
996
997 if (!needsAcquireImageOrProcessResult() && !mSwapchainImages.empty())
998 {
999 // swapchain image doesn't own ANI semaphore. Release ANI semaphore from image so that it
1000 // can destroy cleanly without hitting assertion..
1001 // Only single swapchain image may have semaphore associated.
1002 ASSERT(mCurrentSwapchainImageIndex < mSwapchainImages.size());
1003 mSwapchainImages[mCurrentSwapchainImageIndex].image->resetAcquireNextImageSemaphore();
1004 }
1005
1006 if (mLockBufferHelper.valid())
1007 {
1008 mLockBufferHelper.destroy(renderer);
1009 }
1010
1011 for (impl::ImagePresentOperation &presentOperation : mPresentHistory)
1012 {
1013 if (presentOperation.fence.valid())
1014 {
1015 (void)presentOperation.fence.wait(device, renderer->getMaxFenceWaitTimeNs());
1016 }
1017 presentOperation.destroy(device, &mPresentFenceRecycler, &mPresentSemaphoreRecycler);
1018 }
1019 mPresentHistory.clear();
1020
1021 destroySwapChainImages(displayVk);
1022
1023 if (mSwapchain)
1024 {
1025 vkDestroySwapchainKHR(device, mSwapchain, nullptr);
1026 mSwapchain = VK_NULL_HANDLE;
1027 }
1028
1029 for (vk::Semaphore &semaphore : mAcquireOperation.unlockedTryAcquireData.acquireImageSemaphores)
1030 {
1031 semaphore.destroy(device);
1032 }
1033 for (SwapchainCleanupData &oldSwapchain : mOldSwapchains)
1034 {
1035 oldSwapchain.waitFences(device, renderer->getMaxFenceWaitTimeNs());
1036 oldSwapchain.destroy(device, &mPresentFenceRecycler, &mPresentSemaphoreRecycler);
1037 }
1038 mOldSwapchains.clear();
1039
1040 mPresentSemaphoreRecycler.destroy(device);
1041 mPresentFenceRecycler.destroy(device);
1042
1043 // Call parent class to destroy any resources parent owns.
1044 SurfaceVk::destroy(display);
1045
1046 // Destroy the surface without holding the EGL lock. This works around a specific deadlock
1047 // in Android. On this platform:
1048 //
1049 // - For EGL applications, parts of surface creation and destruction are handled by the
1050 // platform, and parts of it are done by the native EGL driver. Namely, on surface
1051 // destruction, native_window_api_disconnect is called outside the EGL driver.
1052 // - For Vulkan applications, vkDestroySurfaceKHR takes full responsibility for destroying
1053 // the surface, including calling native_window_api_disconnect.
1054 //
1055 // Unfortunately, native_window_api_disconnect may use EGL sync objects and can lead to
1056 // calling into the EGL driver. For ANGLE, this is particularly problematic because it is
1057 // simultaneously a Vulkan application and the EGL driver, causing `vkDestroySurfaceKHR` to
1058 // call back into ANGLE and attempt to reacquire the EGL lock.
1059 //
1060 // Since there are no users of the surface when calling vkDestroySurfaceKHR, it is safe for
1061 // ANGLE to destroy it without holding the EGL lock, effectively simulating the situation
1062 // for EGL applications, where native_window_api_disconnect is called after the EGL driver
1063 // has returned.
1064 if (mSurface)
1065 {
1066 egl::Display::GetCurrentThreadUnlockedTailCall()->add([surface = mSurface, instance]() {
1067 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::destroy:vkDestroySurfaceKHR");
1068 vkDestroySurfaceKHR(instance, surface, nullptr);
1069 });
1070 mSurface = VK_NULL_HANDLE;
1071 }
1072 }
1073
initialize(const egl::Display * display)1074 egl::Error WindowSurfaceVk::initialize(const egl::Display *display)
1075 {
1076 DisplayVk *displayVk = vk::GetImpl(display);
1077 angle::Result result = initializeImpl(displayVk);
1078 if (result == angle::Result::Incomplete)
1079 {
1080 return angle::ToEGL(result, EGL_BAD_MATCH);
1081 }
1082 else
1083 {
1084 return angle::ToEGL(result, EGL_BAD_SURFACE);
1085 }
1086 }
1087
unMakeCurrent(const gl::Context * context)1088 egl::Error WindowSurfaceVk::unMakeCurrent(const gl::Context *context)
1089 {
1090 ContextVk *contextVk = vk::GetImpl(context);
1091
1092 angle::Result result = contextVk->onSurfaceUnMakeCurrent(this);
1093 // Even though all swap chain images are tracked individually, the semaphores are not
1094 // tracked by ResourceUse. This propagates context's queue serial to surface when it
1095 // detaches from context so that surface will always wait until context is finished.
1096 mUse.merge(contextVk->getSubmittedResourceUse());
1097
1098 return angle::ToEGL(result, EGL_BAD_CURRENT_SURFACE);
1099 }
1100
initializeImpl(DisplayVk * displayVk)1101 angle::Result WindowSurfaceVk::initializeImpl(DisplayVk *displayVk)
1102 {
1103 RendererVk *renderer = displayVk->getRenderer();
1104
1105 mColorImageMSViews.init(renderer);
1106 mDepthStencilImageViews.init(renderer);
1107
1108 renderer->reloadVolkIfNeeded();
1109
1110 gl::Extents windowSize;
1111 ANGLE_TRY(createSurfaceVk(displayVk, &windowSize));
1112
1113 uint32_t presentQueue = 0;
1114 ANGLE_TRY(renderer->selectPresentQueueForSurface(displayVk, mSurface, &presentQueue));
1115 ANGLE_UNUSED_VARIABLE(presentQueue);
1116
1117 const VkPhysicalDevice &physicalDevice = renderer->getPhysicalDevice();
1118
1119 if (renderer->getFeatures().supportsSurfaceCapabilities2Extension.enabled)
1120 {
1121 VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo2 = {};
1122 surfaceInfo2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
1123 surfaceInfo2.surface = mSurface;
1124
1125 VkSurfaceCapabilities2KHR surfaceCaps2 = {};
1126 surfaceCaps2.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
1127
1128 VkSharedPresentSurfaceCapabilitiesKHR sharedPresentSurfaceCaps = {};
1129 if (renderer->getFeatures().supportsSharedPresentableImageExtension.enabled)
1130 {
1131 sharedPresentSurfaceCaps.sType =
1132 VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR;
1133 sharedPresentSurfaceCaps.sharedPresentSupportedUsageFlags =
1134 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1135
1136 vk::AddToPNextChain(&surfaceCaps2, &sharedPresentSurfaceCaps);
1137 }
1138
1139 VkSurfaceProtectedCapabilitiesKHR surfaceProtectedCaps = {};
1140 if (renderer->getFeatures().supportsSurfaceProtectedCapabilitiesExtension.enabled)
1141 {
1142 surfaceProtectedCaps.sType = VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR;
1143
1144 vk::AddToPNextChain(&surfaceCaps2, &surfaceProtectedCaps);
1145 }
1146
1147 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceCapabilities2KHR(
1148 physicalDevice, &surfaceInfo2, &surfaceCaps2));
1149
1150 mSurfaceCaps = surfaceCaps2.surfaceCapabilities;
1151 mSupportsProtectedSwapchain = surfaceProtectedCaps.supportsProtected;
1152 }
1153 else
1154 {
1155 ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface,
1156 &mSurfaceCaps));
1157 }
1158
1159 if (IsAndroid())
1160 {
1161 mSupportsProtectedSwapchain = true;
1162 }
1163
1164 ANGLE_VK_CHECK(displayVk, (mState.hasProtectedContent() ? mSupportsProtectedSwapchain : true),
1165 VK_ERROR_FEATURE_NOT_PRESENT);
1166
1167 // Adjust width and height to the swapchain if necessary.
1168 uint32_t width = mSurfaceCaps.currentExtent.width;
1169 uint32_t height = mSurfaceCaps.currentExtent.height;
1170
1171 ANGLE_VK_CHECK(displayVk,
1172 (mSurfaceCaps.supportedUsageFlags & kSurfaceVkColorImageUsageFlags) ==
1173 kSurfaceVkColorImageUsageFlags,
1174 VK_ERROR_INITIALIZATION_FAILED);
1175
1176 EGLAttrib attribWidth = mState.attributes.get(EGL_WIDTH, 0);
1177 EGLAttrib attribHeight = mState.attributes.get(EGL_HEIGHT, 0);
1178
1179 if (mSurfaceCaps.currentExtent.width == kSurfaceSizedBySwapchain)
1180 {
1181 ASSERT(mSurfaceCaps.currentExtent.height == kSurfaceSizedBySwapchain);
1182
1183 width = (attribWidth != 0) ? static_cast<uint32_t>(attribWidth) : windowSize.width;
1184 height = (attribHeight != 0) ? static_cast<uint32_t>(attribHeight) : windowSize.height;
1185 }
1186
1187 gl::Extents extents(static_cast<int>(width), static_cast<int>(height), 1);
1188
1189 // Introduction to Android rotation and pre-rotation:
1190 //
1191 // Android devices have one native orientation, but a window may be displayed in a different
1192 // orientation. This results in the window being "rotated" relative to the native orientation.
1193 // For example, the native orientation of a Pixel 4 is portrait (i.e. height > width).
1194 // However, many games want to be landscape (i.e. width > height). Some applications will
1195 // adapt to whatever orientation the user places the device in (e.g. auto-rotation).
1196 //
1197 // A convention is used within ANGLE of referring to the "rotated" and "non-rotated" aspects of
1198 // a topic (e.g. a window's extents, a scissor, a viewport):
1199 //
1200 // - Non-rotated. This refers to the way that the application views the window. Rotation is
1201 // an Android concept, not a GL concept. An application may view its window as landscape or
1202 // portrait, but not necessarily view its window as being rotated. For example, an
1203 // application will set a scissor and viewport in a manner consistent with its view of the
1204 // window size (i.e. a non-rotated manner).
1205 //
1206 // - Rotated. This refers to the way that Vulkan views the window. If the window's
1207 // orientation is the same as the native orientation, the rotated view will happen to be
1208 // equivalent to the non-rotated view, but regardless of the window's orientation, ANGLE uses
1209 // the "rotated" term as whatever the Vulkan view of the window is.
1210 //
1211 // Most of ANGLE is designed to work with the non-rotated view of the window. This is
1212 // certainly true of the ANGLE front-end. It is also true of most of the Vulkan back-end,
1213 // which is still translating GL to Vulkan. Only part of the Vulkan back-end needs to
1214 // communicate directly to Vulkan in terms of the window's rotation. For example, the viewport
1215 // and scissor calculations are done with non-rotated values; and then the final values are
1216 // rotated.
1217 //
1218 // ANGLE learns about the window's rotation from mSurfaceCaps.currentTransform. If
1219 // currentTransform is non-IDENTITY, ANGLE must "pre-rotate" various aspects of its work
1220 // (e.g. rotate vertices in the vertex shaders, change scissor, viewport, and render-pass
1221 // renderArea). The swapchain's transform is given the value of mSurfaceCaps.currentTransform.
1222 // That prevents SurfaceFlinger from doing a rotation blit for every frame (which is costly in
1223 // terms of performance and power).
1224 //
1225 // When a window is rotated 90 or 270 degrees, the aspect ratio changes. The width and height
1226 // are swapped. The x/y and width/height of various values in ANGLE must also be swapped
1227 // before communicating the values to Vulkan.
1228 if (renderer->getFeatures().enablePreRotateSurfaces.enabled)
1229 {
1230 // Use the surface's transform. For many platforms, this will always be identity (ANGLE
1231 // does not need to do any pre-rotation). However, when mSurfaceCaps.currentTransform is
1232 // not identity, the device has been rotated away from its natural orientation. In such a
1233 // case, ANGLE must rotate all rendering in order to avoid the compositor
1234 // (e.g. SurfaceFlinger on Android) performing an additional rotation blit. In addition,
1235 // ANGLE must create the swapchain with VkSwapchainCreateInfoKHR::preTransform set to the
1236 // value of mSurfaceCaps.currentTransform.
1237 mPreTransform = mSurfaceCaps.currentTransform;
1238 }
1239 else
1240 {
1241 // Default to identity transform.
1242 mPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
1243
1244 if ((mSurfaceCaps.supportedTransforms & mPreTransform) == 0)
1245 {
1246 mPreTransform = mSurfaceCaps.currentTransform;
1247 }
1248 }
1249
1250 // Set emulated pre-transform if any emulated prerotation features are set.
1251 if (renderer->getFeatures().emulatedPrerotation90.enabled)
1252 {
1253 mEmulatedPreTransform = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR;
1254 }
1255 else if (renderer->getFeatures().emulatedPrerotation180.enabled)
1256 {
1257 mEmulatedPreTransform = VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR;
1258 }
1259 else if (renderer->getFeatures().emulatedPrerotation270.enabled)
1260 {
1261 mEmulatedPreTransform = VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
1262 }
1263
1264 // If prerotation is emulated, the window is physically rotated. With real prerotation, the
1265 // surface reports the rotated sizes. With emulated prerotation however, the surface reports
1266 // the actual window sizes. Adjust the window extents to match what real prerotation would have
1267 // reported.
1268 if (Is90DegreeRotation(mEmulatedPreTransform))
1269 {
1270 ASSERT(mPreTransform == VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR);
1271 std::swap(extents.width, extents.height);
1272 }
1273
1274 ANGLE_TRY(GetPresentModes(displayVk, physicalDevice, mSurface, &mPresentModes));
1275
1276 // Select appropriate present mode based on vsync parameter. Default to 1 (FIFO), though it
1277 // will get clamped to the min/max values specified at display creation time.
1278 setSwapInterval(mState.getPreferredSwapInterval());
1279
1280 // Ensure that the format and colorspace pair is supported.
1281 const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
1282 VkFormat nativeFormat = format.getActualRenderableImageVkFormat();
1283 // For devices that don't support creating swapchain images with RGB8, emulate with RGBA8.
1284 if (renderer->getFeatures().overrideSurfaceFormatRGB8ToRGBA8.enabled &&
1285 nativeFormat == VK_FORMAT_R8G8B8_UNORM)
1286 {
1287 nativeFormat = VK_FORMAT_R8G8B8A8_UNORM;
1288 }
1289 VkColorSpaceKHR colorSpace = MapEglColorSpaceToVkColorSpace(
1290 renderer, static_cast<EGLenum>(mState.attributes.get(EGL_GL_COLORSPACE, EGL_NONE)));
1291 if (!displayVk->isSurfaceFormatColorspacePairSupported(mSurface, nativeFormat, colorSpace))
1292 {
1293 return angle::Result::Incomplete;
1294 }
1295
1296 mCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1297 if ((mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) == 0)
1298 {
1299 mCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
1300 }
1301 ANGLE_VK_CHECK(displayVk, (mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) != 0,
1302 VK_ERROR_INITIALIZATION_FAILED);
1303
1304 // Single buffer, if supported
1305 if ((mState.attributes.getAsInt(EGL_RENDER_BUFFER, EGL_BACK_BUFFER) == EGL_SINGLE_BUFFER) &&
1306 supportsPresentMode(vk::PresentMode::SharedDemandRefreshKHR))
1307 {
1308 std::vector<vk::PresentMode> presentModes = {vk::PresentMode::SharedDemandRefreshKHR};
1309 mDesiredSwapchainPresentMode = GetDesiredPresentMode(presentModes, 0);
1310 }
1311
1312 ANGLE_TRY(createSwapChain(displayVk, extents, VK_NULL_HANDLE));
1313
1314 // Create the semaphores that will be used for vkAcquireNextImageKHR.
1315 for (vk::Semaphore &semaphore : mAcquireOperation.unlockedTryAcquireData.acquireImageSemaphores)
1316 {
1317 ANGLE_VK_TRY(displayVk, semaphore.init(displayVk->getDevice()));
1318 }
1319
1320 VkResult vkResult = acquireNextSwapchainImage(displayVk);
1321 ASSERT(vkResult != VK_SUBOPTIMAL_KHR);
1322 ANGLE_VK_TRY(displayVk, vkResult);
1323
1324 return angle::Result::Continue;
1325 }
1326
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,GLsizei samples,FramebufferAttachmentRenderTarget ** rtOut)1327 angle::Result WindowSurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
1328 GLenum binding,
1329 const gl::ImageIndex &imageIndex,
1330 GLsizei samples,
1331 FramebufferAttachmentRenderTarget **rtOut)
1332 {
1333 if (needsAcquireImageOrProcessResult())
1334 {
1335 // Acquire the next image (previously deferred) before it is drawn to or read from.
1336 ContextVk *contextVk = vk::GetImpl(context);
1337 ANGLE_VK_TRACE_EVENT_AND_MARKER(contextVk, "First Swap Image Use");
1338 ANGLE_TRY(doDeferredAcquireNextImage(context, false));
1339 }
1340 return SurfaceVk::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut);
1341 }
1342
recreateSwapchain(ContextVk * contextVk,const gl::Extents & extents)1343 angle::Result WindowSurfaceVk::recreateSwapchain(ContextVk *contextVk, const gl::Extents &extents)
1344 {
1345 ASSERT(!mSwapchainStatus.isPending);
1346
1347 // If no present operation has been done on the new swapchain, it can be destroyed right away.
1348 // This means that a new swapchain was created, but before any of its images were presented,
1349 // it's asked to be recreated. This can happen for example if vkQueuePresentKHR returns
1350 // OUT_OF_DATE, the swapchain is recreated and the following vkAcquireNextImageKHR again
1351 // returns OUT_OF_DATE. Otherwise, keep the current swapchain as the old swapchain to be
1352 // scheduled for destruction.
1353 //
1354 // The old(er) swapchains still need to be kept to be scheduled for destruction.
1355 VkSwapchainKHR swapchainToDestroy = VK_NULL_HANDLE;
1356
1357 if (mPresentHistory.empty())
1358 {
1359 // Destroy the current (never-used) swapchain.
1360 swapchainToDestroy = mSwapchain;
1361 }
1362
1363 // Place all present operation into mOldSwapchains. That gets scheduled for destruction when the
1364 // semaphore of the first image of the next swapchain can be recycled or when fences are
1365 // signaled (when VK_EXT_swapchain_maintenance1 is supported).
1366 SwapchainCleanupData cleanupData;
1367
1368 // If the swapchain is not being immediately destroyed, schedule it for destruction.
1369 if (swapchainToDestroy == VK_NULL_HANDLE)
1370 {
1371 cleanupData.swapchain = mSwapchain;
1372 }
1373
1374 for (impl::ImagePresentOperation &presentOperation : mPresentHistory)
1375 {
1376 // fence is only used when VK_EXT_swapchain_maintenance1 is supported.
1377 if (presentOperation.fence.valid())
1378 {
1379 cleanupData.fences.emplace_back(std::move(presentOperation.fence));
1380 }
1381
1382 ASSERT(presentOperation.semaphore.valid());
1383 cleanupData.semaphores.emplace_back(std::move(presentOperation.semaphore));
1384
1385 // Accumulate any previous swapchains that are pending destruction too.
1386 for (SwapchainCleanupData &oldSwapchain : presentOperation.oldSwapchains)
1387 {
1388 mOldSwapchains.emplace_back(std::move(oldSwapchain));
1389 }
1390 presentOperation.oldSwapchains.clear();
1391 }
1392 mPresentHistory.clear();
1393
1394 // If too many old swapchains have accumulated, wait idle and destroy them. This is to prevent
1395 // failures due to too many swapchains allocated.
1396 //
1397 // Note: Nvidia has been observed to fail creation of swapchains after 20 are allocated on
1398 // desktop, or less than 10 on Quadro P400.
1399 static constexpr size_t kMaxOldSwapchains = 5;
1400 if (mOldSwapchains.size() > kMaxOldSwapchains)
1401 {
1402 mUse.merge(contextVk->getSubmittedResourceUse());
1403 ANGLE_TRY(finish(contextVk));
1404 for (SwapchainCleanupData &oldSwapchain : mOldSwapchains)
1405 {
1406 oldSwapchain.waitFences(contextVk->getDevice(),
1407 contextVk->getRenderer()->getMaxFenceWaitTimeNs());
1408 oldSwapchain.destroy(contextVk->getDevice(), &mPresentFenceRecycler,
1409 &mPresentSemaphoreRecycler);
1410 }
1411 mOldSwapchains.clear();
1412 }
1413
1414 if (cleanupData.swapchain != VK_NULL_HANDLE || !cleanupData.fences.empty() ||
1415 !cleanupData.semaphores.empty())
1416 {
1417 mOldSwapchains.emplace_back(std::move(cleanupData));
1418 }
1419
1420 // Recreate the swapchain based on the most recent one.
1421 VkSwapchainKHR lastSwapchain = mSwapchain;
1422 mSwapchain = VK_NULL_HANDLE;
1423
1424 releaseSwapchainImages(contextVk);
1425
1426 // If prerotation is emulated, adjust the window extents to match what real prerotation would
1427 // have reported.
1428 gl::Extents swapchainExtents = extents;
1429 if (Is90DegreeRotation(mEmulatedPreTransform))
1430 {
1431 ASSERT(mPreTransform == VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR);
1432 std::swap(swapchainExtents.width, swapchainExtents.height);
1433 }
1434
1435 // On Android, vkCreateSwapchainKHR destroys lastSwapchain, which is incorrect. Wait idle in
1436 // that case as a workaround.
1437 if (lastSwapchain &&
1438 contextVk->getRenderer()->getFeatures().waitIdleBeforeSwapchainRecreation.enabled)
1439 {
1440 mUse.merge(contextVk->getSubmittedResourceUse());
1441 ANGLE_TRY(finish(contextVk));
1442 }
1443 angle::Result result = createSwapChain(contextVk, swapchainExtents, lastSwapchain);
1444
1445 // Notify the parent classes of the surface's new state.
1446 onStateChange(angle::SubjectMessage::SurfaceChanged);
1447
1448 // If the most recent swapchain was never used, destroy it right now.
1449 if (swapchainToDestroy)
1450 {
1451 vkDestroySwapchainKHR(contextVk->getDevice(), swapchainToDestroy, nullptr);
1452 }
1453
1454 return result;
1455 }
1456
resizeSwapchainImages(vk::Context * context,uint32_t imageCount)1457 angle::Result WindowSurfaceVk::resizeSwapchainImages(vk::Context *context, uint32_t imageCount)
1458 {
1459 if (static_cast<size_t>(imageCount) != mSwapchainImages.size())
1460 {
1461 mSwapchainImageBindings.clear();
1462 mSwapchainImages.resize(imageCount);
1463
1464 // Update the image bindings. Because the observer binding class uses raw pointers we
1465 // need to first ensure the entire image vector is fully allocated before binding the
1466 // subject and observer together.
1467 for (uint32_t index = 0; index < imageCount; ++index)
1468 {
1469 mSwapchainImageBindings.push_back(
1470 angle::ObserverBinding(this, kAnySurfaceImageSubjectIndex));
1471 }
1472
1473 for (uint32_t index = 0; index < imageCount; ++index)
1474 {
1475 mSwapchainImages[index].image = std::make_unique<vk::ImageHelper>();
1476 mSwapchainImageBindings[index].bind(mSwapchainImages[index].image.get());
1477 }
1478 }
1479
1480 return angle::Result::Continue;
1481 }
1482
createSwapChain(vk::Context * context,const gl::Extents & extents,VkSwapchainKHR lastSwapchain)1483 angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
1484 const gl::Extents &extents,
1485 VkSwapchainKHR lastSwapchain)
1486 {
1487 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::createSwapchain");
1488
1489 ASSERT(mSwapchain == VK_NULL_HANDLE);
1490
1491 RendererVk *renderer = context->getRenderer();
1492 VkDevice device = renderer->getDevice();
1493
1494 const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
1495 angle::FormatID actualFormatID = format.getActualRenderableImageFormatID();
1496 angle::FormatID intendedFormatID = format.getIntendedFormatID();
1497
1498 // For devices that don't support creating swapchain images with RGB8, emulate with RGBA8.
1499 if (renderer->getFeatures().overrideSurfaceFormatRGB8ToRGBA8.enabled &&
1500 intendedFormatID == angle::FormatID::R8G8B8_UNORM)
1501 {
1502 actualFormatID = angle::FormatID::R8G8B8A8_UNORM;
1503 }
1504
1505 gl::Extents rotatedExtents = extents;
1506 if (Is90DegreeRotation(getPreTransform()))
1507 {
1508 // The Surface is oriented such that its aspect ratio no longer matches that of the
1509 // device. In this case, the width and height of the swapchain images must be swapped to
1510 // match the device's native orientation. This must also be done for other attachments
1511 // used with the swapchain (e.g. depth buffer). The width and height of the viewport,
1512 // scissor, and render-pass render area must also be swapped. Then, when ANGLE rotates
1513 // gl_Position in the vertex shader, the rendering will look the same as if no
1514 // pre-rotation had been done.
1515 std::swap(rotatedExtents.width, rotatedExtents.height);
1516 }
1517
1518 // We need transfer src for reading back from the backbuffer.
1519 VkImageUsageFlags imageUsageFlags = kSurfaceVkColorImageUsageFlags;
1520
1521 // If shaders may be fetching from this, we need this image to be an input
1522 if (NeedsInputAttachmentUsage(renderer->getFeatures()))
1523 {
1524 imageUsageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
1525 }
1526
1527 VkSwapchainCreateInfoKHR swapchainInfo = {};
1528 swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1529 swapchainInfo.flags = mState.hasProtectedContent() ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR : 0;
1530 swapchainInfo.surface = mSurface;
1531 swapchainInfo.minImageCount = mMinImageCount;
1532 swapchainInfo.imageFormat = vk::GetVkFormatFromFormatID(actualFormatID);
1533 swapchainInfo.imageColorSpace = MapEglColorSpaceToVkColorSpace(
1534 renderer, static_cast<EGLenum>(mState.attributes.get(EGL_GL_COLORSPACE, EGL_NONE)));
1535 // Note: Vulkan doesn't allow 0-width/height swapchains.
1536 swapchainInfo.imageExtent.width = std::max(rotatedExtents.width, 1);
1537 swapchainInfo.imageExtent.height = std::max(rotatedExtents.height, 1);
1538 swapchainInfo.imageArrayLayers = 1;
1539 swapchainInfo.imageUsage = imageUsageFlags;
1540 swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1541 swapchainInfo.queueFamilyIndexCount = 0;
1542 swapchainInfo.pQueueFamilyIndices = nullptr;
1543 swapchainInfo.preTransform = mPreTransform;
1544 swapchainInfo.compositeAlpha = mCompositeAlpha;
1545 swapchainInfo.presentMode = vk::ConvertPresentModeToVkPresentMode(mDesiredSwapchainPresentMode);
1546 swapchainInfo.clipped = VK_TRUE;
1547 swapchainInfo.oldSwapchain = lastSwapchain;
1548
1549 #if defined(ANGLE_PLATFORM_WINDOWS)
1550 // On some AMD drivers we need to explicitly enable the extension and set
1551 // it to "disallowed" mode in order to avoid seeing impossible-to-handle
1552 // extension-specific error codes from swapchain functions.
1553 VkSurfaceFullScreenExclusiveInfoEXT fullscreen = {};
1554 fullscreen.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT;
1555 fullscreen.fullScreenExclusive = VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT;
1556
1557 VkSurfaceFullScreenExclusiveWin32InfoEXT fullscreenWin32 = {};
1558 fullscreenWin32.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT;
1559 fullscreenWin32.hmonitor = MonitorFromWindow((HWND)mNativeWindowType, MONITOR_DEFAULTTONEAREST);
1560
1561 if (renderer->getFeatures().supportsFullScreenExclusive.enabled &&
1562 renderer->getFeatures().forceDisableFullScreenExclusive.enabled)
1563 {
1564 vk::AddToPNextChain(&swapchainInfo, &fullscreen);
1565 vk::AddToPNextChain(&swapchainInfo, &fullscreenWin32);
1566 }
1567 #endif
1568
1569 if (context->getFeatures().supportsSwapchainMaintenance1.enabled)
1570 {
1571 swapchainInfo.flags |= VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT;
1572 }
1573
1574 if (isSharedPresentModeDesired())
1575 {
1576 swapchainInfo.minImageCount = 1;
1577
1578 // This feature is by default disabled, and only affects Android platform wsi behavior
1579 // transparent to angle internal tracking for shared present.
1580 if (renderer->getFeatures().forceContinuousRefreshOnSharedPresent.enabled)
1581 {
1582 swapchainInfo.presentMode = VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
1583 }
1584 }
1585
1586 // Get the list of compatible present modes to avoid unnecessary swapchain recreation.
1587 VkSwapchainPresentModesCreateInfoEXT compatibleModesInfo = {};
1588 if (renderer->getFeatures().supportsSwapchainMaintenance1.enabled)
1589 {
1590 VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo2 = {};
1591 surfaceInfo2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
1592 surfaceInfo2.surface = mSurface;
1593
1594 VkSurfacePresentModeEXT surfacePresentMode = {};
1595 surfacePresentMode.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT;
1596 surfacePresentMode.presentMode = swapchainInfo.presentMode;
1597 vk::AddToPNextChain(&surfaceInfo2, &surfacePresentMode);
1598
1599 VkSurfaceCapabilities2KHR surfaceCaps2 = {};
1600 surfaceCaps2.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
1601
1602 mCompatiblePresentModes.resize(kMaxCompatiblePresentModes);
1603
1604 VkSurfacePresentModeCompatibilityEXT compatibleModes = {};
1605 compatibleModes.sType = VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT;
1606 compatibleModes.presentModeCount = kMaxCompatiblePresentModes;
1607 compatibleModes.pPresentModes = mCompatiblePresentModes.data();
1608 vk::AddToPNextChain(&surfaceCaps2, &compatibleModes);
1609
1610 ANGLE_VK_TRY(context, vkGetPhysicalDeviceSurfaceCapabilities2KHR(
1611 renderer->getPhysicalDevice(), &surfaceInfo2, &surfaceCaps2));
1612
1613 mCompatiblePresentModes.resize(compatibleModes.presentModeCount);
1614
1615 // The implementation must always return the given preesnt mode as compatible with itself.
1616 ASSERT(IsCompatiblePresentMode(mDesiredSwapchainPresentMode, mCompatiblePresentModes.data(),
1617 mCompatiblePresentModes.size()));
1618
1619 if (compatibleModes.presentModeCount > 1)
1620 {
1621 compatibleModesInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT;
1622 compatibleModesInfo.presentModeCount = compatibleModes.presentModeCount;
1623 compatibleModesInfo.pPresentModes = mCompatiblePresentModes.data();
1624
1625 vk::AddToPNextChain(&swapchainInfo, &compatibleModesInfo);
1626 }
1627 }
1628 else
1629 {
1630 // Without VK_EXT_swapchain_maintenance1, each present mode can be considered only
1631 // compatible with itself.
1632 mCompatiblePresentModes.resize(1);
1633 mCompatiblePresentModes[0] = swapchainInfo.presentMode;
1634 }
1635
1636 // TODO: Once EGL_SWAP_BEHAVIOR_PRESERVED_BIT is supported, the contents of the old swapchain
1637 // need to carry over to the new one. http://anglebug.com/2942
1638 VkSwapchainKHR newSwapChain = VK_NULL_HANDLE;
1639 ANGLE_VK_TRY(context, vkCreateSwapchainKHR(device, &swapchainInfo, nullptr, &newSwapChain));
1640 mSwapchain = newSwapChain;
1641 mSwapchainPresentMode = mDesiredSwapchainPresentMode;
1642
1643 // If frame timestamp was enabled for the surface, [re]enable it when [re]creating the swapchain
1644 if (renderer->getFeatures().supportsTimestampSurfaceAttribute.enabled &&
1645 mState.timestampsEnabled)
1646 {
1647 // The implementation of "vkGetPastPresentationTimingGOOGLE" on Android calls into the
1648 // appropriate ANativeWindow API that enables frame timestamps.
1649 uint32_t count = 0;
1650 ANGLE_VK_TRY(context,
1651 vkGetPastPresentationTimingGOOGLE(device, mSwapchain, &count, nullptr));
1652 }
1653
1654 // Initialize the swapchain image views.
1655 uint32_t imageCount = 0;
1656 ANGLE_VK_TRY(context, vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, nullptr));
1657
1658 std::vector<VkImage> swapchainImages(imageCount);
1659 ANGLE_VK_TRY(context,
1660 vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data()));
1661
1662 // If multisampling is enabled, create a multisampled image which gets resolved just prior to
1663 // present.
1664 GLint samples = GetSampleCount(mState.config);
1665 ANGLE_VK_CHECK(context, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
1666
1667 VkExtent3D vkExtents;
1668 gl_vk::GetExtent(rotatedExtents, &vkExtents);
1669
1670 bool robustInit = mState.isRobustResourceInitEnabled();
1671
1672 if (samples > 1)
1673 {
1674 VkImageUsageFlags usage = kSurfaceVkColorImageUsageFlags;
1675 if (NeedsInputAttachmentUsage(renderer->getFeatures()))
1676 {
1677 usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
1678 }
1679
1680 // Create a multisampled image that will be rendered to, and then resolved to a swapchain
1681 // image. The actual VkImage is created with rotated coordinates to make it easier to do
1682 // the resolve. The ImageHelper::mExtents will have non-rotated extents in order to fit
1683 // with the rest of ANGLE, (e.g. which calculates the Vulkan scissor with non-rotated
1684 // values and then rotates the final rectangle).
1685 ANGLE_TRY(mColorImageMS.initMSAASwapchain(
1686 context, gl::TextureType::_2D, vkExtents, Is90DegreeRotation(getPreTransform()), format,
1687 samples, usage, gl::LevelIndex(0), 1, 1, robustInit, mState.hasProtectedContent()));
1688 ANGLE_TRY(mColorImageMS.initMemory(
1689 context, mState.hasProtectedContent(), renderer->getMemoryProperties(),
1690 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vk::MemoryAllocationType::SwapchainMSAAImage));
1691
1692 // Initialize the color render target with the multisampled targets. If not multisampled,
1693 // the render target will be updated to refer to a swapchain image on every acquire.
1694 mColorRenderTarget.init(&mColorImageMS, &mColorImageMSViews, nullptr, nullptr, {},
1695 gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
1696 }
1697
1698 ANGLE_TRY(resizeSwapchainImages(context, imageCount));
1699
1700 for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex)
1701 {
1702 SwapchainImage &member = mSwapchainImages[imageIndex];
1703
1704 ASSERT(member.image);
1705 member.image->init2DWeakReference(context, swapchainImages[imageIndex], extents,
1706 Is90DegreeRotation(getPreTransform()), intendedFormatID,
1707 actualFormatID, 0, 1, robustInit);
1708 member.imageViews.init(renderer);
1709 member.frameNumber = 0;
1710 }
1711
1712 // Initialize depth/stencil if requested.
1713 if (mState.config->depthStencilFormat != GL_NONE)
1714 {
1715 const vk::Format &dsFormat = renderer->getFormat(mState.config->depthStencilFormat);
1716
1717 const VkImageUsageFlags dsUsage = kSurfaceVkDepthStencilImageUsageFlags;
1718
1719 ANGLE_TRY(mDepthStencilImage.init(context, gl::TextureType::_2D, vkExtents, dsFormat,
1720 samples, dsUsage, gl::LevelIndex(0), 1, 1, robustInit,
1721 mState.hasProtectedContent()));
1722 ANGLE_TRY(mDepthStencilImage.initMemory(
1723 context, mState.hasProtectedContent(), renderer->getMemoryProperties(),
1724 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1725 vk::MemoryAllocationType::SwapchainDepthStencilImage));
1726
1727 mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageViews, nullptr,
1728 nullptr, {}, gl::LevelIndex(0), 0, 1,
1729 RenderTargetTransience::Default);
1730
1731 // We will need to pass depth/stencil image views to the RenderTargetVk in the future.
1732 }
1733
1734 // Need to acquire a new image before the swapchain can be used.
1735 mAcquireOperation.needToAcquireNextSwapchainImage = true;
1736
1737 return angle::Result::Continue;
1738 }
1739
isMultiSampled() const1740 bool WindowSurfaceVk::isMultiSampled() const
1741 {
1742 return mColorImageMS.valid();
1743 }
1744
queryAndAdjustSurfaceCaps(ContextVk * contextVk,VkSurfaceCapabilitiesKHR * surfaceCaps)1745 angle::Result WindowSurfaceVk::queryAndAdjustSurfaceCaps(ContextVk *contextVk,
1746 VkSurfaceCapabilitiesKHR *surfaceCaps)
1747 {
1748 const VkPhysicalDevice &physicalDevice = contextVk->getRenderer()->getPhysicalDevice();
1749 ANGLE_VK_TRY(contextVk,
1750 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface, surfaceCaps));
1751 if (surfaceCaps->currentExtent.width == kSurfaceSizedBySwapchain)
1752 {
1753 ASSERT(surfaceCaps->currentExtent.height == kSurfaceSizedBySwapchain);
1754 ASSERT(!IsAndroid());
1755
1756 // vkGetPhysicalDeviceSurfaceCapabilitiesKHR does not provide useful extents for some
1757 // platforms (e.g. Fuschia). Therefore, we must query the window size via a
1758 // platform-specific mechanism. Add those extents to the surfaceCaps
1759 gl::Extents currentExtents;
1760 ANGLE_TRY(getCurrentWindowSize(contextVk, ¤tExtents));
1761 surfaceCaps->currentExtent.width = currentExtents.width;
1762 surfaceCaps->currentExtent.height = currentExtents.height;
1763 }
1764
1765 return angle::Result::Continue;
1766 }
1767
checkForOutOfDateSwapchain(ContextVk * contextVk,bool presentOutOfDate,bool * swapchainRecreatedOut)1768 angle::Result WindowSurfaceVk::checkForOutOfDateSwapchain(ContextVk *contextVk,
1769 bool presentOutOfDate,
1770 bool *swapchainRecreatedOut)
1771 {
1772 *swapchainRecreatedOut = false;
1773
1774 bool swapIntervalChanged =
1775 !IsCompatiblePresentMode(mDesiredSwapchainPresentMode, mCompatiblePresentModes.data(),
1776 mCompatiblePresentModes.size());
1777 presentOutOfDate = presentOutOfDate || swapIntervalChanged;
1778
1779 // If there's no change, early out.
1780 if (!contextVk->getRenderer()->getFeatures().perFrameWindowSizeQuery.enabled &&
1781 !presentOutOfDate)
1782 {
1783 return angle::Result::Continue;
1784 }
1785
1786 // Get the latest surface capabilities.
1787 ANGLE_TRY(queryAndAdjustSurfaceCaps(contextVk, &mSurfaceCaps));
1788
1789 if (contextVk->getRenderer()->getFeatures().perFrameWindowSizeQuery.enabled &&
1790 !presentOutOfDate)
1791 {
1792 // This device generates neither VK_ERROR_OUT_OF_DATE_KHR nor VK_SUBOPTIMAL_KHR. Check for
1793 // whether the size and/or rotation have changed since the swapchain was created.
1794 uint32_t swapchainWidth = getWidth();
1795 uint32_t swapchainHeight = getHeight();
1796 presentOutOfDate = mSurfaceCaps.currentTransform != mPreTransform ||
1797 mSurfaceCaps.currentExtent.width != swapchainWidth ||
1798 mSurfaceCaps.currentExtent.height != swapchainHeight;
1799 }
1800
1801 // If anything has changed, recreate the swapchain.
1802 if (!presentOutOfDate)
1803 {
1804 return angle::Result::Continue;
1805 }
1806
1807 gl::Extents newSwapchainExtents(mSurfaceCaps.currentExtent.width,
1808 mSurfaceCaps.currentExtent.height, 1);
1809
1810 if (contextVk->getFeatures().enablePreRotateSurfaces.enabled)
1811 {
1812 // Update the surface's transform, which can change even if the window size does not.
1813 mPreTransform = mSurfaceCaps.currentTransform;
1814 }
1815
1816 *swapchainRecreatedOut = true;
1817 return recreateSwapchain(contextVk, newSwapchainExtents);
1818 }
1819
releaseSwapchainImages(ContextVk * contextVk)1820 void WindowSurfaceVk::releaseSwapchainImages(ContextVk *contextVk)
1821 {
1822 RendererVk *renderer = contextVk->getRenderer();
1823
1824 mColorRenderTarget.release(contextVk);
1825 mDepthStencilRenderTarget.release(contextVk);
1826
1827 if (mDepthStencilImage.valid())
1828 {
1829 mDepthStencilImageViews.release(renderer, mDepthStencilImage.getResourceUse());
1830 mDepthStencilImage.releaseImageFromShareContexts(renderer, contextVk, {});
1831 mDepthStencilImage.releaseStagedUpdates(renderer);
1832 }
1833
1834 if (mColorImageMS.valid())
1835 {
1836 mColorImageMSViews.release(renderer, mColorImageMS.getResourceUse());
1837 mColorImageMS.releaseImageFromShareContexts(renderer, contextVk, {});
1838 mColorImageMS.releaseStagedUpdates(renderer);
1839 contextVk->addGarbage(&mFramebufferMS);
1840 }
1841
1842 mSwapchainImageBindings.clear();
1843
1844 for (SwapchainImage &swapchainImage : mSwapchainImages)
1845 {
1846 ASSERT(swapchainImage.image);
1847 swapchainImage.imageViews.release(renderer, swapchainImage.image->getResourceUse());
1848 // swapchain image must not have ANI semaphore assigned here, since acquired image must be
1849 // presented before swapchain recreation.
1850 swapchainImage.image->resetImageWeakReference();
1851 swapchainImage.image->destroy(renderer);
1852
1853 contextVk->addGarbage(&swapchainImage.framebuffer);
1854 if (swapchainImage.fetchFramebuffer.valid())
1855 {
1856 contextVk->addGarbage(&swapchainImage.fetchFramebuffer);
1857 }
1858 if (swapchainImage.framebufferResolveMS.valid())
1859 {
1860 contextVk->addGarbage(&swapchainImage.framebufferResolveMS);
1861 }
1862 }
1863
1864 mSwapchainImages.clear();
1865 }
1866
finish(vk::Context * context)1867 angle::Result WindowSurfaceVk::finish(vk::Context *context)
1868 {
1869 RendererVk *renderer = context->getRenderer();
1870
1871 mUse.merge(mDepthStencilImage.getResourceUse());
1872 mUse.merge(mColorImageMS.getResourceUse());
1873 for (SwapchainImage &swapchainImage : mSwapchainImages)
1874 {
1875 mUse.merge(swapchainImage.image->getResourceUse());
1876 }
1877
1878 return renderer->finishResourceUse(context, mUse);
1879 }
1880
destroySwapChainImages(DisplayVk * displayVk)1881 void WindowSurfaceVk::destroySwapChainImages(DisplayVk *displayVk)
1882 {
1883 RendererVk *renderer = displayVk->getRenderer();
1884 VkDevice device = displayVk->getDevice();
1885
1886 mDepthStencilImage.destroy(renderer);
1887 mDepthStencilImageViews.destroy(device);
1888 mColorImageMS.destroy(renderer);
1889 mColorImageMSViews.destroy(device);
1890 mFramebufferMS.destroy(device);
1891
1892 for (SwapchainImage &swapchainImage : mSwapchainImages)
1893 {
1894 ASSERT(swapchainImage.image);
1895 // swapchain image must not have ANI semaphore assigned here, because it should be released
1896 // in the destroy() prior to calling this method.
1897 // We don't own the swapchain image handles, so we just remove our reference to it.
1898 swapchainImage.image->resetImageWeakReference();
1899 swapchainImage.image->destroy(renderer);
1900 swapchainImage.imageViews.destroy(device);
1901 swapchainImage.framebuffer.destroy(device);
1902 if (swapchainImage.fetchFramebuffer.valid())
1903 {
1904 swapchainImage.fetchFramebuffer.destroy(device);
1905 }
1906 if (swapchainImage.framebufferResolveMS.valid())
1907 {
1908 swapchainImage.framebufferResolveMS.destroy(device);
1909 }
1910 }
1911
1912 mSwapchainImages.clear();
1913 }
1914
prepareSwap(const gl::Context * context)1915 egl::Error WindowSurfaceVk::prepareSwap(const gl::Context *context)
1916 {
1917 if (!mAcquireOperation.needToAcquireNextSwapchainImage)
1918 {
1919 return egl::NoError();
1920 }
1921
1922 DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
1923 RendererVk *renderer = displayVk->getRenderer();
1924
1925 bool swapchainRecreated = false;
1926 angle::Result result = prepareForAcquireNextSwapchainImage(context, false, &swapchainRecreated);
1927 if (result != angle::Result::Continue)
1928 {
1929 return angle::ToEGL(result, EGL_BAD_SURFACE);
1930 }
1931 if (swapchainRecreated || isSharedPresentMode())
1932 {
1933 // If swapchain is recreated or it is a shred present mode, acquire the image right away;
1934 // it's not going to block.
1935 result = doDeferredAcquireNextImageWithUsableSwapchain(context);
1936 return angle::ToEGL(result, EGL_BAD_SURFACE);
1937 }
1938
1939 // Call vkAcquireNextImageKHR without holding the share group lock. The following are accessed
1940 // by this function:
1941 //
1942 // - mAcquireOperation.needToAcquireNextSwapchainImage, which is atomic
1943 // - Contents of mAcquireOperation.unlockedTryAcquireData and
1944 // mAcquireOperation.unlockedTryAcquireResult, which are protected by
1945 // unlockedTryAcquireData.mutex
1946 // - context->getDevice(), which doesn't need external synchronization
1947 // - mSwapchain
1948 //
1949 // The latter two are also protected by unlockedTryAcquireData.mutex during this call. Note
1950 // that due to the presence of needToAcquireNextSwapchainImage, the threads may be in either of
1951 // these states:
1952 //
1953 // 1. Calling eglPrepareSwapBuffersANGLE; in this case, they are accessing mSwapchain protected
1954 // by the aforementioned mutex
1955 // 2. Calling doDeferredAcquireNextImage() through an EGL/GL call
1956 // * If needToAcquireNextSwapchainImage is true, these variables are protected by the same
1957 // mutex in the same TryAcquireNextImageUnlocked call.
1958 // * If needToAcquireNextSwapchainImage is false, these variables are not protected by this
1959 // mutex. However, in this case no thread could be calling TryAcquireNextImageUnlocked
1960 // because needToAcquireNextSwapchainImage is false (and hence there is no data race).
1961 // Note that needToAcquireNextSwapchainImage's atomic store and load ensure
1962 // availability/visibility of changes to these variables between threads.
1963 //
1964 // The result of this call is processed in doDeferredAcquireNextImage() by whoever ends up
1965 // calling it (likely the eglSwapBuffers call that follows)
1966
1967 egl::Display::GetCurrentThreadUnlockedTailCall()->add(
1968 [device = renderer->getDevice(), swapchain = mSwapchain, acquire = &mAcquireOperation]() {
1969 ANGLE_TRACE_EVENT0("gpu.angle", "Acquire Swap Image Before Swap");
1970 TryAcquireNextImageUnlocked(device, swapchain, acquire);
1971 });
1972
1973 return egl::NoError();
1974 }
1975
swapWithDamage(const gl::Context * context,const EGLint * rects,EGLint n_rects)1976 egl::Error WindowSurfaceVk::swapWithDamage(const gl::Context *context,
1977 const EGLint *rects,
1978 EGLint n_rects)
1979 {
1980 const angle::Result result = swapImpl(context, rects, n_rects, nullptr);
1981 return angle::ToEGL(result, EGL_BAD_SURFACE);
1982 }
1983
swap(const gl::Context * context)1984 egl::Error WindowSurfaceVk::swap(const gl::Context *context)
1985 {
1986 // When in shared present mode, eglSwapBuffers is unnecessary except for mode change. When mode
1987 // change is not expected, the eglSwapBuffers call is forwarded to the context as a glFlush.
1988 // This allows the context to skip it if there's nothing to flush. Otherwise control is bounced
1989 // back swapImpl().
1990 //
1991 // Some apps issue eglSwapBuffers after glFlush unnecessary, causing the CPU throttling logic to
1992 // effectively wait for the just submitted commands.
1993 if (isSharedPresentMode() && mSwapchainPresentMode == mDesiredSwapchainPresentMode)
1994 {
1995 const angle::Result result = vk::GetImpl(context)->flush(context);
1996 return angle::ToEGL(result, EGL_BAD_SURFACE);
1997 }
1998
1999 const angle::Result result = swapImpl(context, nullptr, 0, nullptr);
2000 return angle::ToEGL(result, EGL_BAD_SURFACE);
2001 }
2002
computePresentOutOfDate(vk::Context * context,VkResult result,bool * presentOutOfDate)2003 angle::Result WindowSurfaceVk::computePresentOutOfDate(vk::Context *context,
2004 VkResult result,
2005 bool *presentOutOfDate)
2006 {
2007 // If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
2008 // continuing. We do the same when VK_SUBOPTIMAL_KHR is returned to avoid visual degradation
2009 // and handle device rotation / screen resize.
2010 *presentOutOfDate = result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR;
2011 if (!*presentOutOfDate)
2012 {
2013 ANGLE_VK_TRY(context, result);
2014 }
2015 return angle::Result::Continue;
2016 }
2017
chooseFramebuffer(const SwapchainResolveMode swapchainResolveMode)2018 vk::Framebuffer &WindowSurfaceVk::chooseFramebuffer(const SwapchainResolveMode swapchainResolveMode)
2019 {
2020 if (isMultiSampled())
2021 {
2022 return swapchainResolveMode == SwapchainResolveMode::Enabled
2023 ? mSwapchainImages[mCurrentSwapchainImageIndex].framebufferResolveMS
2024 : mFramebufferMS;
2025 }
2026
2027 // Choose which framebuffer to use based on fetch, so it will have a matching renderpass
2028 ASSERT(swapchainResolveMode == SwapchainResolveMode::Disabled);
2029 return mFramebufferFetchMode == FramebufferFetchMode::Enabled
2030 ? mSwapchainImages[mCurrentSwapchainImageIndex].fetchFramebuffer
2031 : mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
2032 }
2033
prePresentSubmit(ContextVk * contextVk,const vk::Semaphore & presentSemaphore)2034 angle::Result WindowSurfaceVk::prePresentSubmit(ContextVk *contextVk,
2035 const vk::Semaphore &presentSemaphore)
2036 {
2037 RendererVk *renderer = contextVk->getRenderer();
2038
2039 SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
2040 vk::Framebuffer ¤tFramebuffer = chooseFramebuffer(SwapchainResolveMode::Disabled);
2041
2042 // Make sure deferred clears are applied, if any.
2043 if (mColorImageMS.valid())
2044 {
2045 ANGLE_TRY(mColorImageMS.flushStagedUpdates(contextVk, gl::LevelIndex(0), gl::LevelIndex(1),
2046 0, 1, {}));
2047 }
2048 else
2049 {
2050 ANGLE_TRY(image.image->flushStagedUpdates(contextVk, gl::LevelIndex(0), gl::LevelIndex(1),
2051 0, 1, {}));
2052 }
2053
2054 // If user calls eglSwapBuffer without use it, image may already in Present layout (if swap
2055 // without any draw) or Undefined (first time present). In this case, if
2056 // acquireNextImageSemaphore has not been waited, we must add to context will force the
2057 // semaphore wait so that it will be in unsignaled state and ready to use for ANI call.
2058 if (image.image->getAcquireNextImageSemaphore().valid())
2059 {
2060 ASSERT(!renderer->getFeatures().supportsPresentation.enabled ||
2061 image.image->getCurrentImageLayout() == vk::ImageLayout::Present ||
2062 image.image->getCurrentImageLayout() == vk::ImageLayout::Undefined);
2063 contextVk->addWaitSemaphore(image.image->getAcquireNextImageSemaphore().getHandle(),
2064 vk::kSwapchainAcquireImageWaitStageFlags);
2065 image.image->resetAcquireNextImageSemaphore();
2066 }
2067
2068 // We can only do present related optimization if this is the last renderpass that touches the
2069 // swapchain image. MSAA resolve and overlay will insert another renderpass which disqualifies
2070 // the optimization.
2071 bool imageResolved = false;
2072 if (currentFramebuffer.valid())
2073 {
2074 ANGLE_TRY(contextVk->optimizeRenderPassForPresent(
2075 currentFramebuffer.getHandle(), &image.imageViews, image.image.get(), &mColorImageMS,
2076 mSwapchainPresentMode, &imageResolved));
2077 }
2078
2079 // Because the color attachment defers layout changes until endRenderPass time, we must call
2080 // finalize the layout transition in the renderpass before we insert layout change to
2081 // ImageLayout::Present bellow.
2082 contextVk->finalizeImageLayout(image.image.get(), {});
2083 contextVk->finalizeImageLayout(&mColorImageMS, {});
2084
2085 vk::OutsideRenderPassCommandBufferHelper *commandBufferHelper;
2086 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper({}, &commandBufferHelper));
2087
2088 if (mColorImageMS.valid() && !imageResolved)
2089 {
2090 // Transition the multisampled image to TRANSFER_SRC for resolve.
2091 vk::CommandBufferAccess access;
2092 access.onImageTransferRead(VK_IMAGE_ASPECT_COLOR_BIT, &mColorImageMS);
2093 access.onImageTransferWrite(gl::LevelIndex(0), 1, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
2094 image.image.get());
2095
2096 ANGLE_TRY(contextVk->getOutsideRenderPassCommandBufferHelper(access, &commandBufferHelper));
2097
2098 VkImageResolve resolveRegion = {};
2099 resolveRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2100 resolveRegion.srcSubresource.mipLevel = 0;
2101 resolveRegion.srcSubresource.baseArrayLayer = 0;
2102 resolveRegion.srcSubresource.layerCount = 1;
2103 resolveRegion.srcOffset = {};
2104 resolveRegion.dstSubresource = resolveRegion.srcSubresource;
2105 resolveRegion.dstOffset = {};
2106 resolveRegion.extent = image.image->getRotatedExtents();
2107
2108 mColorImageMS.resolve(image.image.get(), resolveRegion,
2109 &commandBufferHelper->getCommandBuffer());
2110 contextVk->getPerfCounters().swapchainResolveOutsideSubpass++;
2111 }
2112
2113 if (renderer->getFeatures().supportsPresentation.enabled)
2114 {
2115 // This does nothing if it's already in the requested layout
2116 image.image->recordReadBarrier(contextVk, VK_IMAGE_ASPECT_COLOR_BIT,
2117 vk::ImageLayout::Present, commandBufferHelper);
2118 }
2119
2120 // The overlay is drawn after this. This ensures that drawing the overlay does not interfere
2121 // with other functionality, especially counters used to validate said functionality.
2122 const bool shouldDrawOverlay = overlayHasEnabledWidget(contextVk);
2123
2124 ANGLE_TRY(contextVk->flushImpl(shouldDrawOverlay ? nullptr : &presentSemaphore, nullptr,
2125 RenderPassClosureReason::EGLSwapBuffers));
2126
2127 if (shouldDrawOverlay)
2128 {
2129 updateOverlay(contextVk);
2130 ANGLE_TRY(drawOverlay(contextVk, &image));
2131 ANGLE_TRY(contextVk->flushImpl(&presentSemaphore, nullptr,
2132 RenderPassClosureReason::AlreadySpecifiedElsewhere));
2133 }
2134
2135 return angle::Result::Continue;
2136 }
2137
present(ContextVk * contextVk,const EGLint * rects,EGLint n_rects,const void * pNextChain,bool * presentOutOfDate)2138 angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
2139 const EGLint *rects,
2140 EGLint n_rects,
2141 const void *pNextChain,
2142 bool *presentOutOfDate)
2143 {
2144 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present");
2145 RendererVk *renderer = contextVk->getRenderer();
2146
2147 // Clean up whatever present is already finished. Do this before allocating new semaphore/fence
2148 // to reduce number of allocations.
2149 ANGLE_TRY(cleanUpPresentHistory(contextVk));
2150
2151 // Get a new semaphore to use for present.
2152 vk::Semaphore presentSemaphore;
2153 ANGLE_TRY(NewSemaphore(contextVk, &mPresentSemaphoreRecycler, &presentSemaphore));
2154
2155 // Make a submission before present to flush whatever's pending. In the very least, a
2156 // submission is necessary to make sure the present semaphore is signaled.
2157 ANGLE_TRY(prePresentSubmit(contextVk, presentSemaphore));
2158
2159 QueueSerial swapSerial = contextVk->getLastSubmittedQueueSerial();
2160
2161 if (!contextVk->getFeatures().supportsSwapchainMaintenance1.enabled)
2162 {
2163 // Associate swapSerial of this present with the previous present of the same imageIndex.
2164 // Completion of swapSerial implies that current ANI semaphore was waited. See
2165 // doc/PresentSemaphores.md for details.
2166 AssociateQueueSerialWithPresentHistory(mCurrentSwapchainImageIndex, swapSerial,
2167 &mPresentHistory);
2168 }
2169
2170 VkPresentInfoKHR presentInfo = {};
2171 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2172 presentInfo.pNext = pNextChain;
2173 presentInfo.waitSemaphoreCount = 1;
2174 presentInfo.pWaitSemaphores = presentSemaphore.ptr();
2175 presentInfo.swapchainCount = 1;
2176 presentInfo.pSwapchains = &mSwapchain;
2177 presentInfo.pImageIndices = &mCurrentSwapchainImageIndex;
2178 presentInfo.pResults = nullptr;
2179
2180 VkPresentRegionKHR presentRegion = {};
2181 VkPresentRegionsKHR presentRegions = {};
2182 std::vector<VkRectLayerKHR> vkRects;
2183 if (contextVk->getFeatures().supportsIncrementalPresent.enabled && (n_rects > 0))
2184 {
2185 EGLint width = getWidth();
2186 EGLint height = getHeight();
2187
2188 const EGLint *eglRects = rects;
2189 presentRegion.rectangleCount = n_rects;
2190 vkRects.resize(n_rects);
2191 for (EGLint i = 0; i < n_rects; i++)
2192 {
2193 vkRects[i] = ToVkRectLayer(
2194 eglRects + i * 4, width, height,
2195 contextVk->getFeatures().bottomLeftOriginPresentRegionRectangles.enabled);
2196 }
2197 presentRegion.pRectangles = vkRects.data();
2198
2199 presentRegions.sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
2200 presentRegions.swapchainCount = 1;
2201 presentRegions.pRegions = &presentRegion;
2202
2203 vk::AddToPNextChain(&presentInfo, &presentRegions);
2204 }
2205
2206 VkSwapchainPresentFenceInfoEXT presentFenceInfo = {};
2207 VkSwapchainPresentModeInfoEXT presentModeInfo = {};
2208 vk::Fence presentFence;
2209 VkPresentModeKHR presentMode;
2210 if (contextVk->getFeatures().supportsSwapchainMaintenance1.enabled)
2211 {
2212 ANGLE_VK_TRY(contextVk,
2213 NewFence(contextVk->getDevice(), &mPresentFenceRecycler, &presentFence));
2214
2215 presentFenceInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT;
2216 presentFenceInfo.swapchainCount = 1;
2217 presentFenceInfo.pFences = presentFence.ptr();
2218
2219 vk::AddToPNextChain(&presentInfo, &presentFenceInfo);
2220
2221 // Update the present mode if necessary and possible
2222 if (mSwapchainPresentMode != mDesiredSwapchainPresentMode &&
2223 IsCompatiblePresentMode(mDesiredSwapchainPresentMode, mCompatiblePresentModes.data(),
2224 mCompatiblePresentModes.size()))
2225 {
2226 presentMode = vk::ConvertPresentModeToVkPresentMode(mDesiredSwapchainPresentMode);
2227
2228 presentModeInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT;
2229 presentModeInfo.swapchainCount = 1;
2230 presentModeInfo.pPresentModes = &presentMode;
2231
2232 vk::AddToPNextChain(&presentInfo, &presentModeInfo);
2233
2234 mSwapchainPresentMode = mDesiredSwapchainPresentMode;
2235 }
2236 }
2237
2238 // The ANI semaphore must have been submitted and waited.
2239 ASSERT(!mSwapchainImages[mCurrentSwapchainImageIndex]
2240 .image->getAcquireNextImageSemaphore()
2241 .valid());
2242
2243 renderer->queuePresent(contextVk, contextVk->getPriority(), presentInfo, &mSwapchainStatus);
2244
2245 // Set FrameNumber for the presented image.
2246 mSwapchainImages[mCurrentSwapchainImageIndex].frameNumber = mFrameCount++;
2247
2248 // Place the semaphore in the present history. Schedule pending old swapchains to be destroyed
2249 // at the same time the semaphore for this present can be destroyed.
2250 mPresentHistory.emplace_back();
2251 mPresentHistory.back().semaphore = std::move(presentSemaphore);
2252 if (contextVk->getFeatures().supportsSwapchainMaintenance1.enabled)
2253 {
2254 mPresentHistory.back().imageIndex = kInvalidImageIndex;
2255 mPresentHistory.back().fence = std::move(presentFence);
2256 ANGLE_TRY(cleanUpOldSwapchains(contextVk));
2257 }
2258 else
2259 {
2260 // Image index is used to associate swapSerial in the next present.
2261 mPresentHistory.back().imageIndex = mCurrentSwapchainImageIndex;
2262 mPresentHistory.back().oldSwapchains = std::move(mOldSwapchains);
2263 }
2264
2265 ANGLE_TRY(
2266 computePresentOutOfDate(contextVk, mSwapchainStatus.lastPresentResult, presentOutOfDate));
2267
2268 // Now apply CPU throttle if needed
2269 ANGLE_TRY(throttleCPU(vk::GetImpl(renderer->getDisplay()), swapSerial));
2270
2271 contextVk->resetPerFramePerfCounters();
2272
2273 return angle::Result::Continue;
2274 }
2275
throttleCPU(DisplayVk * displayVk,const QueueSerial & currentSubmitSerial)2276 angle::Result WindowSurfaceVk::throttleCPU(DisplayVk *displayVk,
2277 const QueueSerial ¤tSubmitSerial)
2278 {
2279 // Wait on the oldest serial and replace it with the newest as the circular buffer moves
2280 // forward.
2281 QueueSerial swapSerial = mSwapHistory.front();
2282 mSwapHistory.front() = currentSubmitSerial;
2283 mSwapHistory.next();
2284
2285 if (swapSerial.valid() && !displayVk->getRenderer()->hasQueueSerialFinished(swapSerial))
2286 {
2287 // Make this call after unlocking the EGL lock. The RendererVk::finishQueueSerial is
2288 // necessarily thread-safe because it can get called from any number of GL commands, which
2289 // don't necessarily hold the EGL lock.
2290 //
2291 // As this is an unlocked tail call, it must not access anything else in RendererVk. The
2292 // display passed to |finishQueueSerial| is a |vk::Context|, and the only possible
2293 // modification to it is through |handleError()|.
2294 egl::Display::GetCurrentThreadUnlockedTailCall()->add([displayVk, swapSerial]() {
2295 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::throttleCPU");
2296 (void)displayVk->getRenderer()->finishQueueSerial(displayVk, swapSerial);
2297 });
2298 }
2299
2300 return angle::Result::Continue;
2301 }
2302
cleanUpPresentHistory(vk::Context * context)2303 angle::Result WindowSurfaceVk::cleanUpPresentHistory(vk::Context *context)
2304 {
2305 const VkDevice device = context->getDevice();
2306
2307 while (!mPresentHistory.empty())
2308 {
2309 impl::ImagePresentOperation &presentOperation = mPresentHistory.front();
2310
2311 // If there is no fence associated with the history, check queueSerial.
2312 if (!presentOperation.fence.valid())
2313 {
2314 // |kInvalidImageIndex| is only possible when |VkSwapchainPresentFenceInfoEXT| is used,
2315 // in which case |fence| is always valid.
2316 ASSERT(presentOperation.imageIndex != kInvalidImageIndex);
2317 // If queueSerial already assigned, check if it is finished.
2318 if (!presentOperation.queueSerial.valid() ||
2319 !context->getRenderer()->hasQueueSerialFinished(presentOperation.queueSerial))
2320 {
2321 // Not yet
2322 break;
2323 }
2324 }
2325 // Otherwise check to see if the fence is signaled.
2326 else
2327 {
2328 VkResult result = presentOperation.fence.getStatus(device);
2329 if (result == VK_NOT_READY)
2330 {
2331 // Not yet
2332 break;
2333 }
2334
2335 ANGLE_VK_TRY(context, result);
2336 }
2337
2338 presentOperation.destroy(device, &mPresentFenceRecycler, &mPresentSemaphoreRecycler);
2339 mPresentHistory.pop_front();
2340 }
2341
2342 // The present history can grow indefinitely if a present operation is done on an index that's
2343 // never presented in the future. In that case, there's no queueSerial associated with that
2344 // present operation. Move the offending entry to last, so the resources associated with the
2345 // rest of the present operations can be duly freed.
2346 if (mPresentHistory.size() > mSwapchainImages.size() * 2 &&
2347 !mPresentHistory.front().fence.valid() && !mPresentHistory.front().queueSerial.valid())
2348 {
2349 impl::ImagePresentOperation presentOperation = std::move(mPresentHistory.front());
2350 mPresentHistory.pop_front();
2351
2352 // |kInvalidImageIndex| is only possible when |VkSwapchainPresentFenceInfoEXT| is used, in
2353 // which case |fence| is always valid.
2354 ASSERT(presentOperation.imageIndex != kInvalidImageIndex);
2355
2356 // Move clean up data to the next (now first) present operation, if any. Note that there
2357 // cannot be any clean up data on the rest of the present operations, because the first
2358 // present already gathers every old swapchain to clean up.
2359 ASSERT(!HasAnyOldSwapchains(mPresentHistory));
2360 mPresentHistory.front().oldSwapchains = std::move(presentOperation.oldSwapchains);
2361
2362 // Put the present operation at the end of the queue so it's revisited after the rest of the
2363 // present operations are cleaned up.
2364 mPresentHistory.push_back(std::move(presentOperation));
2365 }
2366
2367 return angle::Result::Continue;
2368 }
2369
cleanUpOldSwapchains(vk::Context * context)2370 angle::Result WindowSurfaceVk::cleanUpOldSwapchains(vk::Context *context)
2371 {
2372 const VkDevice device = context->getDevice();
2373
2374 ASSERT(context->getFeatures().supportsSwapchainMaintenance1.enabled);
2375
2376 while (!mOldSwapchains.empty())
2377 {
2378 impl::SwapchainCleanupData &oldSwapchain = mOldSwapchains.front();
2379 VkResult result = oldSwapchain.getFencesStatus(device);
2380 if (result == VK_NOT_READY)
2381 {
2382 break;
2383 }
2384 ANGLE_VK_TRY(context, result);
2385 oldSwapchain.destroy(device, &mPresentFenceRecycler, &mPresentSemaphoreRecycler);
2386 mOldSwapchains.pop_front();
2387 }
2388
2389 return angle::Result::Continue;
2390 }
2391
swapImpl(const gl::Context * context,const EGLint * rects,EGLint n_rects,const void * pNextChain)2392 angle::Result WindowSurfaceVk::swapImpl(const gl::Context *context,
2393 const EGLint *rects,
2394 EGLint n_rects,
2395 const void *pNextChain)
2396 {
2397 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::swapImpl");
2398
2399 ContextVk *contextVk = vk::GetImpl(context);
2400
2401 // prepareSwap() has already called vkAcquireNextImageKHR if necessary, but its results need to
2402 // be processed now if not already. doDeferredAcquireNextImage() will
2403 // automatically skip the prepareForAcquireNextSwapchainImage() and vkAcquireNextImageKHR calls
2404 // in that case. The swapchain recreation path in
2405 // doDeferredAcquireNextImageWithUsableSwapchain() is acceptable because it only happens if
2406 // previous vkAcquireNextImageKHR failed.
2407 if (needsAcquireImageOrProcessResult())
2408 {
2409 ANGLE_TRY(doDeferredAcquireNextImage(context, false));
2410 }
2411
2412 bool presentOutOfDate = false;
2413 ANGLE_TRY(present(contextVk, rects, n_rects, pNextChain, &presentOutOfDate));
2414
2415 if (!presentOutOfDate)
2416 {
2417 // Defer acquiring the next swapchain image since the swapchain is not out-of-date.
2418 deferAcquireNextImage();
2419 }
2420 else
2421 {
2422 // Immediately try to acquire the next image, which will recognize the out-of-date
2423 // swapchain (potentially because of a rotation change), and recreate it.
2424 ANGLE_VK_TRACE_EVENT_AND_MARKER(contextVk, "Out-of-Date Swapbuffer");
2425 ANGLE_TRY(doDeferredAcquireNextImage(context, presentOutOfDate));
2426 }
2427
2428 RendererVk *renderer = contextVk->getRenderer();
2429 DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
2430 ANGLE_TRY(renderer->syncPipelineCacheVk(displayVk, context));
2431
2432 return angle::Result::Continue;
2433 }
2434
onSharedPresentContextFlush(const gl::Context * context)2435 angle::Result WindowSurfaceVk::onSharedPresentContextFlush(const gl::Context *context)
2436 {
2437 return swapImpl(context, nullptr, 0, nullptr);
2438 }
2439
hasStagedUpdates() const2440 bool WindowSurfaceVk::hasStagedUpdates() const
2441 {
2442 return !needsAcquireImageOrProcessResult() &&
2443 mSwapchainImages[mCurrentSwapchainImageIndex].image->hasStagedUpdatesInAllocatedLevels();
2444 }
2445
setTimestampsEnabled(bool enabled)2446 void WindowSurfaceVk::setTimestampsEnabled(bool enabled)
2447 {
2448 // The frontend has already cached the state, nothing to do.
2449 ASSERT(IsAndroid());
2450 }
2451
deferAcquireNextImage()2452 void WindowSurfaceVk::deferAcquireNextImage()
2453 {
2454 mAcquireOperation.needToAcquireNextSwapchainImage = true;
2455
2456 // Set gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 via subject-observer message-passing
2457 // to the front-end Surface, Framebuffer, and Context classes. The DIRTY_BIT_COLOR_ATTACHMENT_0
2458 // is processed before all other dirty bits. However, since the attachments of the default
2459 // framebuffer cannot change, this bit will be processed before all others. It will cause
2460 // WindowSurfaceVk::getAttachmentRenderTarget() to be called (which will acquire the next image)
2461 // before any RenderTargetVk accesses. The processing of other dirty bits as well as other
2462 // setup for draws and reads will then access a properly-updated RenderTargetVk.
2463 onStateChange(angle::SubjectMessage::SwapchainImageChanged);
2464 }
2465
prepareForAcquireNextSwapchainImage(const gl::Context * context,bool presentOutOfDate,bool * swapchainRecreatedOut)2466 angle::Result WindowSurfaceVk::prepareForAcquireNextSwapchainImage(const gl::Context *context,
2467 bool presentOutOfDate,
2468 bool *swapchainRecreatedOut)
2469 {
2470 ASSERT(!NeedToProcessAcquireNextImageResult(mAcquireOperation.unlockedTryAcquireResult));
2471
2472 ContextVk *contextVk = vk::GetImpl(context);
2473 RendererVk *renderer = contextVk->getRenderer();
2474
2475 // TODO(jmadill): Expose in CommandQueueInterface, or manage in CommandQueue. b/172704839
2476 if (renderer->isAsyncCommandQueueEnabled())
2477 {
2478 ANGLE_TRY(renderer->waitForPresentToBeSubmitted(&mSwapchainStatus));
2479 VkResult result = mSwapchainStatus.lastPresentResult;
2480
2481 // Now that we have the result from the last present need to determine if it's out of date
2482 // or not.
2483 ANGLE_TRY(computePresentOutOfDate(contextVk, result, &presentOutOfDate));
2484 }
2485
2486 return checkForOutOfDateSwapchain(contextVk, presentOutOfDate, swapchainRecreatedOut);
2487 }
2488
doDeferredAcquireNextImage(const gl::Context * context,bool presentOutOfDate)2489 angle::Result WindowSurfaceVk::doDeferredAcquireNextImage(const gl::Context *context,
2490 bool presentOutOfDate)
2491 {
2492 bool swapchainRecreated = false;
2493 // prepareForAcquireNextSwapchainImage() may recreate Swapchain even if there is an image
2494 // acquired. Avoid this, by skipping the prepare call.
2495 if (!NeedToProcessAcquireNextImageResult(mAcquireOperation.unlockedTryAcquireResult))
2496 {
2497 ANGLE_TRY(
2498 prepareForAcquireNextSwapchainImage(context, presentOutOfDate, &swapchainRecreated));
2499 }
2500 return doDeferredAcquireNextImageWithUsableSwapchain(context);
2501 }
2502
doDeferredAcquireNextImageWithUsableSwapchain(const gl::Context * context)2503 angle::Result WindowSurfaceVk::doDeferredAcquireNextImageWithUsableSwapchain(
2504 const gl::Context *context)
2505 {
2506 ContextVk *contextVk = vk::GetImpl(context);
2507
2508 {
2509 // Note: TRACE_EVENT0 is put here instead of inside the function to workaround this issue:
2510 // http://anglebug.com/2927
2511 ANGLE_TRACE_EVENT0("gpu.angle", "acquireNextSwapchainImage");
2512 // Get the next available swapchain image.
2513
2514 VkResult result = acquireNextSwapchainImage(contextVk);
2515
2516 ASSERT(result != VK_SUBOPTIMAL_KHR);
2517 // If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
2518 // continuing.
2519 if (ANGLE_UNLIKELY(result == VK_ERROR_OUT_OF_DATE_KHR))
2520 {
2521 bool swapchainRecreated = false;
2522 ANGLE_TRY(checkForOutOfDateSwapchain(contextVk, true, &swapchainRecreated));
2523 ASSERT(swapchainRecreated);
2524 // Try one more time and bail if we fail
2525 result = acquireNextSwapchainImage(contextVk);
2526 }
2527 ANGLE_VK_TRY(contextVk, result);
2528 }
2529
2530 // Auto-invalidate the contents of the surface. According to EGL, on swap:
2531 //
2532 // - When EGL_BUFFER_DESTROYED is specified, the contents of the color image can be
2533 // invalidated.
2534 // * This is disabled when buffer age has been queried to work around a dEQP test bug.
2535 // - Depth/Stencil can always be invalidated
2536 //
2537 // In all cases, when in shared present mode, swap is implicit and the swap behavior
2538 // doesn't apply so no invalidation is done.
2539 if (!isSharedPresentMode())
2540 {
2541 if (mState.swapBehavior == EGL_BUFFER_DESTROYED && mBufferAgeQueryFrameNumber == 0)
2542 {
2543 mSwapchainImages[mCurrentSwapchainImageIndex].image->invalidateSubresourceContent(
2544 contextVk, gl::LevelIndex(0), 0, 1, nullptr);
2545 if (mColorImageMS.valid())
2546 {
2547 mColorImageMS.invalidateSubresourceContent(contextVk, gl::LevelIndex(0), 0, 1,
2548 nullptr);
2549 }
2550 }
2551 if (mDepthStencilImage.valid())
2552 {
2553 mDepthStencilImage.invalidateSubresourceContent(contextVk, gl::LevelIndex(0), 0, 1,
2554 nullptr);
2555 mDepthStencilImage.invalidateSubresourceStencilContent(contextVk, gl::LevelIndex(0), 0,
2556 1, nullptr);
2557 }
2558 }
2559
2560 return angle::Result::Continue;
2561 }
2562
skipAcquireNextSwapchainImageForSharedPresentMode() const2563 bool WindowSurfaceVk::skipAcquireNextSwapchainImageForSharedPresentMode() const
2564 {
2565 if (isSharedPresentMode())
2566 {
2567 ASSERT(mSwapchainImages.size());
2568 const SwapchainImage &image = mSwapchainImages[0];
2569 if (image.image->valid() &&
2570 image.image->getCurrentImageLayout() == vk::ImageLayout::SharedPresent)
2571 {
2572 return true;
2573 }
2574 }
2575
2576 return false;
2577 }
2578
2579 // This method will either return VK_SUCCESS or VK_ERROR_*. Thus, it is appropriate to ASSERT that
2580 // the return value won't be VK_SUBOPTIMAL_KHR.
acquireNextSwapchainImage(vk::Context * context)2581 VkResult WindowSurfaceVk::acquireNextSwapchainImage(vk::Context *context)
2582 {
2583 VkDevice device = context->getDevice();
2584
2585 if (skipAcquireNextSwapchainImageForSharedPresentMode())
2586 {
2587 ASSERT(!NeedToProcessAcquireNextImageResult(mAcquireOperation.unlockedTryAcquireResult));
2588 // This will check for OUT_OF_DATE when in single image mode. and prevent
2589 // re-AcquireNextImage.
2590 VkResult result = vkGetSwapchainStatusKHR(device, mSwapchain);
2591 if (ANGLE_UNLIKELY(result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR))
2592 {
2593 return result;
2594 }
2595 // Note that an acquire is no longer needed.
2596 mAcquireOperation.needToAcquireNextSwapchainImage = false;
2597 return VK_SUCCESS;
2598 }
2599
2600 // If calling vkAcquireNextImageKHR is necessary, do so first.
2601 if (mAcquireOperation.needToAcquireNextSwapchainImage)
2602 {
2603 TryAcquireNextImageUnlocked(context->getDevice(), mSwapchain, &mAcquireOperation);
2604 }
2605
2606 // If the result of vkAcquireNextImageKHR is not yet processed, do so now.
2607 if (NeedToProcessAcquireNextImageResult(mAcquireOperation.unlockedTryAcquireResult))
2608 {
2609 return postProcessUnlockedTryAcquire(context);
2610 }
2611
2612 return VK_SUCCESS;
2613 }
2614
postProcessUnlockedTryAcquire(vk::Context * context)2615 VkResult WindowSurfaceVk::postProcessUnlockedTryAcquire(vk::Context *context)
2616 {
2617 const VkResult result = mAcquireOperation.unlockedTryAcquireResult.result;
2618 const VkSemaphore acquireImageSemaphore =
2619 mAcquireOperation.unlockedTryAcquireResult.acquireSemaphore;
2620 mAcquireOperation.unlockedTryAcquireResult.acquireSemaphore = VK_NULL_HANDLE;
2621
2622 // VK_SUBOPTIMAL_KHR is ok since we still have an Image that can be presented successfully
2623 if (ANGLE_UNLIKELY(result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR))
2624 {
2625 // vkAcquireNextImageKHR still needs to be called after swapchain recreation:
2626 mAcquireOperation.needToAcquireNextSwapchainImage = true;
2627 return result;
2628 }
2629
2630 mCurrentSwapchainImageIndex = mAcquireOperation.unlockedTryAcquireResult.imageIndex;
2631 ASSERT(!isSharedPresentMode() || mCurrentSwapchainImageIndex == 0);
2632
2633 SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
2634
2635 // Let Image keep the ani semaphore so that it can add to the semaphore wait list if it is
2636 // being used. Image's barrier code will move the semaphore into CommandBufferHelper object
2637 // and then added to waitSemaphores when commands gets flushed and submitted. Since all
2638 // image use after ANI must go through barrier code, this approach is very robust. And since
2639 // this is tracked bny ImageHelper object, it also ensures it only added to command that
2640 // image is actually being referenced, thus avoid potential bugs.
2641 image.image->setAcquireNextImageSemaphore(acquireImageSemaphore);
2642
2643 // Single Image Mode
2644 if (isSharedPresentMode())
2645 {
2646 ASSERT(image.image->valid() &&
2647 image.image->getCurrentImageLayout() != vk::ImageLayout::SharedPresent);
2648 rx::RendererVk *rendererVk = context->getRenderer();
2649 rx::vk::PrimaryCommandBuffer primaryCommandBuffer;
2650 auto protectionType = vk::ConvertProtectionBoolToType(mState.hasProtectedContent());
2651 if (rendererVk->getCommandBufferOneOff(context, protectionType, &primaryCommandBuffer) ==
2652 angle::Result::Continue)
2653 {
2654 VkSemaphore semaphore;
2655 // Note return errors is early exit may leave new Image and Swapchain in unknown state.
2656 image.image->recordWriteBarrierOneOff(context, vk::ImageLayout::SharedPresent,
2657 &primaryCommandBuffer, &semaphore);
2658 ASSERT(semaphore == acquireImageSemaphore);
2659 if (primaryCommandBuffer.end() != VK_SUCCESS)
2660 {
2661 mDesiredSwapchainPresentMode = vk::PresentMode::FifoKHR;
2662 return VK_ERROR_OUT_OF_DATE_KHR;
2663 }
2664 QueueSerial queueSerial;
2665 if (rendererVk->queueSubmitOneOff(
2666 context, std::move(primaryCommandBuffer), protectionType,
2667 egl::ContextPriority::Medium, semaphore,
2668 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
2669 vk::SubmitPolicy::EnsureSubmitted, &queueSerial) != angle::Result::Continue)
2670 {
2671 mDesiredSwapchainPresentMode = vk::PresentMode::FifoKHR;
2672 return VK_ERROR_OUT_OF_DATE_KHR;
2673 }
2674 mUse.setQueueSerial(queueSerial);
2675 }
2676 }
2677
2678 // The semaphore will be waited on in the next flush.
2679 mAcquireOperation.unlockedTryAcquireData.acquireImageSemaphores.next();
2680
2681 // Update RenderTarget pointers to this swapchain image if not multisampling. Note: a possible
2682 // optimization is to defer the |vkAcquireNextImageKHR| call itself to |present()| if
2683 // multisampling, as the swapchain image is essentially unused until then.
2684 if (!mColorImageMS.valid())
2685 {
2686 mColorRenderTarget.updateSwapchainImage(image.image.get(), &image.imageViews, nullptr,
2687 nullptr);
2688 }
2689
2690 // Notify the owning framebuffer there may be staged updates.
2691 if (image.image->hasStagedUpdatesInAllocatedLevels())
2692 {
2693 onStateChange(angle::SubjectMessage::SwapchainImageChanged);
2694 }
2695
2696 ASSERT(!needsAcquireImageOrProcessResult());
2697
2698 return VK_SUCCESS;
2699 }
2700
needsAcquireImageOrProcessResult() const2701 bool WindowSurfaceVk::needsAcquireImageOrProcessResult() const
2702 {
2703 // Go down the acquireNextSwapchainImage() path if either vkAcquireNextImageKHR needs to be
2704 // called, or its results processed
2705 return mAcquireOperation.needToAcquireNextSwapchainImage ||
2706 NeedToProcessAcquireNextImageResult(mAcquireOperation.unlockedTryAcquireResult);
2707 }
2708
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)2709 egl::Error WindowSurfaceVk::postSubBuffer(const gl::Context *context,
2710 EGLint x,
2711 EGLint y,
2712 EGLint width,
2713 EGLint height)
2714 {
2715 // TODO(jmadill)
2716 return egl::NoError();
2717 }
2718
querySurfacePointerANGLE(EGLint attribute,void ** value)2719 egl::Error WindowSurfaceVk::querySurfacePointerANGLE(EGLint attribute, void **value)
2720 {
2721 UNREACHABLE();
2722 return egl::EglBadCurrentSurface();
2723 }
2724
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)2725 egl::Error WindowSurfaceVk::bindTexImage(const gl::Context *context,
2726 gl::Texture *texture,
2727 EGLint buffer)
2728 {
2729 return egl::NoError();
2730 }
2731
releaseTexImage(const gl::Context * context,EGLint buffer)2732 egl::Error WindowSurfaceVk::releaseTexImage(const gl::Context *context, EGLint buffer)
2733 {
2734 return egl::NoError();
2735 }
2736
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)2737 egl::Error WindowSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
2738 EGLuint64KHR * /*msc*/,
2739 EGLuint64KHR * /*sbc*/)
2740 {
2741 UNIMPLEMENTED();
2742 return egl::EglBadAccess();
2743 }
2744
getMscRate(EGLint *,EGLint *)2745 egl::Error WindowSurfaceVk::getMscRate(EGLint * /*numerator*/, EGLint * /*denominator*/)
2746 {
2747 UNIMPLEMENTED();
2748 return egl::EglBadAccess();
2749 }
2750
setSwapInterval(EGLint interval)2751 void WindowSurfaceVk::setSwapInterval(EGLint interval)
2752 {
2753 // Don't let setSwapInterval change presentation mode if using SHARED present.
2754 if (isSharedPresentMode())
2755 {
2756 return;
2757 }
2758
2759 const EGLint minSwapInterval = mState.config->minSwapInterval;
2760 const EGLint maxSwapInterval = mState.config->maxSwapInterval;
2761 ASSERT(minSwapInterval == 0 || minSwapInterval == 1);
2762 ASSERT(maxSwapInterval == 0 || maxSwapInterval == 1);
2763
2764 interval = gl::clamp(interval, minSwapInterval, maxSwapInterval);
2765
2766 mDesiredSwapchainPresentMode = GetDesiredPresentMode(mPresentModes, interval);
2767
2768 // - On mailbox, we need at least three images; one is being displayed to the user until the
2769 // next v-sync, and the application alternatingly renders to the other two, one being
2770 // recorded, and the other queued for presentation if v-sync happens in the meantime.
2771 // - On immediate, we need at least two images; the application alternates between the two
2772 // images.
2773 // - On fifo, we use at least three images. Triple-buffering allows us to present an image,
2774 // have one in the queue, and record in another. Note: on certain configurations (windows +
2775 // nvidia + windowed mode), we could get away with a smaller number.
2776 //
2777 // For simplicity, we always allocate at least three images.
2778 mMinImageCount = std::max(3u, mSurfaceCaps.minImageCount);
2779
2780 // Make sure we don't exceed maxImageCount.
2781 if (mSurfaceCaps.maxImageCount > 0 && mMinImageCount > mSurfaceCaps.maxImageCount)
2782 {
2783 mMinImageCount = mSurfaceCaps.maxImageCount;
2784 }
2785
2786 // On the next swap, if the desired present mode is different from the current one, the
2787 // swapchain will be recreated.
2788 }
2789
getWidth() const2790 EGLint WindowSurfaceVk::getWidth() const
2791 {
2792 return static_cast<EGLint>(mColorRenderTarget.getExtents().width);
2793 }
2794
getRotatedWidth() const2795 EGLint WindowSurfaceVk::getRotatedWidth() const
2796 {
2797 return static_cast<EGLint>(mColorRenderTarget.getRotatedExtents().width);
2798 }
2799
getHeight() const2800 EGLint WindowSurfaceVk::getHeight() const
2801 {
2802 return static_cast<EGLint>(mColorRenderTarget.getExtents().height);
2803 }
2804
getRotatedHeight() const2805 EGLint WindowSurfaceVk::getRotatedHeight() const
2806 {
2807 return static_cast<EGLint>(mColorRenderTarget.getRotatedExtents().height);
2808 }
2809
getUserWidth(const egl::Display * display,EGLint * value) const2810 egl::Error WindowSurfaceVk::getUserWidth(const egl::Display *display, EGLint *value) const
2811 {
2812 DisplayVk *displayVk = vk::GetImpl(display);
2813
2814 if (mSurfaceCaps.currentExtent.width == kSurfaceSizedBySwapchain)
2815 {
2816 // Surface has no intrinsic size; use current size.
2817 *value = getWidth();
2818 return egl::NoError();
2819 }
2820
2821 VkSurfaceCapabilitiesKHR surfaceCaps;
2822 angle::Result result = getUserExtentsImpl(displayVk, &surfaceCaps);
2823 if (result == angle::Result::Continue)
2824 {
2825 // The EGL spec states that value is not written if there is an error
2826 ASSERT(surfaceCaps.currentExtent.width != kSurfaceSizedBySwapchain);
2827 *value = static_cast<EGLint>(surfaceCaps.currentExtent.width);
2828 }
2829 return angle::ToEGL(result, EGL_BAD_SURFACE);
2830 }
2831
getUserHeight(const egl::Display * display,EGLint * value) const2832 egl::Error WindowSurfaceVk::getUserHeight(const egl::Display *display, EGLint *value) const
2833 {
2834 DisplayVk *displayVk = vk::GetImpl(display);
2835
2836 if (mSurfaceCaps.currentExtent.height == kSurfaceSizedBySwapchain)
2837 {
2838 // Surface has no intrinsic size; use current size.
2839 *value = getHeight();
2840 return egl::NoError();
2841 }
2842
2843 VkSurfaceCapabilitiesKHR surfaceCaps;
2844 angle::Result result = getUserExtentsImpl(displayVk, &surfaceCaps);
2845 if (result == angle::Result::Continue)
2846 {
2847 // The EGL spec states that value is not written if there is an error
2848 ASSERT(surfaceCaps.currentExtent.height != kSurfaceSizedBySwapchain);
2849 *value = static_cast<EGLint>(surfaceCaps.currentExtent.height);
2850 }
2851 return angle::ToEGL(result, EGL_BAD_SURFACE);
2852 }
2853
getUserExtentsImpl(DisplayVk * displayVk,VkSurfaceCapabilitiesKHR * surfaceCaps) const2854 angle::Result WindowSurfaceVk::getUserExtentsImpl(DisplayVk *displayVk,
2855 VkSurfaceCapabilitiesKHR *surfaceCaps) const
2856 {
2857 const VkPhysicalDevice &physicalDevice = displayVk->getRenderer()->getPhysicalDevice();
2858
2859 ANGLE_VK_TRY(displayVk,
2860 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface, surfaceCaps));
2861
2862 // With real prerotation, the surface reports the rotated sizes. With emulated prerotation,
2863 // adjust the window extents to match what real pre-rotation would have reported.
2864 if (Is90DegreeRotation(mEmulatedPreTransform))
2865 {
2866 std::swap(surfaceCaps->currentExtent.width, surfaceCaps->currentExtent.height);
2867 }
2868
2869 return angle::Result::Continue;
2870 }
2871
isPostSubBufferSupported() const2872 EGLint WindowSurfaceVk::isPostSubBufferSupported() const
2873 {
2874 // TODO(jmadill)
2875 return EGL_FALSE;
2876 }
2877
getSwapBehavior() const2878 EGLint WindowSurfaceVk::getSwapBehavior() const
2879 {
2880 // TODO(jmadill)
2881 return EGL_BUFFER_DESTROYED;
2882 }
2883
getCurrentFramebuffer(ContextVk * contextVk,FramebufferFetchMode fetchMode,const vk::RenderPass & compatibleRenderPass,const SwapchainResolveMode swapchainResolveMode,vk::MaybeImagelessFramebuffer * framebufferOut)2884 angle::Result WindowSurfaceVk::getCurrentFramebuffer(
2885 ContextVk *contextVk,
2886 FramebufferFetchMode fetchMode,
2887 const vk::RenderPass &compatibleRenderPass,
2888 const SwapchainResolveMode swapchainResolveMode,
2889 vk::MaybeImagelessFramebuffer *framebufferOut)
2890 {
2891 // FramebufferVk dirty-bit processing should ensure that a new image was acquired.
2892 ASSERT(!needsAcquireImageOrProcessResult());
2893
2894 // Track the new fetch mode
2895 mFramebufferFetchMode = fetchMode;
2896
2897 vk::Framebuffer ¤tFramebuffer = chooseFramebuffer(swapchainResolveMode);
2898 if (currentFramebuffer.valid())
2899 {
2900 // Validation layers should detect if the render pass is really compatible.
2901 framebufferOut->setHandle(currentFramebuffer.getHandle());
2902 return angle::Result::Continue;
2903 }
2904
2905 VkFramebufferCreateInfo framebufferInfo = {};
2906 uint32_t attachmentCount = mDepthStencilImage.valid() ? 2u : 1u;
2907
2908 const gl::Extents rotatedExtents = mColorRenderTarget.getRotatedExtents();
2909 std::array<VkImageView, 3> imageViews = {};
2910
2911 if (mDepthStencilImage.valid())
2912 {
2913 const vk::ImageView *imageView = nullptr;
2914 ANGLE_TRY(mDepthStencilRenderTarget.getImageView(contextVk, &imageView));
2915 imageViews[1] = imageView->getHandle();
2916 }
2917
2918 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
2919 framebufferInfo.flags = 0;
2920 framebufferInfo.renderPass = compatibleRenderPass.getHandle();
2921 framebufferInfo.attachmentCount = attachmentCount;
2922 framebufferInfo.pAttachments = imageViews.data();
2923 framebufferInfo.width = static_cast<uint32_t>(rotatedExtents.width);
2924 framebufferInfo.height = static_cast<uint32_t>(rotatedExtents.height);
2925 framebufferInfo.layers = 1;
2926
2927 if (isMultiSampled())
2928 {
2929 const vk::ImageView *imageView = nullptr;
2930 ANGLE_TRY(mColorRenderTarget.getImageView(contextVk, &imageView));
2931 imageViews[0] = imageView->getHandle();
2932
2933 if (swapchainResolveMode == SwapchainResolveMode::Enabled)
2934 {
2935 SwapchainImage &swapchainImage = mSwapchainImages[mCurrentSwapchainImageIndex];
2936
2937 framebufferInfo.attachmentCount = attachmentCount + 1;
2938
2939 ANGLE_TRY(swapchainImage.imageViews.getLevelLayerDrawImageView(
2940 contextVk, *swapchainImage.image, vk::LevelIndex(0), 0,
2941 gl::SrgbWriteControlMode::Default, &imageView));
2942 imageViews[attachmentCount] = imageView->getHandle();
2943
2944 ANGLE_VK_TRY(contextVk, swapchainImage.framebufferResolveMS.init(contextVk->getDevice(),
2945 framebufferInfo));
2946 }
2947 else
2948 {
2949 // If multisampled, there is only a single color image and framebuffer.
2950 ANGLE_VK_TRY(contextVk, mFramebufferMS.init(contextVk->getDevice(), framebufferInfo));
2951 }
2952 }
2953 else
2954 {
2955 SwapchainImage &swapchainImage = mSwapchainImages[mCurrentSwapchainImageIndex];
2956
2957 const vk::ImageView *imageView = nullptr;
2958 ANGLE_TRY(swapchainImage.imageViews.getLevelLayerDrawImageView(
2959 contextVk, *swapchainImage.image, vk::LevelIndex(0), 0,
2960 gl::SrgbWriteControlMode::Default, &imageView));
2961
2962 imageViews[0] = imageView->getHandle();
2963
2964 if (fetchMode == FramebufferFetchMode::Enabled)
2965 {
2966 ANGLE_VK_TRY(contextVk, swapchainImage.fetchFramebuffer.init(contextVk->getDevice(),
2967 framebufferInfo));
2968 }
2969 else
2970 {
2971 ANGLE_VK_TRY(contextVk,
2972 swapchainImage.framebuffer.init(contextVk->getDevice(), framebufferInfo));
2973 }
2974 }
2975
2976 ASSERT(currentFramebuffer.valid());
2977 framebufferOut->setHandle(currentFramebuffer.getHandle());
2978 return angle::Result::Continue;
2979 }
2980
initializeContents(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex)2981 angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context,
2982 GLenum binding,
2983 const gl::ImageIndex &imageIndex)
2984 {
2985 ContextVk *contextVk = vk::GetImpl(context);
2986
2987 if (needsAcquireImageOrProcessResult())
2988 {
2989 // Acquire the next image (previously deferred). Some tests (e.g.
2990 // GenerateMipmapWithRedefineBenchmark.Run/vulkan_webgl) cause this path to be taken,
2991 // because of dirty-object processing.
2992 ANGLE_VK_TRACE_EVENT_AND_MARKER(contextVk, "Initialize Swap Image");
2993 ANGLE_TRY(doDeferredAcquireNextImage(context, false));
2994 }
2995
2996 ASSERT(mSwapchainImages.size() > 0);
2997 ASSERT(mCurrentSwapchainImageIndex < mSwapchainImages.size());
2998
2999 switch (binding)
3000 {
3001 case GL_BACK:
3002 {
3003 vk::ImageHelper *image =
3004 isMultiSampled() ? &mColorImageMS
3005 : mSwapchainImages[mCurrentSwapchainImageIndex].image.get();
3006 image->stageRobustResourceClear(imageIndex);
3007 ANGLE_TRY(image->flushAllStagedUpdates(contextVk));
3008 break;
3009 }
3010 case GL_DEPTH:
3011 case GL_STENCIL:
3012 ASSERT(mDepthStencilImage.valid());
3013 mDepthStencilImage.stageRobustResourceClear(gl::ImageIndex::Make2D(0));
3014 ANGLE_TRY(mDepthStencilImage.flushAllStagedUpdates(contextVk));
3015 break;
3016 default:
3017 UNREACHABLE();
3018 break;
3019 }
3020
3021 return angle::Result::Continue;
3022 }
3023
updateOverlay(ContextVk * contextVk) const3024 void WindowSurfaceVk::updateOverlay(ContextVk *contextVk) const
3025 {
3026 const gl::OverlayType *overlay = contextVk->getOverlay();
3027
3028 // If overlay is disabled, nothing to do.
3029 if (!overlay->isEnabled())
3030 {
3031 return;
3032 }
3033
3034 RendererVk *renderer = contextVk->getRenderer();
3035
3036 uint32_t validationMessageCount = 0;
3037 std::string lastValidationMessage =
3038 renderer->getAndClearLastValidationMessage(&validationMessageCount);
3039 if (validationMessageCount)
3040 {
3041 overlay->getTextWidget(gl::WidgetId::VulkanLastValidationMessage)
3042 ->set(std::move(lastValidationMessage));
3043 overlay->getCountWidget(gl::WidgetId::VulkanValidationMessageCount)
3044 ->set(validationMessageCount);
3045 }
3046
3047 contextVk->updateOverlayOnPresent();
3048 }
3049
overlayHasEnabledWidget(ContextVk * contextVk) const3050 ANGLE_INLINE bool WindowSurfaceVk::overlayHasEnabledWidget(ContextVk *contextVk) const
3051 {
3052 const gl::OverlayType *overlay = contextVk->getOverlay();
3053 OverlayVk *overlayVk = vk::GetImpl(overlay);
3054 return overlayVk && overlayVk->getEnabledWidgetCount() > 0;
3055 }
3056
drawOverlay(ContextVk * contextVk,SwapchainImage * image) const3057 angle::Result WindowSurfaceVk::drawOverlay(ContextVk *contextVk, SwapchainImage *image) const
3058 {
3059 const gl::OverlayType *overlay = contextVk->getOverlay();
3060 OverlayVk *overlayVk = vk::GetImpl(overlay);
3061
3062 // Draw overlay
3063 const vk::ImageView *imageView = nullptr;
3064 ANGLE_TRY(image->imageViews.getLevelLayerDrawImageView(
3065 contextVk, *image->image, vk::LevelIndex(0), 0, gl::SrgbWriteControlMode::Default,
3066 &imageView));
3067 ANGLE_TRY(overlayVk->onPresent(contextVk, image->image.get(), imageView,
3068 Is90DegreeRotation(getPreTransform())));
3069
3070 return angle::Result::Continue;
3071 }
3072
setAutoRefreshEnabled(bool enabled)3073 egl::Error WindowSurfaceVk::setAutoRefreshEnabled(bool enabled)
3074 {
3075 if (enabled && !supportsPresentMode(vk::PresentMode::SharedContinuousRefreshKHR))
3076 {
3077 return egl::EglBadMatch();
3078 }
3079
3080 vk::PresentMode newDesiredSwapchainPresentMode =
3081 enabled ? vk::PresentMode::SharedContinuousRefreshKHR
3082 : vk::PresentMode::SharedDemandRefreshKHR;
3083 // Auto refresh is only applicable in shared present mode
3084 if (isSharedPresentModeDesired() &&
3085 (mDesiredSwapchainPresentMode != newDesiredSwapchainPresentMode))
3086 {
3087 // In cases where the user switches to single buffer and have yet to call eglSwapBuffer,
3088 // enabling/disabling auto refresh should only change mDesiredSwapchainPresentMode as we
3089 // have not yet actually switched to single buffer mode.
3090 mDesiredSwapchainPresentMode = newDesiredSwapchainPresentMode;
3091
3092 // If auto refresh is updated and we are already in single buffer mode we may need to
3093 // recreate swapchain. We need the deferAcquireNextImage() call as unlike setRenderBuffer(),
3094 // the user does not have to call eglSwapBuffers after setting the auto refresh attribute
3095 if (isSharedPresentMode() &&
3096 !IsCompatiblePresentMode(mDesiredSwapchainPresentMode, mCompatiblePresentModes.data(),
3097 mCompatiblePresentModes.size()))
3098 {
3099 deferAcquireNextImage();
3100 }
3101 }
3102
3103 return egl::NoError();
3104 }
3105
getBufferAge(const gl::Context * context,EGLint * age)3106 egl::Error WindowSurfaceVk::getBufferAge(const gl::Context *context, EGLint *age)
3107 {
3108 ANGLE_TRACE_EVENT0("gpu.angle", "getBufferAge");
3109
3110 if (needsAcquireImageOrProcessResult())
3111 {
3112 // Acquire the current image if needed.
3113 egl::Error result =
3114 angle::ToEGL(doDeferredAcquireNextImage(context, false), EGL_BAD_SURFACE);
3115 if (result.isError())
3116 {
3117 return result;
3118 }
3119 }
3120
3121 if (isMultiSampled())
3122 {
3123 *age = 0;
3124 return egl::NoError();
3125 }
3126
3127 if (mBufferAgeQueryFrameNumber == 0)
3128 {
3129 ANGLE_VK_PERF_WARNING(vk::GetImpl(context), GL_DEBUG_SEVERITY_LOW,
3130 "Querying age of a surface will make it retain its content");
3131
3132 mBufferAgeQueryFrameNumber = mFrameCount;
3133 }
3134 if (age != nullptr)
3135 {
3136 uint64_t frameNumber = mSwapchainImages[mCurrentSwapchainImageIndex].frameNumber;
3137 if (frameNumber < mBufferAgeQueryFrameNumber)
3138 {
3139 *age = 0; // Has not been used for rendering yet or since age was queried, no age.
3140 }
3141 else
3142 {
3143 *age = static_cast<EGLint>(mFrameCount - frameNumber);
3144 }
3145 }
3146 return egl::NoError();
3147 }
3148
supportsPresentMode(vk::PresentMode presentMode) const3149 bool WindowSurfaceVk::supportsPresentMode(vk::PresentMode presentMode) const
3150 {
3151 return (std::find(mPresentModes.begin(), mPresentModes.end(), presentMode) !=
3152 mPresentModes.end());
3153 }
3154
setRenderBuffer(EGLint renderBuffer)3155 egl::Error WindowSurfaceVk::setRenderBuffer(EGLint renderBuffer)
3156 {
3157 if (renderBuffer == EGL_SINGLE_BUFFER)
3158 {
3159 vk::PresentMode presentMode = mState.autoRefreshEnabled
3160 ? vk::PresentMode::SharedContinuousRefreshKHR
3161 : vk::PresentMode::SharedDemandRefreshKHR;
3162 if (!supportsPresentMode(presentMode))
3163 {
3164 return egl::EglBadMatch();
3165 }
3166 mDesiredSwapchainPresentMode = presentMode;
3167 }
3168 else // EGL_BACK_BUFFER
3169 {
3170 mDesiredSwapchainPresentMode = vk::PresentMode::FifoKHR;
3171 }
3172 return egl::NoError();
3173 }
3174
lockSurface(const egl::Display * display,EGLint usageHint,bool preservePixels,uint8_t ** bufferPtrOut,EGLint * bufferPitchOut)3175 egl::Error WindowSurfaceVk::lockSurface(const egl::Display *display,
3176 EGLint usageHint,
3177 bool preservePixels,
3178 uint8_t **bufferPtrOut,
3179 EGLint *bufferPitchOut)
3180 {
3181 ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::lockSurface");
3182
3183 vk::ImageHelper *image = mSwapchainImages[mCurrentSwapchainImageIndex].image.get();
3184 if (!image->valid())
3185 {
3186 mAcquireOperation.needToAcquireNextSwapchainImage = true;
3187 if (acquireNextSwapchainImage(vk::GetImpl(display)) != VK_SUCCESS)
3188 {
3189 return egl::EglBadAccess();
3190 }
3191 }
3192 image = mSwapchainImages[mCurrentSwapchainImageIndex].image.get();
3193 ASSERT(image->valid());
3194
3195 angle::Result result =
3196 LockSurfaceImpl(vk::GetImpl(display), image, mLockBufferHelper, getWidth(), getHeight(),
3197 usageHint, preservePixels, bufferPtrOut, bufferPitchOut);
3198 return angle::ToEGL(result, EGL_BAD_ACCESS);
3199 }
3200
unlockSurface(const egl::Display * display,bool preservePixels)3201 egl::Error WindowSurfaceVk::unlockSurface(const egl::Display *display, bool preservePixels)
3202 {
3203 vk::ImageHelper *image = mSwapchainImages[mCurrentSwapchainImageIndex].image.get();
3204 ASSERT(image->valid());
3205 ASSERT(mLockBufferHelper.valid());
3206
3207 return angle::ToEGL(UnlockSurfaceImpl(vk::GetImpl(display), image, mLockBufferHelper,
3208 getWidth(), getHeight(), preservePixels),
3209 EGL_BAD_ACCESS);
3210 }
3211
origin() const3212 EGLint WindowSurfaceVk::origin() const
3213 {
3214 return EGL_UPPER_LEFT_KHR;
3215 }
3216
attachToFramebuffer(const gl::Context * context,gl::Framebuffer * framebuffer)3217 egl::Error WindowSurfaceVk::attachToFramebuffer(const gl::Context *context,
3218 gl::Framebuffer *framebuffer)
3219 {
3220 FramebufferVk *framebufferVk = GetImplAs<FramebufferVk>(framebuffer);
3221 ASSERT(!framebufferVk->getBackbuffer());
3222 framebufferVk->setBackbuffer(this);
3223 return egl::NoError();
3224 }
3225
detachFromFramebuffer(const gl::Context * context,gl::Framebuffer * framebuffer)3226 egl::Error WindowSurfaceVk::detachFromFramebuffer(const gl::Context *context,
3227 gl::Framebuffer *framebuffer)
3228 {
3229 FramebufferVk *framebufferVk = GetImplAs<FramebufferVk>(framebuffer);
3230 ASSERT(framebufferVk->getBackbuffer() == this);
3231 framebufferVk->setBackbuffer(nullptr);
3232 return egl::NoError();
3233 }
3234
3235 } // namespace rx
3236