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