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, ¤tExtents));
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 ¤tFramebuffer =
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 = ¤tFramebuffer;
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 = ¤tFramebuffer;
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