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 ¤tSubmitSerial); 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