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