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