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