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