• 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.h:
7 //    Defines the class interface for SurfaceVk, implementing SurfaceImpl.
8 //
9 
10 #ifndef LIBANGLE_RENDERER_VULKAN_SURFACEVK_H_
11 #define LIBANGLE_RENDERER_VULKAN_SURFACEVK_H_
12 
13 #include "common/CircularBuffer.h"
14 #include "common/SimpleMutex.h"
15 #include "common/vulkan/vk_headers.h"
16 #include "libANGLE/renderer/SurfaceImpl.h"
17 #include "libANGLE/renderer/vulkan/CommandProcessor.h"
18 #include "libANGLE/renderer/vulkan/RenderTargetVk.h"
19 #include "libANGLE/renderer/vulkan/vk_helpers.h"
20 
21 namespace rx
22 {
23 class SurfaceVk : public SurfaceImpl, public angle::ObserverInterface, public vk::Resource
24 {
25   public:
26     angle::Result getAttachmentRenderTarget(const gl::Context *context,
27                                             GLenum binding,
28                                             const gl::ImageIndex &imageIndex,
29                                             GLsizei samples,
30                                             FramebufferAttachmentRenderTarget **rtOut) override;
31 
32   protected:
33     SurfaceVk(const egl::SurfaceState &surfaceState);
34     ~SurfaceVk() override;
35 
36     void destroy(const egl::Display *display) override;
37     // We monitor the staging buffer for changes. This handles staged data from outside this class.
38     void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override;
39 
40     RenderTargetVk mColorRenderTarget;
41     RenderTargetVk mDepthStencilRenderTarget;
42 };
43 
44 class OffscreenSurfaceVk : public SurfaceVk
45 {
46   public:
47     OffscreenSurfaceVk(const egl::SurfaceState &surfaceState, vk::Renderer *renderer);
48     ~OffscreenSurfaceVk() override;
49 
50     egl::Error initialize(const egl::Display *display) override;
51     void destroy(const egl::Display *display) override;
52 
53     egl::Error unMakeCurrent(const gl::Context *context) override;
getColorImage()54     const vk::ImageHelper *getColorImage() const { return &mColorAttachment.image; }
55 
56     egl::Error swap(const gl::Context *context) override;
57     egl::Error postSubBuffer(const gl::Context *context,
58                              EGLint x,
59                              EGLint y,
60                              EGLint width,
61                              EGLint height) override;
62     egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override;
63     egl::Error bindTexImage(const gl::Context *context,
64                             gl::Texture *texture,
65                             EGLint buffer) override;
66     egl::Error releaseTexImage(const gl::Context *context, EGLint buffer) override;
67     egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override;
68     egl::Error getMscRate(EGLint *numerator, EGLint *denominator) override;
69     void setSwapInterval(EGLint interval) override;
70 
71     // width and height can change with client window resizing
72     EGLint getWidth() const override;
73     EGLint getHeight() const override;
74 
75     EGLint isPostSubBufferSupported() const override;
76     EGLint getSwapBehavior() const override;
77 
78     angle::Result initializeContents(const gl::Context *context,
79                                      GLenum binding,
80                                      const gl::ImageIndex &imageIndex) override;
81 
82     vk::ImageHelper *getColorAttachmentImage();
83 
84     egl::Error lockSurface(const egl::Display *display,
85                            EGLint usageHint,
86                            bool preservePixels,
87                            uint8_t **bufferPtrOut,
88                            EGLint *bufferPitchOut) override;
89     egl::Error unlockSurface(const egl::Display *display, bool preservePixels) override;
90     EGLint origin() const override;
91 
92     egl::Error attachToFramebuffer(const gl::Context *context,
93                                    gl::Framebuffer *framebuffer) override;
94     egl::Error detachFromFramebuffer(const gl::Context *context,
95                                      gl::Framebuffer *framebuffer) override;
96 
97   protected:
98     struct AttachmentImage final : angle::NonCopyable
99     {
100         AttachmentImage(SurfaceVk *surfaceVk);
101         ~AttachmentImage();
102 
103         angle::Result initialize(DisplayVk *displayVk,
104                                  EGLint width,
105                                  EGLint height,
106                                  const vk::Format &vkFormat,
107                                  GLint samples,
108                                  bool isRobustResourceInitEnabled,
109                                  bool hasProtectedContent);
110 
111         void destroy(const egl::Display *display);
112 
113         vk::ImageHelper image;
114         vk::ImageViewHelper imageViews;
115         angle::ObserverBinding imageObserverBinding;
116     };
117 
118     virtual angle::Result initializeImpl(DisplayVk *displayVk);
119 
120     EGLint mWidth;
121     EGLint mHeight;
122 
123     AttachmentImage mColorAttachment;
124     AttachmentImage mDepthStencilAttachment;
125 
126     // EGL_KHR_lock_surface3
127     vk::BufferHelper mLockBufferHelper;
128 };
129 
130 // Data structures used in WindowSurfaceVk
131 namespace impl
132 {
133 static constexpr size_t kSwapHistorySize = 2;
134 
135 // Old swapchain and associated present fences/semaphores that need to be scheduled for
136 // recycling/destruction when appropriate.
137 struct SwapchainCleanupData : angle::NonCopyable
138 {
139     SwapchainCleanupData();
140     SwapchainCleanupData(SwapchainCleanupData &&other);
141     ~SwapchainCleanupData();
142 
143     // Fences must not be empty (VK_EXT_swapchain_maintenance1 is supported).
144     VkResult getFencesStatus(VkDevice device) const;
145     // Waits fences if any. Use before force destroying the swapchain.
146     void waitFences(VkDevice device, uint64_t timeout) const;
147     void destroy(VkDevice device,
148                  vk::Recycler<vk::Fence> *fenceRecycler,
149                  vk::Recycler<vk::Semaphore> *semaphoreRecycler);
150 
151     // The swapchain to be destroyed.
152     VkSwapchainKHR swapchain = VK_NULL_HANDLE;
153     // Any present fences/semaphores that were pending recycle at the time the swapchain was
154     // recreated will be scheduled for recycling at the same time as the swapchain's destruction.
155     // fences must be in the present operation order.
156     std::vector<vk::Fence> fences;
157     std::vector<vk::Semaphore> semaphores;
158 };
159 
160 // Each present operation is associated with a wait semaphore.  To know when that semaphore can be
161 // recycled, a swapSerial is used.  When that swapSerial is finished, the semaphore used in the
162 // previous present operation involving imageIndex can be recycled.  See doc/PresentSemaphores.md
163 // for details.
164 // When VK_EXT_swapchain_maintenance1 is supported, present fence is used instead of the swapSerial.
165 //
166 // Old swapchains are scheduled to be destroyed at the same time as the last wait semaphore used to
167 // present an image to the old swapchains can be recycled (only relevant when
168 // VK_EXT_swapchain_maintenance1 is not supported).
169 struct ImagePresentOperation : angle::NonCopyable
170 {
171     ImagePresentOperation();
172     ImagePresentOperation(ImagePresentOperation &&other);
173     ImagePresentOperation &operator=(ImagePresentOperation &&other);
174     ~ImagePresentOperation();
175 
176     void destroy(VkDevice device,
177                  vk::Recycler<vk::Fence> *fenceRecycler,
178                  vk::Recycler<vk::Semaphore> *semaphoreRecycler);
179 
180     // fence is only used when VK_EXT_swapchain_maintenance1 is supported.
181     vk::Fence fence;
182     vk::Semaphore semaphore;
183 
184     // Below members only relevant when VK_EXT_swapchain_maintenance1 is not supported.
185     // Used to associate a swapSerial with the previous present operation of the image.
186     uint32_t imageIndex;
187     QueueSerial queueSerial;
188     std::deque<SwapchainCleanupData> oldSwapchains;
189 };
190 
191 // Swapchain images and their associated objects.
192 struct SwapchainImage : angle::NonCopyable
193 {
194     SwapchainImage();
195     SwapchainImage(SwapchainImage &&other);
196     ~SwapchainImage();
197 
198     std::unique_ptr<vk::ImageHelper> image;
199     vk::ImageViewHelper imageViews;
200     vk::Framebuffer framebuffer;
201     vk::Framebuffer fetchFramebuffer;
202 
203     uint64_t frameNumber = 0;
204 };
205 
206 // Associated data for a call to vkAcquireNextImageKHR without necessarily holding the share group
207 // lock.
208 struct UnlockedTryAcquireData : angle::NonCopyable
209 {
210     // A mutex to protect against concurrent attempts to call vkAcquireNextImageKHR.
211     angle::SimpleMutex mutex;
212 
213     // Given that the CPU is throttled after a number of swaps, there is an upper bound to the
214     // number of semaphores that are used to acquire swapchain images, and that is
215     // kSwapHistorySize+1:
216     //
217     //             Unrelated submission in     Submission as part of
218     //               the middle of frame          buffer swap
219     //                              |                 |
220     //                              V                 V
221     //     Frame i:     ... ANI ... QS (fence Fa) ... QS (Fence Fb) QP Wait(..)
222     //     Frame i+1:   ... ANI ... QS (fence Fc) ... QS (Fence Fd) QP Wait(..) <--\
223     //     Frame i+2:   ... ANI ... QS (fence Fe) ... QS (Fence Ff) QP Wait(Fb)    |
224     //                                                                  ^          |
225     //                                                                  |          |
226     //                                                           CPU throttling    |
227     //                                                                             |
228     //                               Note: app should throttle itself here (equivalent of Wait(Fb))
229     //
230     // In frame i+2 (2 is kSwapHistorySize), ANGLE waits on fence Fb which means that the semaphore
231     // used for Frame i's ANI can be reused (because Fb-is-signalled implies Fa-is-signalled).
232     // Before this wait, there were three acquire semaphores in use corresponding to frames i, i+1
233     // and i+2.  Frame i+3 can reuse the semaphore of frame i.
234     angle::CircularBuffer<vk::Semaphore, impl::kSwapHistorySize + 1> acquireImageSemaphores;
235 };
236 
237 struct UnlockedTryAcquireResult : angle::NonCopyable
238 {
239     // The result of the call to vkAcquireNextImageKHR.  This result is processed later under the
240     // share group lock.
241     VkResult result = VK_SUCCESS;
242 
243     // Semaphore to signal.
244     VkSemaphore acquireSemaphore = VK_NULL_HANDLE;
245 
246     // Image index that was acquired
247     uint32_t imageIndex = std::numeric_limits<uint32_t>::max();
248 };
249 
250 struct ImageAcquireOperation : angle::NonCopyable
251 {
252     ImageAcquireOperation();
253 
254     // True when acquiring the next image is deferred.
255     std::atomic<bool> needToAcquireNextSwapchainImage;
256 
257     // Data used to call vkAcquireNextImageKHR without necessarily holding the share group lock.
258     // The result of this operation can be found in mAcquireOperation.unlockedTryAcquireResult,
259     // which is processed once the share group lock is taken in the future.
260     //
261     // |unlockedTryAcquireData::mutex| is necessary to hold when making the vkAcquireNextImageKHR
262     // call as multiple contexts in the share group may end up provoking it (only one may be calling
263     // it without the share group lock though, the one calling eglPrepareSwapBuffersANGLE).  During
264     // processing of the results however (for example in the following eglSwapBuffers call, or if
265     // called during a GL call, immediately afterwards), the contents of |unlockedTryAcquireResult|
266     // can be accessed without |unlockedTryAcquireData::mutex| because the share group lock is
267     // already taken, and no thread can be attempting an unlocked vkAcquireNextImageKHR.
268     UnlockedTryAcquireData unlockedTryAcquireData;
269     UnlockedTryAcquireResult unlockedTryAcquireResult;
270 };
271 }  // namespace impl
272 
273 enum class FramebufferFetchMode
274 {
275     Disabled,
276     Enabled,
277 };
278 
279 class WindowSurfaceVk : public SurfaceVk
280 {
281   public:
282     WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativeWindowType window);
283     ~WindowSurfaceVk() override;
284 
285     void destroy(const egl::Display *display) override;
286 
287     egl::Error initialize(const egl::Display *display) override;
288 
289     egl::Error unMakeCurrent(const gl::Context *context) override;
290 
291     angle::Result getAttachmentRenderTarget(const gl::Context *context,
292                                             GLenum binding,
293                                             const gl::ImageIndex &imageIndex,
294                                             GLsizei samples,
295                                             FramebufferAttachmentRenderTarget **rtOut) override;
296     egl::Error prepareSwap(const gl::Context *context) override;
297     egl::Error swap(const gl::Context *context) override;
298     egl::Error swapWithDamage(const gl::Context *context,
299                               const EGLint *rects,
300                               EGLint n_rects) override;
301     egl::Error postSubBuffer(const gl::Context *context,
302                              EGLint x,
303                              EGLint y,
304                              EGLint width,
305                              EGLint height) override;
306     egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override;
307     egl::Error bindTexImage(const gl::Context *context,
308                             gl::Texture *texture,
309                             EGLint buffer) override;
310     egl::Error releaseTexImage(const gl::Context *context, EGLint buffer) override;
311     egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override;
312     egl::Error getMscRate(EGLint *numerator, EGLint *denominator) override;
313     void setSwapInterval(EGLint interval) override;
314 
315     // width and height can change with client window resizing
316     EGLint getWidth() const override;
317     EGLint getHeight() const override;
318     EGLint getRotatedWidth() const;
319     EGLint getRotatedHeight() const;
320     // Note: windows cannot be resized on Android.  The approach requires
321     // calling vkGetPhysicalDeviceSurfaceCapabilitiesKHR.  However, that is
322     // expensive; and there are troublesome timing issues for other parts of
323     // ANGLE (which cause test failures and crashes).  Therefore, a
324     // special-Android-only path is created just for the querying of EGL_WIDTH
325     // and EGL_HEIGHT.
326     // https://issuetracker.google.com/issues/153329980
327     egl::Error getUserWidth(const egl::Display *display, EGLint *value) const override;
328     egl::Error getUserHeight(const egl::Display *display, EGLint *value) const override;
329     angle::Result getUserExtentsImpl(DisplayVk *displayVk,
330                                      VkSurfaceCapabilitiesKHR *surfaceCaps) const;
331 
332     EGLint isPostSubBufferSupported() const override;
333     EGLint getSwapBehavior() const override;
334 
335     angle::Result initializeContents(const gl::Context *context,
336                                      GLenum binding,
337                                      const gl::ImageIndex &imageIndex) override;
338 
339     vk::Framebuffer &chooseFramebuffer();
340 
341     angle::Result getCurrentFramebuffer(ContextVk *context,
342                                         FramebufferFetchMode fetchMode,
343                                         const vk::RenderPass &compatibleRenderPass,
344                                         vk::Framebuffer *framebufferOut);
345 
getPreTransform()346     VkSurfaceTransformFlagBitsKHR getPreTransform() const
347     {
348         if (mEmulatedPreTransform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
349         {
350             return mEmulatedPreTransform;
351         }
352         return mPreTransform;
353     }
354 
355     egl::Error setAutoRefreshEnabled(bool enabled) override;
356 
357     egl::Error getBufferAge(const gl::Context *context, EGLint *age) override;
358 
359     egl::Error setRenderBuffer(EGLint renderBuffer) override;
360 
isSharedPresentMode()361     bool isSharedPresentMode() const
362     {
363         return (mSwapchainPresentMode == vk::PresentMode::SharedDemandRefreshKHR ||
364                 mSwapchainPresentMode == vk::PresentMode::SharedContinuousRefreshKHR);
365     }
366 
isSharedPresentModeDesired()367     bool isSharedPresentModeDesired() const
368     {
369         return (mDesiredSwapchainPresentMode == vk::PresentMode::SharedDemandRefreshKHR ||
370                 mDesiredSwapchainPresentMode == vk::PresentMode::SharedContinuousRefreshKHR);
371     }
372 
373     egl::Error lockSurface(const egl::Display *display,
374                            EGLint usageHint,
375                            bool preservePixels,
376                            uint8_t **bufferPtrOut,
377                            EGLint *bufferPitchOut) override;
378     egl::Error unlockSurface(const egl::Display *display, bool preservePixels) override;
379     EGLint origin() const override;
380 
381     egl::Error attachToFramebuffer(const gl::Context *context,
382                                    gl::Framebuffer *framebuffer) override;
383     egl::Error detachFromFramebuffer(const gl::Context *context,
384                                      gl::Framebuffer *framebuffer) override;
385 
386     angle::Result onSharedPresentContextFlush(const gl::Context *context);
387 
388     bool hasStagedUpdates() const;
389 
390     void setTimestampsEnabled(bool enabled) override;
391 
392   protected:
393     angle::Result swapImpl(const gl::Context *context,
394                            const EGLint *rects,
395                            EGLint n_rects,
396                            const void *pNextChain);
397     // Called when a swapchain image whose acquisition was deferred must be acquired.  This method
398     // will recreate the swapchain (if needed due to present returning OUT_OF_DATE, swap interval
399     // changing, surface size changing etc, by calling prepareForAcquireNextSwapchainImage()) and
400     // call the doDeferredAcquireNextImageWithUsableSwapchain() method.
401     angle::Result doDeferredAcquireNextImage(const gl::Context *context, bool presentOutOfDate);
402     // Calls acquireNextSwapchainImage() and sets up the acquired image.  On some platforms,
403     // vkAcquireNextImageKHR returns OUT_OF_DATE instead of present, so this function may still
404     // recreate the swapchain.  The main difference with doDeferredAcquireNextImage is that it does
405     // not check for surface property changes for the purposes of swapchain recreation (because
406     // that's already done by prepareForAcquireNextSwapchainImage.
407     angle::Result doDeferredAcquireNextImageWithUsableSwapchain(const gl::Context *context);
408 
409     EGLNativeWindowType mNativeWindowType;
410     VkSurfaceKHR mSurface;
411     VkSurfaceCapabilitiesKHR mSurfaceCaps;
412     VkBool32 mSupportsProtectedSwapchain;
413 
414   private:
415     virtual angle::Result createSurfaceVk(vk::Context *context, gl::Extents *extentsOut)      = 0;
416     virtual angle::Result getCurrentWindowSize(vk::Context *context, gl::Extents *extentsOut) = 0;
417 
418     angle::Result initializeImpl(DisplayVk *displayVk, bool *anyMatchesOut);
419     angle::Result recreateSwapchain(ContextVk *contextVk, const gl::Extents &extents);
420     angle::Result createSwapChain(vk::Context *context,
421                                   const gl::Extents &extents,
422                                   VkSwapchainKHR oldSwapchain);
423     angle::Result queryAndAdjustSurfaceCaps(ContextVk *contextVk,
424                                             VkSurfaceCapabilitiesKHR *surfaceCaps);
425     angle::Result checkForOutOfDateSwapchain(ContextVk *contextVk,
426                                              bool presentOutOfDate,
427                                              bool *swapchainRecreatedOut);
428     angle::Result resizeSwapchainImages(vk::Context *context, uint32_t imageCount);
429     void releaseSwapchainImages(ContextVk *contextVk);
430     void destroySwapChainImages(DisplayVk *displayVk);
431     angle::Result prepareForAcquireNextSwapchainImage(const gl::Context *context,
432                                                       bool presentOutOfDate,
433                                                       bool *swapchainRecreatedOut);
434     // This method calls vkAcquireNextImageKHR() to acquire the next swapchain image.  It is called
435     // when the swapchain is initially created and when present() finds the swapchain out of date.
436     // Otherwise, it is scheduled to be called later by deferAcquireNextImage().
437     VkResult acquireNextSwapchainImage(vk::Context *context);
438     // Process the result of vkAcquireNextImageKHR, which may have been done previously without
439     // holding a lock.
440     VkResult postProcessUnlockedTryAcquire(vk::Context *context);
441     // Whether vkAcquireNextImageKHR needs to be called or its results processed
442     bool needsAcquireImageOrProcessResult() const;
443     // This method is called when a swapchain image is presented.  It schedules
444     // acquireNextSwapchainImage() to be called later.
445     void deferAcquireNextImage();
446     bool skipAcquireNextSwapchainImageForSharedPresentMode() const;
447 
448     angle::Result computePresentOutOfDate(vk::Context *context,
449                                           VkResult result,
450                                           bool *presentOutOfDate);
451     angle::Result prePresentSubmit(ContextVk *contextVk, const vk::Semaphore &presentSemaphore);
452     angle::Result present(ContextVk *contextVk,
453                           const EGLint *rects,
454                           EGLint n_rects,
455                           const void *pNextChain,
456                           bool *presentOutOfDate);
457 
458     angle::Result cleanUpPresentHistory(vk::Context *context);
459     angle::Result cleanUpOldSwapchains(vk::Context *context);
460 
461     // Throttle the CPU such that application's logic and command buffer recording doesn't get more
462     // than two frame ahead of the frame being rendered (and three frames ahead of the one being
463     // presented).  This is a failsafe, as the application should ensure command buffer recording is
464     // not ahead of the frame being rendered by *one* frame.
465     angle::Result throttleCPU(vk::Context *context, const QueueSerial &currentSubmitSerial);
466 
467     // Finish all GPU operations on the surface
468     angle::Result finish(vk::Context *context);
469 
470     void updateOverlay(ContextVk *contextVk) const;
471     bool overlayHasEnabledWidget(ContextVk *contextVk) const;
472     angle::Result drawOverlay(ContextVk *contextVk, impl::SwapchainImage *image) const;
473 
474     bool isMultiSampled() const;
475 
476     bool supportsPresentMode(vk::PresentMode presentMode) const;
477 
478     bool updateColorSpace(DisplayVk *displayVk);
479 
480     angle::FormatID getIntendedFormatID(vk::Renderer *renderer);
481     angle::FormatID getActualFormatID(vk::Renderer *renderer);
482 
483     std::vector<vk::PresentMode> mPresentModes;
484 
485     VkSwapchainKHR mSwapchain;
486     vk::SwapchainStatus mSwapchainStatus;
487     // Cached information used to recreate swapchains.
488     vk::PresentMode mSwapchainPresentMode;         // Current swapchain mode
489     vk::PresentMode mDesiredSwapchainPresentMode;  // Desired mode set through setSwapInterval()
490     uint32_t mMinImageCount;
491     VkSurfaceTransformFlagBitsKHR mPreTransform;
492     VkSurfaceTransformFlagBitsKHR mEmulatedPreTransform;
493     VkCompositeAlphaFlagBitsKHR mCompositeAlpha;
494     VkColorSpaceKHR mSurfaceColorSpace;
495 
496     // Present modes that are compatible with the current mode.  If mDesiredSwapchainPresentMode is
497     // in this list, mode switch can happen without the need to recreate the swapchain.  Fast
498     // vector's size is 6, as there are currently only 6 possible present modes.
499     static constexpr uint32_t kMaxCompatiblePresentModes = 6;
500     angle::FixedVector<VkPresentModeKHR, kMaxCompatiblePresentModes> mCompatiblePresentModes;
501 
502     // A circular buffer that stores the serial of the submission fence of the context on every
503     // swap. The CPU is throttled by waiting for the 2nd previous serial to finish.  This should
504     // normally be a no-op, as the application should pace itself to avoid input lag, and is
505     // implemented in ANGLE as a fail safe.  Removing this throttling requires untangling it from
506     // acquire semaphore recycling (see mAcquireImageSemaphores above)
507     angle::CircularBuffer<QueueSerial, impl::kSwapHistorySize> mSwapHistory;
508 
509     // The previous swapchain which needs to be scheduled for destruction when appropriate.  This
510     // will be done when the first image of the current swapchain is presented or when fences are
511     // signaled (when VK_EXT_swapchain_maintenance1 is supported).  If there were older swapchains
512     // pending destruction when the swapchain is recreated, they will accumulate and be destroyed
513     // with the previous swapchain.
514     //
515     // Note that if the user resizes the window such that the swapchain is recreated every frame,
516     // this array can go grow indefinitely.
517     std::deque<impl::SwapchainCleanupData> mOldSwapchains;
518 
519     std::vector<impl::SwapchainImage> mSwapchainImages;
520     std::vector<angle::ObserverBinding> mSwapchainImageBindings;
521     uint32_t mCurrentSwapchainImageIndex;
522 
523     // There is no direct signal from Vulkan regarding when a Present semaphore can be be reused.
524     // During window resizing when swapchains are recreated every frame, the number of in-flight
525     // present semaphores can grow indefinitely.  See doc/PresentSemaphores.md.
526     vk::Recycler<vk::Semaphore> mPresentSemaphoreRecycler;
527     // Fences are associated with present semaphores to know when they can be recycled.
528     vk::Recycler<vk::Fence> mPresentFenceRecycler;
529 
530     // The presentation history, used to recycle semaphores and destroy old swapchains.
531     std::deque<impl::ImagePresentOperation> mPresentHistory;
532 
533     // Depth/stencil image.  Possibly multisampled.
534     vk::ImageHelper mDepthStencilImage;
535     vk::ImageViewHelper mDepthStencilImageViews;
536     angle::ObserverBinding mDepthStencilImageBinding;
537 
538     // Multisample color image, view and framebuffer, if multisampling enabled.
539     vk::ImageHelper mColorImageMS;
540     vk::ImageViewHelper mColorImageMSViews;
541     angle::ObserverBinding mColorImageMSBinding;
542     vk::Framebuffer mFramebufferMS;
543 
544     impl::ImageAcquireOperation mAcquireOperation;
545 
546     // EGL_EXT_buffer_age: Track frame count.
547     uint64_t mFrameCount;
548 
549     // EGL_KHR_lock_surface3
550     vk::BufferHelper mLockBufferHelper;
551 
552     // EGL_KHR_partial_update
553     uint64_t mBufferAgeQueryFrameNumber;
554 
555     // GL_EXT_shader_framebuffer_fetch
556     FramebufferFetchMode mFramebufferFetchMode = FramebufferFetchMode::Disabled;
557 };
558 
559 }  // namespace rx
560 
561 #endif  // LIBANGLE_RENDERER_VULKAN_SURFACEVK_H_
562