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