• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/Surface.h"
16 #include "libANGLE/renderer/vulkan/ContextVk.h"
17 #include "libANGLE/renderer/vulkan/DisplayVk.h"
18 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
19 #include "libANGLE/renderer/vulkan/RendererVk.h"
20 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
21 #include "libANGLE/trace.h"
22 
23 namespace rx
24 {
25 
26 namespace
27 {
GetSampleCount(const egl::Config * config)28 GLint GetSampleCount(const egl::Config *config)
29 {
30     GLint samples = 1;
31     if (config->sampleBuffers && config->samples > 1)
32     {
33         samples = config->samples;
34     }
35     return samples;
36 }
37 
GetDesiredPresentMode(const std::vector<VkPresentModeKHR> & presentModes,EGLint interval)38 VkPresentModeKHR GetDesiredPresentMode(const std::vector<VkPresentModeKHR> &presentModes,
39                                        EGLint interval)
40 {
41     ASSERT(!presentModes.empty());
42 
43     // If v-sync is enabled, use FIFO, which throttles you to the display rate and is guaranteed to
44     // always be supported.
45     if (interval > 0)
46     {
47         return VK_PRESENT_MODE_FIFO_KHR;
48     }
49 
50     // Otherwise, choose either of the following, if available, in order specified here:
51     //
52     // - Mailbox is similar to triple-buffering.
53     // - Immediate is similar to single-buffering.
54     //
55     // If neither is supported, we fallback to FIFO.
56 
57     bool mailboxAvailable   = false;
58     bool immediateAvailable = false;
59 
60     for (VkPresentModeKHR presentMode : presentModes)
61     {
62         switch (presentMode)
63         {
64             case VK_PRESENT_MODE_MAILBOX_KHR:
65                 mailboxAvailable = true;
66                 break;
67             case VK_PRESENT_MODE_IMMEDIATE_KHR:
68                 immediateAvailable = true;
69                 break;
70             default:
71                 break;
72         }
73     }
74 
75     if (immediateAvailable)
76     {
77         return VK_PRESENT_MODE_IMMEDIATE_KHR;
78     }
79 
80     if (mailboxAvailable)
81     {
82         return VK_PRESENT_MODE_MAILBOX_KHR;
83     }
84 
85     // Note again that VK_PRESENT_MODE_FIFO_KHR is guaranteed to be available.
86     return VK_PRESENT_MODE_FIFO_KHR;
87 }
88 
89 constexpr VkImageUsageFlags kSurfaceVKImageUsageFlags =
90     VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
91 constexpr VkImageUsageFlags kSurfaceVKColorImageUsageFlags =
92     kSurfaceVKImageUsageFlags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
93 constexpr VkImageUsageFlags kSurfaceVKDepthStencilImageUsageFlags =
94     kSurfaceVKImageUsageFlags | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
95 
96 }  // namespace
97 
SurfaceVk(const egl::SurfaceState & surfaceState)98 SurfaceVk::SurfaceVk(const egl::SurfaceState &surfaceState) : SurfaceImpl(surfaceState) {}
99 
100 SurfaceVk::~SurfaceVk() = default;
101 
getAttachmentRenderTarget(const gl::Context * context,GLenum binding,const gl::ImageIndex & imageIndex,FramebufferAttachmentRenderTarget ** rtOut)102 angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
103                                                    GLenum binding,
104                                                    const gl::ImageIndex &imageIndex,
105                                                    FramebufferAttachmentRenderTarget **rtOut)
106 {
107     ContextVk *contextVk = vk::GetImpl(context);
108 
109     if (binding == GL_BACK)
110     {
111         ANGLE_TRY(mColorRenderTarget.flushStagedUpdates(contextVk));
112         *rtOut = &mColorRenderTarget;
113     }
114     else
115     {
116         ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
117         ANGLE_TRY(mDepthStencilRenderTarget.flushStagedUpdates(contextVk));
118         *rtOut = &mDepthStencilRenderTarget;
119     }
120 
121     return angle::Result::Continue;
122 }
123 
AttachmentImage()124 OffscreenSurfaceVk::AttachmentImage::AttachmentImage() {}
125 
126 OffscreenSurfaceVk::AttachmentImage::~AttachmentImage() = default;
127 
initialize(DisplayVk * displayVk,EGLint width,EGLint height,const vk::Format & vkFormat,GLint samples)128 angle::Result OffscreenSurfaceVk::AttachmentImage::initialize(DisplayVk *displayVk,
129                                                               EGLint width,
130                                                               EGLint height,
131                                                               const vk::Format &vkFormat,
132                                                               GLint samples)
133 {
134     RendererVk *renderer = displayVk->getRenderer();
135 
136     const angle::Format &textureFormat = vkFormat.imageFormat();
137     bool isDepthOrStencilFormat   = textureFormat.depthBits > 0 || textureFormat.stencilBits > 0;
138     const VkImageUsageFlags usage = isDepthOrStencilFormat ? kSurfaceVKDepthStencilImageUsageFlags
139                                                            : kSurfaceVKColorImageUsageFlags;
140 
141     VkExtent3D extents = {std::max(static_cast<uint32_t>(width), 1u),
142                           std::max(static_cast<uint32_t>(height), 1u), 1u};
143     ANGLE_TRY(image.init(displayVk, gl::TextureType::_2D, extents, vkFormat, samples, usage, 1, 1));
144 
145     VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
146     ANGLE_TRY(image.initMemory(displayVk, renderer->getMemoryProperties(), flags));
147 
148     VkImageAspectFlags aspect = vk::GetFormatAspectFlags(textureFormat);
149 
150     ANGLE_TRY(image.initImageView(displayVk, gl::TextureType::_2D, aspect, gl::SwizzleState(),
151                                   &imageView, 0, 1));
152 
153     // Clear the image if it has emulated channels.
154     image.stageClearIfEmulatedFormat(gl::ImageIndex::Make2D(0), vkFormat);
155 
156     return angle::Result::Continue;
157 }
158 
destroy(const egl::Display * display)159 void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
160 {
161     DisplayVk *displayVk = vk::GetImpl(display);
162 
163     std::vector<vk::GarbageObjectBase> garbageObjects;
164     image.releaseImage(displayVk, &garbageObjects);
165     image.releaseStagingBuffer(displayVk, &garbageObjects);
166 
167     // It should be safe to immediately destroy the backing images of a surface on surface
168     // destruction.
169     // If this assumption is broken, we could track the last submit fence for the last context that
170     // used this surface to garbage collect the surfaces.
171     for (vk::GarbageObjectBase &garbage : garbageObjects)
172     {
173         garbage.destroy(displayVk->getDevice());
174     }
175 
176     imageView.destroy(displayVk->getDevice());
177 }
178 
OffscreenSurfaceVk(const egl::SurfaceState & surfaceState,EGLint width,EGLint height)179 OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState,
180                                        EGLint width,
181                                        EGLint height)
182     : SurfaceVk(surfaceState), mWidth(width), mHeight(height)
183 {
184     mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageView, nullptr, 0, 0);
185     mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
186                                    &mDepthStencilAttachment.imageView, nullptr, 0, 0);
187 }
188 
~OffscreenSurfaceVk()189 OffscreenSurfaceVk::~OffscreenSurfaceVk() {}
190 
initialize(const egl::Display * display)191 egl::Error OffscreenSurfaceVk::initialize(const egl::Display *display)
192 {
193     DisplayVk *displayVk = vk::GetImpl(display);
194     angle::Result result = initializeImpl(displayVk);
195     return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
196 }
197 
initializeImpl(DisplayVk * displayVk)198 angle::Result OffscreenSurfaceVk::initializeImpl(DisplayVk *displayVk)
199 {
200     RendererVk *renderer      = displayVk->getRenderer();
201     const egl::Config *config = mState.config;
202 
203     GLint samples = GetSampleCount(mState.config);
204     ANGLE_VK_CHECK(displayVk, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
205 
206     if (config->renderTargetFormat != GL_NONE)
207     {
208         ANGLE_TRY(mColorAttachment.initialize(
209             displayVk, mWidth, mHeight, renderer->getFormat(config->renderTargetFormat), samples));
210     }
211 
212     if (config->depthStencilFormat != GL_NONE)
213     {
214         ANGLE_TRY(mDepthStencilAttachment.initialize(
215             displayVk, mWidth, mHeight, renderer->getFormat(config->depthStencilFormat), samples));
216     }
217 
218     return angle::Result::Continue;
219 }
220 
destroy(const egl::Display * display)221 void OffscreenSurfaceVk::destroy(const egl::Display *display)
222 {
223     mColorAttachment.destroy(display);
224     mDepthStencilAttachment.destroy(display);
225 }
226 
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)227 FramebufferImpl *OffscreenSurfaceVk::createDefaultFramebuffer(const gl::Context *context,
228                                                               const gl::FramebufferState &state)
229 {
230     RendererVk *renderer = vk::GetImpl(context)->getRenderer();
231 
232     // Use a user FBO for an offscreen RT.
233     return FramebufferVk::CreateUserFBO(renderer, state);
234 }
235 
swap(const gl::Context * context)236 egl::Error OffscreenSurfaceVk::swap(const gl::Context *context)
237 {
238     return egl::NoError();
239 }
240 
postSubBuffer(const gl::Context *,EGLint,EGLint,EGLint,EGLint)241 egl::Error OffscreenSurfaceVk::postSubBuffer(const gl::Context * /*context*/,
242                                              EGLint /*x*/,
243                                              EGLint /*y*/,
244                                              EGLint /*width*/,
245                                              EGLint /*height*/)
246 {
247     return egl::NoError();
248 }
249 
querySurfacePointerANGLE(EGLint,void **)250 egl::Error OffscreenSurfaceVk::querySurfacePointerANGLE(EGLint /*attribute*/, void ** /*value*/)
251 {
252     UNREACHABLE();
253     return egl::EglBadCurrentSurface();
254 }
255 
bindTexImage(const gl::Context *,gl::Texture *,EGLint)256 egl::Error OffscreenSurfaceVk::bindTexImage(const gl::Context * /*context*/,
257                                             gl::Texture * /*texture*/,
258                                             EGLint /*buffer*/)
259 {
260     return egl::NoError();
261 }
262 
releaseTexImage(const gl::Context *,EGLint)263 egl::Error OffscreenSurfaceVk::releaseTexImage(const gl::Context * /*context*/, EGLint /*buffer*/)
264 {
265     return egl::NoError();
266 }
267 
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)268 egl::Error OffscreenSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
269                                              EGLuint64KHR * /*msc*/,
270                                              EGLuint64KHR * /*sbc*/)
271 {
272     UNIMPLEMENTED();
273     return egl::EglBadAccess();
274 }
275 
setSwapInterval(EGLint)276 void OffscreenSurfaceVk::setSwapInterval(EGLint /*interval*/) {}
277 
getWidth() const278 EGLint OffscreenSurfaceVk::getWidth() const
279 {
280     return mWidth;
281 }
282 
getHeight() const283 EGLint OffscreenSurfaceVk::getHeight() const
284 {
285     return mHeight;
286 }
287 
isPostSubBufferSupported() const288 EGLint OffscreenSurfaceVk::isPostSubBufferSupported() const
289 {
290     return EGL_FALSE;
291 }
292 
getSwapBehavior() const293 EGLint OffscreenSurfaceVk::getSwapBehavior() const
294 {
295     return EGL_BUFFER_DESTROYED;
296 }
297 
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)298 angle::Result OffscreenSurfaceVk::initializeContents(const gl::Context *context,
299                                                      const gl::ImageIndex &imageIndex)
300 {
301     ContextVk *contextVk = vk::GetImpl(context);
302 
303     if (mColorAttachment.image.valid())
304     {
305         mColorAttachment.image.stageSubresourceRobustClear(
306             imageIndex, mColorAttachment.image.getFormat().angleFormat());
307         ANGLE_TRY(mColorAttachment.image.flushAllStagedUpdates(contextVk));
308     }
309 
310     if (mDepthStencilAttachment.image.valid())
311     {
312         mDepthStencilAttachment.image.stageSubresourceRobustClear(
313             imageIndex, mDepthStencilAttachment.image.getFormat().angleFormat());
314         ANGLE_TRY(mDepthStencilAttachment.image.flushAllStagedUpdates(contextVk));
315     }
316     return angle::Result::Continue;
317 }
318 
getColorAttachmentImage()319 vk::ImageHelper *OffscreenSurfaceVk::getColorAttachmentImage()
320 {
321     return &mColorAttachment.image;
322 }
323 
324 WindowSurfaceVk::SwapchainImage::SwapchainImage()  = default;
325 WindowSurfaceVk::SwapchainImage::~SwapchainImage() = default;
326 
SwapchainImage(SwapchainImage && other)327 WindowSurfaceVk::SwapchainImage::SwapchainImage(SwapchainImage &&other)
328     : image(std::move(other.image)),
329       imageView(std::move(other.imageView)),
330       framebuffer(std::move(other.framebuffer))
331 {}
332 
333 WindowSurfaceVk::SwapHistory::SwapHistory() = default;
334 
335 WindowSurfaceVk::SwapHistory::~SwapHistory() = default;
336 
destroy(RendererVk * renderer)337 void WindowSurfaceVk::SwapHistory::destroy(RendererVk *renderer)
338 {
339     if (swapchain != VK_NULL_HANDLE)
340     {
341         vkDestroySwapchainKHR(renderer->getDevice(), swapchain, nullptr);
342         swapchain = VK_NULL_HANDLE;
343     }
344 
345     renderer->resetSharedFence(&sharedFence);
346     presentImageSemaphore.destroy(renderer->getDevice());
347 }
348 
waitFence(ContextVk * contextVk)349 angle::Result WindowSurfaceVk::SwapHistory::waitFence(ContextVk *contextVk)
350 {
351     if (sharedFence.isReferenced())
352     {
353         ANGLE_VK_TRY(contextVk, sharedFence.get().wait(contextVk->getDevice(),
354                                                        std::numeric_limits<uint64_t>::max()));
355     }
356     return angle::Result::Continue;
357 }
358 
WindowSurfaceVk(const egl::SurfaceState & surfaceState,EGLNativeWindowType window,EGLint width,EGLint height)359 WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState,
360                                  EGLNativeWindowType window,
361                                  EGLint width,
362                                  EGLint height)
363     : SurfaceVk(surfaceState),
364       mNativeWindowType(window),
365       mSurface(VK_NULL_HANDLE),
366       mInstance(VK_NULL_HANDLE),
367       mSwapchain(VK_NULL_HANDLE),
368       mSwapchainPresentMode(VK_PRESENT_MODE_FIFO_KHR),
369       mDesiredSwapchainPresentMode(VK_PRESENT_MODE_FIFO_KHR),
370       mMinImageCount(0),
371       mPreTransform(VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR),
372       mCompositeAlpha(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR),
373       mCurrentSwapchainImageIndex(0),
374       mCurrentSwapHistoryIndex(0)
375 {
376     // Initialize the color render target with the multisampled targets.  If not multisampled, the
377     // render target will be updated to refer to a swapchain image on every acquire.
378     mColorRenderTarget.init(&mColorImageMS, &mColorImageViewMS, nullptr, 0, 0);
379     mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageView, nullptr, 0, 0);
380 }
381 
~WindowSurfaceVk()382 WindowSurfaceVk::~WindowSurfaceVk()
383 {
384     ASSERT(mSurface == VK_NULL_HANDLE);
385     ASSERT(mSwapchain == VK_NULL_HANDLE);
386 }
387 
destroy(const egl::Display * display)388 void WindowSurfaceVk::destroy(const egl::Display *display)
389 {
390     DisplayVk *displayVk = vk::GetImpl(display);
391     RendererVk *renderer = displayVk->getRenderer();
392     VkDevice device      = renderer->getDevice();
393     VkInstance instance  = renderer->getInstance();
394 
395     // We might not need to flush the pipe here.
396     (void)renderer->queueWaitIdle(displayVk);
397 
398     destroySwapChainImages(displayVk);
399 
400     for (SwapHistory &swap : mSwapHistory)
401     {
402         swap.destroy(renderer);
403     }
404 
405     if (mSwapchain)
406     {
407         vkDestroySwapchainKHR(device, mSwapchain, nullptr);
408         mSwapchain = VK_NULL_HANDLE;
409     }
410 
411     if (mSurface)
412     {
413         vkDestroySurfaceKHR(instance, mSurface, nullptr);
414         mSurface = VK_NULL_HANDLE;
415     }
416 
417     mAcquireImageSemaphore.destroy(device);
418 }
419 
initialize(const egl::Display * display)420 egl::Error WindowSurfaceVk::initialize(const egl::Display *display)
421 {
422     DisplayVk *displayVk = vk::GetImpl(display);
423     angle::Result result = initializeImpl(displayVk);
424     return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
425 }
426 
initializeImpl(DisplayVk * displayVk)427 angle::Result WindowSurfaceVk::initializeImpl(DisplayVk *displayVk)
428 {
429     RendererVk *renderer = displayVk->getRenderer();
430 
431     gl::Extents windowSize;
432     ANGLE_TRY(createSurfaceVk(displayVk, &windowSize));
433 
434     uint32_t presentQueue = 0;
435     ANGLE_TRY(renderer->selectPresentQueueForSurface(displayVk, mSurface, &presentQueue));
436     ANGLE_UNUSED_VARIABLE(presentQueue);
437 
438     const VkPhysicalDevice &physicalDevice = renderer->getPhysicalDevice();
439 
440     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface,
441                                                                       &mSurfaceCaps));
442 
443     // Adjust width and height to the swapchain if necessary.
444     uint32_t width  = mSurfaceCaps.currentExtent.width;
445     uint32_t height = mSurfaceCaps.currentExtent.height;
446 
447     // TODO(jmadill): Support devices which don't support copy. We use this for ReadPixels.
448     ANGLE_VK_CHECK(displayVk,
449                    (mSurfaceCaps.supportedUsageFlags & kSurfaceVKColorImageUsageFlags) ==
450                        kSurfaceVKColorImageUsageFlags,
451                    VK_ERROR_INITIALIZATION_FAILED);
452 
453     EGLAttrib attribWidth  = mState.attributes.get(EGL_WIDTH, 0);
454     EGLAttrib attribHeight = mState.attributes.get(EGL_HEIGHT, 0);
455 
456     if (mSurfaceCaps.currentExtent.width == 0xFFFFFFFFu)
457     {
458         ASSERT(mSurfaceCaps.currentExtent.height == 0xFFFFFFFFu);
459 
460         width  = (attribWidth != 0) ? static_cast<uint32_t>(attribWidth) : windowSize.width;
461         height = (attribHeight != 0) ? static_cast<uint32_t>(attribHeight) : windowSize.height;
462     }
463 
464     gl::Extents extents(static_cast<int>(width), static_cast<int>(height), 1);
465 
466     uint32_t presentModeCount = 0;
467     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, mSurface,
468                                                                       &presentModeCount, nullptr));
469     ASSERT(presentModeCount > 0);
470 
471     mPresentModes.resize(presentModeCount);
472     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfacePresentModesKHR(
473                                 physicalDevice, mSurface, &presentModeCount, mPresentModes.data()));
474 
475     // Select appropriate present mode based on vsync parameter.  Default to 1 (FIFO), though it
476     // will get clamped to the min/max values specified at display creation time.
477     setSwapInterval(renderer->getFeatures().disableFifoPresentMode.enabled ? 0 : 1);
478 
479     // Default to identity transform.
480     mPreTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
481     if ((mSurfaceCaps.supportedTransforms & mPreTransform) == 0)
482     {
483         mPreTransform = mSurfaceCaps.currentTransform;
484     }
485 
486     uint32_t surfaceFormatCount = 0;
487     ANGLE_VK_TRY(displayVk, vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface,
488                                                                  &surfaceFormatCount, nullptr));
489 
490     std::vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatCount);
491     ANGLE_VK_TRY(displayVk,
492                  vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &surfaceFormatCount,
493                                                       surfaceFormats.data()));
494 
495     const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
496     VkFormat nativeFormat    = format.vkImageFormat;
497 
498     if (surfaceFormatCount == 1u && surfaceFormats[0].format == VK_FORMAT_UNDEFINED)
499     {
500         // This is fine.
501     }
502     else
503     {
504         bool foundFormat = false;
505         for (const VkSurfaceFormatKHR &surfaceFormat : surfaceFormats)
506         {
507             if (surfaceFormat.format == nativeFormat)
508             {
509                 foundFormat = true;
510                 break;
511             }
512         }
513 
514         ANGLE_VK_CHECK(displayVk, foundFormat, VK_ERROR_INITIALIZATION_FAILED);
515     }
516 
517     mCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
518     if ((mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) == 0)
519     {
520         mCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
521     }
522     ANGLE_VK_CHECK(displayVk, (mSurfaceCaps.supportedCompositeAlpha & mCompositeAlpha) != 0,
523                    VK_ERROR_INITIALIZATION_FAILED);
524 
525     ANGLE_TRY(createSwapChain(displayVk, extents, VK_NULL_HANDLE));
526 
527     VkResult vkResult = nextSwapchainImage(displayVk);
528     // VK_SUBOPTIMAL_KHR is ok since we still have an Image that can be presented successfully
529     if (ANGLE_UNLIKELY((vkResult != VK_SUCCESS) && (vkResult != VK_SUBOPTIMAL_KHR)))
530     {
531         ANGLE_VK_TRY(displayVk, vkResult);
532     }
533 
534     return angle::Result::Continue;
535 }
536 
recreateSwapchain(ContextVk * contextVk,const gl::Extents & extents,uint32_t swapHistoryIndex)537 angle::Result WindowSurfaceVk::recreateSwapchain(ContextVk *contextVk,
538                                                  const gl::Extents &extents,
539                                                  uint32_t swapHistoryIndex)
540 {
541     VkSwapchainKHR oldSwapchain = mSwapchain;
542     mSwapchain                  = VK_NULL_HANDLE;
543 
544     if (oldSwapchain)
545     {
546         // Note: the old swapchain must be destroyed regardless of whether creating the new
547         // swapchain succeeds.  We can only destroy the swapchain once rendering to all its images
548         // have finished.  We therefore store the handle to the swapchain being destroyed in the
549         // swap history (alongside the serial of the last submission) so it can be destroyed once we
550         // wait on that serial as part of the CPU throttling.
551         mSwapHistory[swapHistoryIndex].swapchain = oldSwapchain;
552     }
553 
554     releaseSwapchainImages(contextVk);
555 
556     return createSwapChain(contextVk, extents, oldSwapchain);
557 }
558 
createSwapChain(vk::Context * context,const gl::Extents & extents,VkSwapchainKHR oldSwapchain)559 angle::Result WindowSurfaceVk::createSwapChain(vk::Context *context,
560                                                const gl::Extents &extents,
561                                                VkSwapchainKHR oldSwapchain)
562 {
563     ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::createSwapchain");
564 
565     ASSERT(mSwapchain == VK_NULL_HANDLE);
566 
567     RendererVk *renderer = context->getRenderer();
568     VkDevice device      = renderer->getDevice();
569 
570     const vk::Format &format = renderer->getFormat(mState.config->renderTargetFormat);
571     VkFormat nativeFormat    = format.vkImageFormat;
572 
573     // We need transfer src for reading back from the backbuffer.
574     constexpr VkImageUsageFlags kImageUsageFlags = kSurfaceVKColorImageUsageFlags;
575 
576     VkSwapchainCreateInfoKHR swapchainInfo = {};
577     swapchainInfo.sType                    = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
578     swapchainInfo.flags                    = 0;
579     swapchainInfo.surface                  = mSurface;
580     swapchainInfo.minImageCount            = mMinImageCount;
581     swapchainInfo.imageFormat              = nativeFormat;
582     swapchainInfo.imageColorSpace          = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
583     // Note: Vulkan doesn't allow 0-width/height swapchains.
584     swapchainInfo.imageExtent.width     = std::max(extents.width, 1);
585     swapchainInfo.imageExtent.height    = std::max(extents.height, 1);
586     swapchainInfo.imageArrayLayers      = 1;
587     swapchainInfo.imageUsage            = kImageUsageFlags;
588     swapchainInfo.imageSharingMode      = VK_SHARING_MODE_EXCLUSIVE;
589     swapchainInfo.queueFamilyIndexCount = 0;
590     swapchainInfo.pQueueFamilyIndices   = nullptr;
591     swapchainInfo.preTransform          = mPreTransform;
592     swapchainInfo.compositeAlpha        = mCompositeAlpha;
593     swapchainInfo.presentMode           = mDesiredSwapchainPresentMode;
594     swapchainInfo.clipped               = VK_TRUE;
595     swapchainInfo.oldSwapchain          = oldSwapchain;
596 
597     // TODO(syoussefi): Once EGL_SWAP_BEHAVIOR_PRESERVED_BIT is supported, the contents of the old
598     // swapchain need to carry over to the new one.  http://anglebug.com/2942
599     ANGLE_VK_TRY(context, vkCreateSwapchainKHR(device, &swapchainInfo, nullptr, &mSwapchain));
600     mSwapchainPresentMode = mDesiredSwapchainPresentMode;
601 
602     // Intialize the swapchain image views.
603     uint32_t imageCount = 0;
604     ANGLE_VK_TRY(context, vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, nullptr));
605 
606     std::vector<VkImage> swapchainImages(imageCount);
607     ANGLE_VK_TRY(context,
608                  vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data()));
609 
610     // If multisampling is enabled, create a multisampled image which gets resolved just prior to
611     // present.
612     GLint samples = GetSampleCount(mState.config);
613     ANGLE_VK_CHECK(context, samples > 0, VK_ERROR_INITIALIZATION_FAILED);
614 
615     VkExtent3D vkExtents;
616     gl_vk::GetExtent(extents, &vkExtents);
617 
618     if (samples > 1)
619     {
620         const VkImageUsageFlags usage = kSurfaceVKColorImageUsageFlags;
621 
622         ANGLE_TRY(mColorImageMS.init(context, gl::TextureType::_2D, vkExtents, format, samples,
623                                      usage, 1, 1));
624         ANGLE_TRY(mColorImageMS.initMemory(context, renderer->getMemoryProperties(),
625                                            VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
626 
627         ANGLE_TRY(mColorImageMS.initImageView(context, gl::TextureType::_2D,
628                                               VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
629                                               &mColorImageViewMS, 0, 1));
630 
631         // Clear the image if it has emulated channels.
632         mColorImageMS.stageClearIfEmulatedFormat(gl::ImageIndex::Make2D(0), format);
633     }
634 
635     mSwapchainImages.resize(imageCount);
636 
637     for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex)
638     {
639         SwapchainImage &member = mSwapchainImages[imageIndex];
640         member.image.init2DWeakReference(swapchainImages[imageIndex], extents, format, 1);
641 
642         if (!mColorImageMS.valid())
643         {
644             // If the multisampled image is used, we don't need a view on the swapchain image, as
645             // it's only used as a resolve destination.  This has the added benefit that we can't
646             // accidentally use this image.
647             ANGLE_TRY(member.image.initImageView(context, gl::TextureType::_2D,
648                                                  VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
649                                                  &member.imageView, 0, 1));
650 
651             // Clear the image if it has emulated channels.  If a multisampled image exists, this
652             // image will be unused until a pre-present resolve, at which point it will be fully
653             // initialized and wouldn't need a clear.
654             member.image.stageClearIfEmulatedFormat(gl::ImageIndex::Make2D(0), format);
655         }
656     }
657 
658     // Initialize depth/stencil if requested.
659     if (mState.config->depthStencilFormat != GL_NONE)
660     {
661         const vk::Format &dsFormat = renderer->getFormat(mState.config->depthStencilFormat);
662 
663         const VkImageUsageFlags dsUsage = kSurfaceVKDepthStencilImageUsageFlags;
664 
665         ANGLE_TRY(mDepthStencilImage.init(context, gl::TextureType::_2D, vkExtents, dsFormat,
666                                           samples, dsUsage, 1, 1));
667         ANGLE_TRY(mDepthStencilImage.initMemory(context, renderer->getMemoryProperties(),
668                                                 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
669 
670         const VkImageAspectFlags aspect = vk::GetDepthStencilAspectFlags(dsFormat.imageFormat());
671 
672         ANGLE_TRY(mDepthStencilImage.initImageView(context, gl::TextureType::_2D, aspect,
673                                                    gl::SwizzleState(), &mDepthStencilImageView, 0,
674                                                    1));
675 
676         // We will need to pass depth/stencil image views to the RenderTargetVk in the future.
677 
678         // Clear the image if it has emulated channels.
679         mDepthStencilImage.stageClearIfEmulatedFormat(gl::ImageIndex::Make2D(0), dsFormat);
680     }
681 
682     return angle::Result::Continue;
683 }
684 
isMultiSampled() const685 bool WindowSurfaceVk::isMultiSampled() const
686 {
687     return mColorImageMS.valid();
688 }
689 
checkForOutOfDateSwapchain(ContextVk * contextVk,uint32_t swapHistoryIndex,bool presentOutOfDate)690 angle::Result WindowSurfaceVk::checkForOutOfDateSwapchain(ContextVk *contextVk,
691                                                           uint32_t swapHistoryIndex,
692                                                           bool presentOutOfDate)
693 {
694     bool swapIntervalChanged = mSwapchainPresentMode != mDesiredSwapchainPresentMode;
695 
696     // If anything has changed, recreate the swapchain.
697     if (swapIntervalChanged || presentOutOfDate ||
698         contextVk->getRenderer()->getFeatures().perFrameWindowSizeQuery.enabled)
699     {
700         gl::Extents swapchainExtents(getWidth(), getHeight(), 1);
701 
702         gl::Extents currentExtents;
703         ANGLE_TRY(getCurrentWindowSize(contextVk, &currentExtents));
704 
705         // If window size has changed, check with surface capabilities.  It has been observed on
706         // Android that `getCurrentWindowSize()` returns 1920x1080 for example, while surface
707         // capabilities returns the size the surface was created with.
708         if (currentExtents != swapchainExtents)
709         {
710             const VkPhysicalDevice &physicalDevice = contextVk->getRenderer()->getPhysicalDevice();
711             ANGLE_VK_TRY(contextVk, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
712                                         physicalDevice, mSurface, &mSurfaceCaps));
713 
714             uint32_t width  = mSurfaceCaps.currentExtent.width;
715             uint32_t height = mSurfaceCaps.currentExtent.height;
716 
717             if (width != 0xFFFFFFFFu)
718             {
719                 ASSERT(height != 0xFFFFFFFFu);
720                 currentExtents.width  = width;
721                 currentExtents.height = height;
722             }
723         }
724 
725         // Check for window resize and recreate swapchain if necessary.
726         // Work-around for some device which does not return OUT_OF_DATE after window resizing
727         if (swapIntervalChanged || presentOutOfDate || currentExtents != swapchainExtents)
728         {
729             ANGLE_TRY(recreateSwapchain(contextVk, currentExtents, swapHistoryIndex));
730         }
731     }
732 
733     return angle::Result::Continue;
734 }
735 
releaseSwapchainImages(ContextVk * contextVk)736 void WindowSurfaceVk::releaseSwapchainImages(ContextVk *contextVk)
737 {
738     if (mDepthStencilImage.valid())
739     {
740         Serial depthStencilSerial = mDepthStencilImage.getStoredQueueSerial();
741         mDepthStencilImage.releaseImage(contextVk);
742         mDepthStencilImage.releaseStagingBuffer(contextVk);
743 
744         if (mDepthStencilImageView.valid())
745         {
746             contextVk->releaseObject(depthStencilSerial, &mDepthStencilImageView);
747         }
748     }
749 
750     if (mColorImageMS.valid())
751     {
752         Serial serial = mColorImageMS.getStoredQueueSerial();
753         mColorImageMS.releaseImage(contextVk);
754         mColorImageMS.releaseStagingBuffer(contextVk);
755 
756         contextVk->releaseObject(serial, &mColorImageViewMS);
757         contextVk->releaseObject(serial, &mFramebufferMS);
758     }
759 
760     for (SwapchainImage &swapchainImage : mSwapchainImages)
761     {
762         Serial imageSerial = swapchainImage.image.getStoredQueueSerial();
763 
764         // We don't own the swapchain image handles, so we just remove our reference to it.
765         swapchainImage.image.resetImageWeakReference();
766         swapchainImage.image.destroy(contextVk->getDevice());
767 
768         if (swapchainImage.imageView.valid())
769         {
770             contextVk->releaseObject(imageSerial, &swapchainImage.imageView);
771         }
772 
773         if (swapchainImage.framebuffer.valid())
774         {
775             contextVk->releaseObject(imageSerial, &swapchainImage.framebuffer);
776         }
777     }
778 
779     mSwapchainImages.clear();
780 }
781 
destroySwapChainImages(DisplayVk * displayVk)782 void WindowSurfaceVk::destroySwapChainImages(DisplayVk *displayVk)
783 {
784     std::vector<vk::GarbageObjectBase> garbageObjects;
785     if (mDepthStencilImage.valid())
786     {
787         mDepthStencilImage.releaseImage(displayVk, &garbageObjects);
788         mDepthStencilImage.releaseStagingBuffer(displayVk, &garbageObjects);
789 
790         if (mDepthStencilImageView.valid())
791         {
792             mDepthStencilImageView.dumpResources(&garbageObjects);
793         }
794     }
795 
796     if (mColorImageMS.valid())
797     {
798         mColorImageMS.releaseImage(displayVk, &garbageObjects);
799         mColorImageMS.releaseImage(displayVk, &garbageObjects);
800 
801         mColorImageViewMS.dumpResources(&garbageObjects);
802         mFramebufferMS.dumpResources(&garbageObjects);
803     }
804 
805     for (vk::GarbageObjectBase &garbage : garbageObjects)
806     {
807         garbage.destroy(displayVk->getDevice());
808     }
809 
810     for (SwapchainImage &swapchainImage : mSwapchainImages)
811     {
812         // We don't own the swapchain image handles, so we just remove our reference to it.
813         swapchainImage.image.resetImageWeakReference();
814         swapchainImage.image.destroy(displayVk->getDevice());
815 
816         if (swapchainImage.imageView.valid())
817         {
818             swapchainImage.imageView.destroy(displayVk->getDevice());
819         }
820 
821         if (swapchainImage.framebuffer.valid())
822         {
823             swapchainImage.framebuffer.destroy(displayVk->getDevice());
824         }
825     }
826 
827     mSwapchainImages.clear();
828 }
829 
createDefaultFramebuffer(const gl::Context * context,const gl::FramebufferState & state)830 FramebufferImpl *WindowSurfaceVk::createDefaultFramebuffer(const gl::Context *context,
831                                                            const gl::FramebufferState &state)
832 {
833     RendererVk *renderer = vk::GetImpl(context)->getRenderer();
834     return FramebufferVk::CreateDefaultFBO(renderer, state, this);
835 }
836 
swapWithDamage(const gl::Context * context,EGLint * rects,EGLint n_rects)837 egl::Error WindowSurfaceVk::swapWithDamage(const gl::Context *context,
838                                            EGLint *rects,
839                                            EGLint n_rects)
840 {
841     DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
842     angle::Result result = swapImpl(context, rects, n_rects);
843     return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
844 }
845 
swap(const gl::Context * context)846 egl::Error WindowSurfaceVk::swap(const gl::Context *context)
847 {
848     DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
849     angle::Result result = swapImpl(context, nullptr, 0);
850     return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE);
851 }
852 
present(ContextVk * contextVk,EGLint * rects,EGLint n_rects,bool * presentOutOfDate)853 angle::Result WindowSurfaceVk::present(ContextVk *contextVk,
854                                        EGLint *rects,
855                                        EGLint n_rects,
856                                        bool *presentOutOfDate)
857 {
858     ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present");
859 
860     // Throttle the submissions to avoid getting too far ahead of the GPU.
861     SwapHistory &swap = mSwapHistory[mCurrentSwapHistoryIndex];
862     {
863         ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present: Throttle CPU");
864         ANGLE_TRY(swap.waitFence(contextVk));
865         swap.destroy(contextVk->getRenderer());
866     }
867 
868     SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
869 
870     vk::CommandBuffer *swapCommands = nullptr;
871     if (mColorImageMS.valid())
872     {
873         // Transition the multisampled image to TRANSFER_SRC for resolve.
874         vk::CommandBuffer *multisampledTransition = nullptr;
875         ANGLE_TRY(mColorImageMS.recordCommands(contextVk, &multisampledTransition));
876 
877         mColorImageMS.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferSrc,
878                                    multisampledTransition);
879 
880         // Setup graph dependency between the swapchain image and the multisampled one.
881         image.image.addReadDependency(&mColorImageMS);
882 
883         VkImageResolve resolveRegion                = {};
884         resolveRegion.srcSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;
885         resolveRegion.srcSubresource.mipLevel       = 0;
886         resolveRegion.srcSubresource.baseArrayLayer = 0;
887         resolveRegion.srcSubresource.layerCount     = 1;
888         resolveRegion.srcOffset                     = {};
889         resolveRegion.dstSubresource                = resolveRegion.srcSubresource;
890         resolveRegion.dstOffset                     = {};
891         resolveRegion.extent                        = image.image.getExtents();
892 
893         ANGLE_TRY(image.image.recordCommands(contextVk, &swapCommands));
894         mColorImageMS.resolve(&image.image, resolveRegion, swapCommands);
895     }
896     else
897     {
898         ANGLE_TRY(image.image.recordCommands(contextVk, &swapCommands));
899     }
900     image.image.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::Present, swapCommands);
901 
902     ANGLE_VK_TRY(contextVk, swap.presentImageSemaphore.init(contextVk->getDevice()));
903 
904     ANGLE_TRY(contextVk->flushImpl(&swap.presentImageSemaphore));
905 
906     VkPresentInfoKHR presentInfo   = {};
907     presentInfo.sType              = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
908     presentInfo.waitSemaphoreCount = 1;
909     presentInfo.pWaitSemaphores    = swap.presentImageSemaphore.ptr();
910     presentInfo.swapchainCount     = 1;
911     presentInfo.pSwapchains        = &mSwapchain;
912     presentInfo.pImageIndices      = &mCurrentSwapchainImageIndex;
913     presentInfo.pResults           = nullptr;
914 
915     VkPresentRegionKHR presentRegion   = {};
916     VkPresentRegionsKHR presentRegions = {};
917     std::vector<VkRectLayerKHR> vkRects;
918     if (contextVk->getFeatures().supportsIncrementalPresent.enabled && (n_rects > 0))
919     {
920         EGLint width  = getWidth();
921         EGLint height = getHeight();
922 
923         EGLint *eglRects             = rects;
924         presentRegion.rectangleCount = n_rects;
925         vkRects.resize(n_rects);
926         for (EGLint i = 0; i < n_rects; i++)
927         {
928             VkRectLayerKHR &rect = vkRects[i];
929 
930             // Make sure the damage rects are within swapchain bounds.
931             rect.offset.x      = gl::clamp(*eglRects++, 0, width);
932             rect.offset.y      = gl::clamp(*eglRects++, 0, height);
933             rect.extent.width  = gl::clamp(*eglRects++, 0, width - rect.offset.x);
934             rect.extent.height = gl::clamp(*eglRects++, 0, height - rect.offset.y);
935             rect.layer         = 0;
936         }
937         presentRegion.pRectangles = vkRects.data();
938 
939         presentRegions.sType          = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
940         presentRegions.pNext          = nullptr;
941         presentRegions.swapchainCount = 1;
942         presentRegions.pRegions       = &presentRegion;
943 
944         presentInfo.pNext = &presentRegions;
945     }
946 
947     // Update the swap history for this presentation
948     swap.sharedFence = contextVk->getLastSubmittedFence();
949     ASSERT(!mAcquireImageSemaphore.valid());
950 
951     ++mCurrentSwapHistoryIndex;
952     mCurrentSwapHistoryIndex =
953         mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex;
954 
955     VkResult result = contextVk->getRenderer()->queuePresent(presentInfo);
956 
957     // If OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
958     // continuing.
959     // If VK_SUBOPTIMAL_KHR is returned we it's because the device orientation changed and we should
960     // recreate the swapchain with a new window orientation. We aren't quite ready for that so just
961     // ignore for now.
962     // TODO: Check for preRotation: http://anglebug.com/3502
963     *presentOutOfDate = result == VK_ERROR_OUT_OF_DATE_KHR;
964     if (!*presentOutOfDate && result != VK_SUBOPTIMAL_KHR)
965     {
966         ANGLE_VK_TRY(contextVk, result);
967     }
968 
969     return angle::Result::Continue;
970 }
971 
swapImpl(const gl::Context * context,EGLint * rects,EGLint n_rects)972 angle::Result WindowSurfaceVk::swapImpl(const gl::Context *context, EGLint *rects, EGLint n_rects)
973 {
974     ANGLE_TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::swapImpl");
975 
976     ContextVk *contextVk = vk::GetImpl(context);
977     DisplayVk *displayVk = vk::GetImpl(context->getDisplay());
978 
979     bool presentOutOfDate = false;
980     // Save this now, since present() will increment the value.
981     uint32_t currentSwapHistoryIndex = static_cast<uint32_t>(mCurrentSwapHistoryIndex);
982 
983     ANGLE_TRY(present(contextVk, rects, n_rects, &presentOutOfDate));
984 
985     ANGLE_TRY(checkForOutOfDateSwapchain(contextVk, currentSwapHistoryIndex, presentOutOfDate));
986 
987     {
988         // Note: TRACE_EVENT0 is put here instead of inside the function to workaround this issue:
989         // http://anglebug.com/2927
990         ANGLE_TRACE_EVENT0("gpu.angle", "nextSwapchainImage");
991         // Get the next available swapchain image.
992 
993         VkResult result = nextSwapchainImage(contextVk);
994         // If SUBOPTIMAL/OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain
995         // before continuing.
996         if (ANGLE_UNLIKELY((result == VK_ERROR_OUT_OF_DATE_KHR) || (result == VK_SUBOPTIMAL_KHR)))
997         {
998             ANGLE_TRY(checkForOutOfDateSwapchain(contextVk, currentSwapHistoryIndex, true));
999             // Try one more time and bail if we fail
1000             result = nextSwapchainImage(contextVk);
1001         }
1002         ANGLE_VK_TRY(contextVk, result);
1003     }
1004 
1005     RendererVk *renderer = contextVk->getRenderer();
1006     ANGLE_TRY(renderer->syncPipelineCacheVk(displayVk));
1007 
1008     return angle::Result::Continue;
1009 }
1010 
nextSwapchainImage(vk::Context * context)1011 VkResult WindowSurfaceVk::nextSwapchainImage(vk::Context *context)
1012 {
1013     VkDevice device = context->getDevice();
1014 
1015     vk::Scoped<vk::Semaphore> acquireImageSemaphore(device);
1016     VkResult result = acquireImageSemaphore.get().init(device);
1017     if (ANGLE_UNLIKELY(result != VK_SUCCESS))
1018     {
1019         return result;
1020     }
1021 
1022     result = vkAcquireNextImageKHR(device, mSwapchain, UINT64_MAX,
1023                                    acquireImageSemaphore.get().getHandle(), VK_NULL_HANDLE,
1024                                    &mCurrentSwapchainImageIndex);
1025     if (ANGLE_UNLIKELY(result != VK_SUCCESS))
1026     {
1027         return result;
1028     }
1029 
1030     // The semaphore will be waited on in the next flush.
1031     mAcquireImageSemaphore = acquireImageSemaphore.release();
1032 
1033     SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
1034 
1035     // This swap chain image is new, reset any dependency information it has.
1036     //
1037     // When the Vulkan backend is multithreading, different contexts can have very different current
1038     // serials.  If a surface is rendered to by multiple contexts in different frames, the last
1039     // context to write a particular swap chain image has no bearing on the current context writing
1040     // to that image.
1041     //
1042     // Clear the image's queue serial because it's possible that it appears to be in the future to
1043     // the next context that writes to the image.
1044     image.image.resetQueueSerial();
1045 
1046     // Update RenderTarget pointers to this swapchain image if not multisampling.  Note: a possible
1047     // optimization is to defer the |vkAcquireNextImageKHR| call itself to |present()| if
1048     // multisampling, as the swapchain image is essentially unused until then.
1049     if (!mColorImageMS.valid())
1050     {
1051         mColorRenderTarget.updateSwapchainImage(&image.image, &image.imageView);
1052     }
1053 
1054     return VK_SUCCESS;
1055 }
1056 
postSubBuffer(const gl::Context * context,EGLint x,EGLint y,EGLint width,EGLint height)1057 egl::Error WindowSurfaceVk::postSubBuffer(const gl::Context *context,
1058                                           EGLint x,
1059                                           EGLint y,
1060                                           EGLint width,
1061                                           EGLint height)
1062 {
1063     // TODO(jmadill)
1064     return egl::NoError();
1065 }
1066 
querySurfacePointerANGLE(EGLint attribute,void ** value)1067 egl::Error WindowSurfaceVk::querySurfacePointerANGLE(EGLint attribute, void **value)
1068 {
1069     UNREACHABLE();
1070     return egl::EglBadCurrentSurface();
1071 }
1072 
bindTexImage(const gl::Context * context,gl::Texture * texture,EGLint buffer)1073 egl::Error WindowSurfaceVk::bindTexImage(const gl::Context *context,
1074                                          gl::Texture *texture,
1075                                          EGLint buffer)
1076 {
1077     return egl::NoError();
1078 }
1079 
releaseTexImage(const gl::Context * context,EGLint buffer)1080 egl::Error WindowSurfaceVk::releaseTexImage(const gl::Context *context, EGLint buffer)
1081 {
1082     return egl::NoError();
1083 }
1084 
getSyncValues(EGLuint64KHR *,EGLuint64KHR *,EGLuint64KHR *)1085 egl::Error WindowSurfaceVk::getSyncValues(EGLuint64KHR * /*ust*/,
1086                                           EGLuint64KHR * /*msc*/,
1087                                           EGLuint64KHR * /*sbc*/)
1088 {
1089     UNIMPLEMENTED();
1090     return egl::EglBadAccess();
1091 }
1092 
setSwapInterval(EGLint interval)1093 void WindowSurfaceVk::setSwapInterval(EGLint interval)
1094 {
1095     const EGLint minSwapInterval = mState.config->minSwapInterval;
1096     const EGLint maxSwapInterval = mState.config->maxSwapInterval;
1097     ASSERT(minSwapInterval == 0 || minSwapInterval == 1);
1098     ASSERT(maxSwapInterval == 0 || maxSwapInterval == 1);
1099 
1100     interval = gl::clamp(interval, minSwapInterval, maxSwapInterval);
1101 
1102     mDesiredSwapchainPresentMode = GetDesiredPresentMode(mPresentModes, interval);
1103 
1104     // - On mailbox, we need at least three images; one is being displayed to the user until the
1105     //   next v-sync, and the application alternatingly renders to the other two, one being
1106     //   recorded, and the other queued for presentation if v-sync happens in the meantime.
1107     // - On immediate, we need at least two images; the application alternates between the two
1108     //   images.
1109     // - On fifo, we use at least three images.  Triple-buffering allows us to present an image,
1110     //   have one in the queue, and record in another.  Note: on certain configurations (windows +
1111     //   nvidia + windowed mode), we could get away with a smaller number.
1112     //
1113     // For simplicity, we always allocate at least three images.
1114     mMinImageCount = std::max(3u, mSurfaceCaps.minImageCount);
1115 
1116     // Make sure we don't exceed maxImageCount.
1117     if (mSurfaceCaps.maxImageCount > 0 && mMinImageCount > mSurfaceCaps.maxImageCount)
1118     {
1119         mMinImageCount = mSurfaceCaps.maxImageCount;
1120     }
1121 
1122     // On the next swap, if the desired present mode is different from the current one, the
1123     // swapchain will be recreated.
1124 }
1125 
getWidth() const1126 EGLint WindowSurfaceVk::getWidth() const
1127 {
1128     return static_cast<EGLint>(mColorRenderTarget.getExtents().width);
1129 }
1130 
getHeight() const1131 EGLint WindowSurfaceVk::getHeight() const
1132 {
1133     return static_cast<EGLint>(mColorRenderTarget.getExtents().height);
1134 }
1135 
isPostSubBufferSupported() const1136 EGLint WindowSurfaceVk::isPostSubBufferSupported() const
1137 {
1138     // TODO(jmadill)
1139     return EGL_FALSE;
1140 }
1141 
getSwapBehavior() const1142 EGLint WindowSurfaceVk::getSwapBehavior() const
1143 {
1144     // TODO(jmadill)
1145     return EGL_BUFFER_DESTROYED;
1146 }
1147 
getCurrentFramebuffer(vk::Context * context,const vk::RenderPass & compatibleRenderPass,vk::Framebuffer ** framebufferOut)1148 angle::Result WindowSurfaceVk::getCurrentFramebuffer(vk::Context *context,
1149                                                      const vk::RenderPass &compatibleRenderPass,
1150                                                      vk::Framebuffer **framebufferOut)
1151 {
1152     vk::Framebuffer &currentFramebuffer =
1153         isMultiSampled() ? mFramebufferMS
1154                          : mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
1155 
1156     if (currentFramebuffer.valid())
1157     {
1158         // Validation layers should detect if the render pass is really compatible.
1159         *framebufferOut = &currentFramebuffer;
1160         return angle::Result::Continue;
1161     }
1162 
1163     VkFramebufferCreateInfo framebufferInfo = {};
1164 
1165     const gl::Extents extents             = mColorRenderTarget.getExtents();
1166     std::array<VkImageView, 2> imageViews = {{VK_NULL_HANDLE, mDepthStencilImageView.getHandle()}};
1167 
1168     framebufferInfo.sType           = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1169     framebufferInfo.flags           = 0;
1170     framebufferInfo.renderPass      = compatibleRenderPass.getHandle();
1171     framebufferInfo.attachmentCount = (mDepthStencilImage.valid() ? 2u : 1u);
1172     framebufferInfo.pAttachments    = imageViews.data();
1173     framebufferInfo.width           = static_cast<uint32_t>(extents.width);
1174     framebufferInfo.height          = static_cast<uint32_t>(extents.height);
1175     framebufferInfo.layers          = 1;
1176 
1177     if (isMultiSampled())
1178     {
1179         // If multisampled, there is only a single color image and framebuffer.
1180         imageViews[0] = mColorImageViewMS.getHandle();
1181         ANGLE_VK_TRY(context, mFramebufferMS.init(context->getDevice(), framebufferInfo));
1182     }
1183     else
1184     {
1185         for (SwapchainImage &swapchainImage : mSwapchainImages)
1186         {
1187             imageViews[0] = swapchainImage.imageView.getHandle();
1188             ANGLE_VK_TRY(context,
1189                          swapchainImage.framebuffer.init(context->getDevice(), framebufferInfo));
1190         }
1191     }
1192 
1193     ASSERT(currentFramebuffer.valid());
1194     *framebufferOut = &currentFramebuffer;
1195     return angle::Result::Continue;
1196 }
1197 
getAcquireImageSemaphore()1198 vk::Semaphore WindowSurfaceVk::getAcquireImageSemaphore()
1199 {
1200     return std::move(mAcquireImageSemaphore);
1201 }
1202 
initializeContents(const gl::Context * context,const gl::ImageIndex & imageIndex)1203 angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context,
1204                                                   const gl::ImageIndex &imageIndex)
1205 {
1206     ContextVk *contextVk = vk::GetImpl(context);
1207 
1208     ASSERT(mSwapchainImages.size() > 0);
1209     ASSERT(mCurrentSwapchainImageIndex < mSwapchainImages.size());
1210 
1211     vk::ImageHelper *image =
1212         isMultiSampled() ? &mColorImageMS : &mSwapchainImages[mCurrentSwapchainImageIndex].image;
1213     image->stageSubresourceRobustClear(imageIndex, image->getFormat().angleFormat());
1214     ANGLE_TRY(image->flushAllStagedUpdates(contextVk));
1215 
1216     if (mDepthStencilImage.valid())
1217     {
1218         mDepthStencilImage.stageSubresourceRobustClear(
1219             gl::ImageIndex::Make2D(0), mDepthStencilImage.getFormat().angleFormat());
1220         ANGLE_TRY(mDepthStencilImage.flushAllStagedUpdates(contextVk));
1221     }
1222 
1223     return angle::Result::Continue;
1224 }
1225 
1226 }  // namespace rx
1227