| // |
| // Copyright 2016 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // SurfaceVk.h: |
| // Defines the class interface for SurfaceVk, implementing SurfaceImpl. |
| // |
| |
| #ifndef LIBANGLE_RENDERER_VULKAN_SURFACEVK_H_ |
| #define LIBANGLE_RENDERER_VULKAN_SURFACEVK_H_ |
| |
| #include <optional> |
| #include "common/CircularBuffer.h" |
| #include "common/SimpleMutex.h" |
| #include "common/vulkan/vk_headers.h" |
| #include "libANGLE/renderer/SurfaceImpl.h" |
| #include "libANGLE/renderer/vulkan/RenderTargetVk.h" |
| #include "libANGLE/renderer/vulkan/vk_helpers.h" |
| |
| namespace rx |
| { |
| class SurfaceVk : public SurfaceImpl, public angle::ObserverInterface, public vk::Resource |
| { |
| public: |
| angle::Result getAttachmentRenderTarget(const gl::Context *context, |
| GLenum binding, |
| const gl::ImageIndex &imageIndex, |
| GLsizei samples, |
| FramebufferAttachmentRenderTarget **rtOut) override; |
| |
| protected: |
| SurfaceVk(const egl::SurfaceState &surfaceState); |
| ~SurfaceVk() override; |
| |
| void destroy(const egl::Display *display) override; |
| // We monitor the staging buffer for changes. This handles staged data from outside this class. |
| void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override; |
| |
| // size can change with client window resizing |
| gl::Extents getSize() const override; |
| |
| EGLint mWidth; |
| EGLint mHeight; |
| |
| RenderTargetVk mColorRenderTarget; |
| RenderTargetVk mDepthStencilRenderTarget; |
| }; |
| |
| class OffscreenSurfaceVk : public SurfaceVk |
| { |
| public: |
| OffscreenSurfaceVk(const egl::SurfaceState &surfaceState, vk::Renderer *renderer); |
| ~OffscreenSurfaceVk() override; |
| |
| egl::Error initialize(const egl::Display *display) override; |
| void destroy(const egl::Display *display) override; |
| |
| egl::Error unMakeCurrent(const gl::Context *context) override; |
| const vk::ImageHelper *getColorImage() const { return &mColorAttachment.image; } |
| |
| egl::Error swap(const gl::Context *context, SurfaceSwapFeedback *feedback) override; |
| egl::Error postSubBuffer(const gl::Context *context, |
| EGLint x, |
| EGLint y, |
| EGLint width, |
| EGLint height) override; |
| egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; |
| egl::Error bindTexImage(const gl::Context *context, |
| gl::Texture *texture, |
| EGLint buffer) override; |
| egl::Error releaseTexImage(const gl::Context *context, EGLint buffer) override; |
| egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override; |
| egl::Error getMscRate(EGLint *numerator, EGLint *denominator) override; |
| void setSwapInterval(const egl::Display *display, EGLint interval) override; |
| |
| EGLint isPostSubBufferSupported() const override; |
| EGLint getSwapBehavior() const override; |
| |
| angle::Result initializeContents(const gl::Context *context, |
| GLenum binding, |
| const gl::ImageIndex &imageIndex) override; |
| |
| vk::ImageHelper *getColorAttachmentImage(); |
| |
| egl::Error lockSurface(const egl::Display *display, |
| EGLint usageHint, |
| bool preservePixels, |
| uint8_t **bufferPtrOut, |
| EGLint *bufferPitchOut) override; |
| egl::Error unlockSurface(const egl::Display *display, bool preservePixels) override; |
| EGLint origin() const override; |
| |
| egl::Error attachToFramebuffer(const gl::Context *context, |
| gl::Framebuffer *framebuffer) override; |
| egl::Error detachFromFramebuffer(const gl::Context *context, |
| gl::Framebuffer *framebuffer) override; |
| |
| protected: |
| struct AttachmentImage final : angle::NonCopyable |
| { |
| AttachmentImage(SurfaceVk *surfaceVk); |
| ~AttachmentImage(); |
| |
| angle::Result initialize(DisplayVk *displayVk, |
| EGLint width, |
| EGLint height, |
| const vk::Format &vkFormat, |
| GLint samples, |
| bool isRobustResourceInitEnabled, |
| bool hasProtectedContent); |
| |
| void destroy(const egl::Display *display); |
| |
| vk::ImageHelper image; |
| vk::ImageViewHelper imageViews; |
| angle::ObserverBinding imageObserverBinding; |
| }; |
| |
| virtual angle::Result initializeImpl(DisplayVk *displayVk); |
| |
| AttachmentImage mColorAttachment; |
| AttachmentImage mDepthStencilAttachment; |
| |
| // EGL_KHR_lock_surface3 |
| vk::BufferHelper mLockBufferHelper; |
| }; |
| |
| // Data structures used in WindowSurfaceVk |
| namespace impl |
| { |
| static constexpr size_t kSwapHistorySize = 2; |
| |
| // Old swapchain and associated present fences/semaphores that need to be scheduled for |
| // recycling/destruction when appropriate. |
| struct SwapchainCleanupData : angle::NonCopyable |
| { |
| SwapchainCleanupData(); |
| SwapchainCleanupData(SwapchainCleanupData &&other); |
| ~SwapchainCleanupData(); |
| |
| // Fences must not be empty (VK_EXT_swapchain_maintenance1 is supported). |
| VkResult getFencesStatus(VkDevice device) const; |
| // Waits fences if any. Use before force destroying the swapchain. |
| void waitFences(VkDevice device, uint64_t timeout) const; |
| void destroy(VkDevice device, |
| vk::Recycler<vk::Fence> *fenceRecycler, |
| vk::Recycler<vk::Semaphore> *semaphoreRecycler); |
| |
| // The swapchain to be destroyed. |
| VkSwapchainKHR swapchain = VK_NULL_HANDLE; |
| // Any present fences/semaphores that were pending recycle at the time the swapchain was |
| // recreated will be scheduled for recycling at the same time as the swapchain's destruction. |
| // fences must be in the present operation order. |
| std::vector<vk::Fence> fences; |
| std::vector<vk::Semaphore> semaphores; |
| }; |
| |
| // Each present operation is associated with a wait semaphore. To know when that semaphore can be |
| // recycled, a swapSerial is used. When that swapSerial is finished, the semaphore used in the |
| // previous present operation involving imageIndex can be recycled. See doc/PresentSemaphores.md |
| // for details. |
| // When VK_EXT_swapchain_maintenance1 is supported, present fence is used instead of the swapSerial. |
| // |
| // Old swapchains are scheduled to be destroyed at the same time as the last wait semaphore used to |
| // present an image to the old swapchains can be recycled (only relevant when |
| // VK_EXT_swapchain_maintenance1 is not supported). |
| struct ImagePresentOperation : angle::NonCopyable |
| { |
| ImagePresentOperation(); |
| ImagePresentOperation(ImagePresentOperation &&other); |
| ImagePresentOperation &operator=(ImagePresentOperation &&other); |
| ~ImagePresentOperation(); |
| |
| void destroy(VkDevice device, |
| vk::Recycler<vk::Fence> *fenceRecycler, |
| vk::Recycler<vk::Semaphore> *semaphoreRecycler); |
| |
| // fence is only used when VK_EXT_swapchain_maintenance1 is supported. |
| vk::Fence fence; |
| vk::Semaphore semaphore; |
| |
| // Below members only relevant when VK_EXT_swapchain_maintenance1 is not supported. |
| // Used to associate a swapSerial with the previous present operation of the image. |
| uint32_t imageIndex; |
| QueueSerial queueSerial; |
| std::deque<SwapchainCleanupData> oldSwapchains; |
| }; |
| |
| // Swapchain images and their associated objects. |
| struct SwapchainImage : angle::NonCopyable |
| { |
| SwapchainImage(); |
| SwapchainImage(SwapchainImage &&other); |
| ~SwapchainImage(); |
| |
| std::unique_ptr<vk::ImageHelper> image; |
| vk::ImageViewHelper imageViews; |
| vk::Framebuffer framebuffer; |
| vk::Framebuffer fetchFramebuffer; |
| |
| uint64_t frameNumber = 0; |
| }; |
| |
| enum class ImageAcquireState |
| { |
| Unacquired, |
| NeedToProcessResult, |
| Ready, |
| }; |
| |
| // Associated data for a call to vkAcquireNextImageKHR without necessarily holding the share group |
| // and global locks but ONLY from a thread where Surface is current. |
| struct UnlockedAcquireData : angle::NonCopyable |
| { |
| // Given that the CPU is throttled after a number of swaps, there is an upper bound to the |
| // number of semaphores that are used to acquire swapchain images, and that is |
| // kSwapHistorySize+1: |
| // |
| // Unrelated submission in Submission as part of |
| // the middle of frame buffer swap |
| // | | |
| // V V |
| // Frame i: ... ANI ... QS (fence Fa) ... QS (Fence Fb) QP Wait(..) |
| // Frame i+1: ... ANI ... QS (fence Fc) ... QS (Fence Fd) QP Wait(..) <--\ |
| // Frame i+2: ... ANI ... QS (fence Fe) ... QS (Fence Ff) QP Wait(Fb) | |
| // ^ | |
| // | | |
| // CPU throttling | |
| // | |
| // Note: app should throttle itself here (equivalent of Wait(Fb)) |
| // |
| // In frame i+2 (2 is kSwapHistorySize), ANGLE waits on fence Fb which means that the semaphore |
| // used for Frame i's ANI can be reused (because Fb-is-signalled implies Fa-is-signalled). |
| // Before this wait, there were three acquire semaphores in use corresponding to frames i, i+1 |
| // and i+2. Frame i+3 can reuse the semaphore of frame i. |
| angle::CircularBuffer<vk::Semaphore, impl::kSwapHistorySize + 1> acquireImageSemaphores; |
| }; |
| |
| struct UnlockedAcquireResult : angle::NonCopyable |
| { |
| // The result of the call to vkAcquireNextImageKHR. |
| VkResult result = VK_SUCCESS; |
| |
| // Semaphore to signal. |
| VkSemaphore acquireSemaphore = VK_NULL_HANDLE; |
| |
| // Image index that was acquired |
| uint32_t imageIndex = std::numeric_limits<uint32_t>::max(); |
| }; |
| |
| struct ImageAcquireOperation : angle::NonCopyable |
| { |
| // Initially image needs to be acquired. |
| ImageAcquireState state = ImageAcquireState::Unacquired; |
| |
| // No synchronization is necessary when making the vkAcquireNextImageKHR call since it is ONLY |
| // possible on a thread where Surface is current. |
| UnlockedAcquireData unlockedAcquireData; |
| UnlockedAcquireResult unlockedAcquireResult; |
| }; |
| |
| enum class SurfaceSizeState |
| { |
| InvalidSwapchain, |
| Unresolved, |
| Resolved, |
| }; |
| } // namespace impl |
| |
| class WindowSurfaceVk : public SurfaceVk |
| { |
| public: |
| WindowSurfaceVk(const egl::SurfaceState &surfaceState, EGLNativeWindowType window); |
| ~WindowSurfaceVk() override; |
| |
| void destroy(const egl::Display *display) override; |
| |
| egl::Error initialize(const egl::Display *display) override; |
| |
| egl::Error unMakeCurrent(const gl::Context *context) override; |
| |
| angle::Result getAttachmentRenderTarget(const gl::Context *context, |
| GLenum binding, |
| const gl::ImageIndex &imageIndex, |
| GLsizei samples, |
| FramebufferAttachmentRenderTarget **rtOut) override; |
| egl::Error prepareSwap(const gl::Context *context) override; |
| egl::Error swap(const gl::Context *context, SurfaceSwapFeedback *feedback) override; |
| egl::Error swapWithDamage(const gl::Context *context, |
| const EGLint *rects, |
| EGLint n_rects, |
| SurfaceSwapFeedback *feedback) override; |
| egl::Error postSubBuffer(const gl::Context *context, |
| EGLint x, |
| EGLint y, |
| EGLint width, |
| EGLint height) override; |
| egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; |
| egl::Error bindTexImage(const gl::Context *context, |
| gl::Texture *texture, |
| EGLint buffer) override; |
| egl::Error releaseTexImage(const gl::Context *context, EGLint buffer) override; |
| egl::Error getSyncValues(EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) override; |
| egl::Error getMscRate(EGLint *numerator, EGLint *denominator) override; |
| void setSwapInterval(const egl::Display *display, EGLint interval) override; |
| |
| // Explicitly resolves surface size to use before state synchronization (e.g. validation). |
| angle::Result ensureSizeResolved(const gl::Context *context) final; |
| gl::Extents getSize() const final; |
| |
| // Unresolved Surface size until render target is first accessed (e.g. after draw). |
| egl::Error getUserSize(const egl::Display *display, EGLint *width, EGLint *height) const final; |
| |
| EGLint isPostSubBufferSupported() const override; |
| EGLint getSwapBehavior() const override; |
| |
| angle::Result initializeContents(const gl::Context *context, |
| GLenum binding, |
| const gl::ImageIndex &imageIndex) override; |
| |
| vk::Framebuffer &chooseFramebuffer(); |
| |
| angle::Result getCurrentFramebuffer(ContextVk *context, |
| vk::FramebufferFetchMode fetchMode, |
| const vk::RenderPass &compatibleRenderPass, |
| vk::Framebuffer *framebufferOut); |
| |
| VkSurfaceTransformFlagBitsKHR getPreTransform() const |
| { |
| if (mEmulatedPreTransform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) |
| { |
| return mEmulatedPreTransform; |
| } |
| return mPreTransform; |
| } |
| |
| egl::Error setAutoRefreshEnabled(bool enabled) override; |
| |
| egl::Error getBufferAge(const gl::Context *context, EGLint *age) override; |
| |
| egl::Error setRenderBuffer(EGLint renderBuffer) override; |
| bool supportsSingleRenderBuffer() const override; |
| |
| bool isSharedPresentMode() const { return IsSharedPresentMode(mSwapchainPresentMode); } |
| |
| bool isSharedPresentModeDesired() const |
| { |
| return IsSharedPresentMode(getDesiredSwapchainPresentMode()); |
| } |
| |
| egl::Error lockSurface(const egl::Display *display, |
| EGLint usageHint, |
| bool preservePixels, |
| uint8_t **bufferPtrOut, |
| EGLint *bufferPitchOut) override; |
| egl::Error unlockSurface(const egl::Display *display, bool preservePixels) override; |
| EGLint origin() const override; |
| |
| egl::Error attachToFramebuffer(const gl::Context *context, |
| gl::Framebuffer *framebuffer) override; |
| egl::Error detachFromFramebuffer(const gl::Context *context, |
| gl::Framebuffer *framebuffer) override; |
| |
| angle::Result onSharedPresentContextFlush(ContextVk *contextVk); |
| |
| bool hasStagedUpdates() const; |
| |
| void setTimestampsEnabled(bool enabled) override; |
| egl::Error setPresentationTime(EGLnsecsANDROID time) override; |
| |
| egl::Error getCompressionRate(const egl::Display *display, |
| const gl::Context *context, |
| EGLint *rate) override; |
| |
| protected: |
| angle::Result swapImpl(ContextVk *contextVk, |
| const EGLint *rects, |
| EGLint n_rects, |
| const void *pNextChain, |
| SurfaceSwapFeedback *feedback); |
| |
| EGLNativeWindowType mNativeWindowType; |
| VkSurfaceKHR mSurface; |
| VkBool32 mSupportsProtectedSwapchain; |
| |
| private: |
| // Present modes that are compatible with the current mode. If mDesiredSwapchainPresentMode is |
| // in this list, mode switch can happen without the need to recreate the swapchain. |
| // There are currently only 6 possible present modes but vector is bigger for a workaround. |
| static constexpr uint32_t kCompatiblePresentModesSize = 10; |
| using CompatiblePresentModes = |
| angle::FixedVector<VkPresentModeKHR, kCompatiblePresentModesSize>; |
| |
| static bool IsSharedPresentMode(vk::PresentMode presentMode) |
| { |
| return (presentMode == vk::PresentMode::SharedDemandRefreshKHR || |
| presentMode == vk::PresentMode::SharedContinuousRefreshKHR); |
| } |
| |
| virtual angle::Result createSurfaceVk(vk::ErrorContext *context) = 0; |
| virtual angle::Result getCurrentWindowSize(vk::ErrorContext *context, |
| gl::Extents *extentsOut) const = 0; |
| virtual angle::Result getWindowVisibility(vk::ErrorContext *context, bool *isVisibleOut) const; |
| |
| impl::SurfaceSizeState getSizeState() const; |
| void setSizeState(impl::SurfaceSizeState sizeState); |
| angle::Result getUserExtentsImpl(vk::ErrorContext *context, VkExtent2D *extentOut) const; |
| |
| vk::PresentMode getDesiredSwapchainPresentMode() const; |
| void setDesiredSwapchainPresentMode(vk::PresentMode presentMode); |
| void setDesiredSwapInterval(EGLint interval); |
| |
| angle::Result initializeImpl(DisplayVk *displayVk, bool *anyMatchesOut); |
| // Invalidates the swapchain pointer, releases images, and defers acquire next swapchain image. |
| void invalidateSwapchain(vk::Renderer *renderer); |
| angle::Result recreateSwapchain(vk::ErrorContext *context); |
| angle::Result createSwapChain(vk::ErrorContext *context); |
| angle::Result collectOldSwapchain(vk::ErrorContext *context, VkSwapchainKHR swapchain); |
| angle::Result queryAndAdjustSurfaceCaps( |
| vk::ErrorContext *context, |
| vk::PresentMode presentMode, |
| VkSurfaceCapabilitiesKHR *surfaceCapsOut, |
| CompatiblePresentModes *compatiblePresentModesOut) const; |
| void adjustSurfaceExtent(VkExtent2D *extent) const; |
| // This method will invalidate the swapchain (if needed due to present returning OUT_OF_DATE, or |
| // swap interval changing). Updates the current swapchain present mode. |
| angle::Result prepareSwapchainForAcquireNextImage(vk::ErrorContext *context); |
| void createSwapchainImages(uint32_t imageCount); |
| void releaseSwapchainImages(vk::Renderer *renderer); |
| void destroySwapChainImages(DisplayVk *displayVk); |
| // Called when a swapchain image whose acquisition was deferred must be acquired or unlocked |
| // ANI result processed. This method will recreate the swapchain (if needed due to surface size |
| // changing etc, by calling prepareSwapchainForAcquireNextImage()) and call the |
| // acquireNextSwapchainImage(). On some platforms, vkAcquireNextImageKHR() returns OUT_OF_DATE |
| // instead of present, which is also recreates the swapchain. It is scheduled to be called |
| // later by deferAcquireNextImage(). |
| angle::Result doDeferredAcquireNextImage(vk::ErrorContext *context); |
| // This method calls vkAcquireNextImageKHR() to acquire the next swapchain image or processes |
| // unlocked ANI result, then sets up the acquired image. |
| VkResult acquireNextSwapchainImage(vk::ErrorContext *context); |
| // This method is called when a swapchain image is presented. It schedules |
| // doDeferredAcquireNextImage() to be called later. |
| void deferAcquireNextImage(); |
| bool skipAcquireNextSwapchainImageForSharedPresentMode() const; |
| |
| angle::Result checkSwapchainOutOfDate(vk::ErrorContext *context, VkResult presentResult); |
| angle::Result prePresentSubmit(ContextVk *contextVk, const vk::Semaphore &presentSemaphore); |
| angle::Result recordPresentLayoutBarrierIfNecessary(ContextVk *contextVk); |
| angle::Result present(ContextVk *contextVk, |
| const EGLint *rects, |
| EGLint n_rects, |
| const void *pNextChain, |
| SurfaceSwapFeedback *feedback); |
| |
| angle::Result cleanUpPresentHistory(vk::ErrorContext *context); |
| angle::Result cleanUpOldSwapchains(vk::ErrorContext *context); |
| |
| // Throttle the CPU such that application's logic and command buffer recording doesn't get more |
| // than two frame ahead of the frame being rendered (and three frames ahead of the one being |
| // presented). This is a failsafe, as the application should ensure command buffer recording is |
| // not ahead of the frame being rendered by *one* frame. |
| angle::Result throttleCPU(vk::ErrorContext *context, const QueueSerial ¤tSubmitSerial); |
| |
| void mergeImageResourceUses(); |
| // Finish all GPU operations on the surface |
| angle::Result finish(vk::ErrorContext *context); |
| |
| void updateOverlay(ContextVk *contextVk) const; |
| bool overlayHasEnabledWidget(ContextVk *contextVk) const; |
| angle::Result drawOverlay(ContextVk *contextVk, impl::SwapchainImage *image) const; |
| |
| bool isMultiSampled() const; |
| |
| bool supportsPresentMode(vk::PresentMode presentMode) const; |
| |
| bool updateColorSpace(DisplayVk *displayVk); |
| |
| angle::FormatID getIntendedFormatID(vk::Renderer *renderer); |
| angle::FormatID getActualFormatID(vk::Renderer *renderer); |
| |
| bool mIsSurfaceSizedBySwapchain; |
| |
| // Atomic is to allow update state without necessarily locking the mSizeMutex. |
| std::atomic<impl::SurfaceSizeState> mSizeState; |
| // Protects mWidth and mHeight against getUserSize() call. |
| mutable angle::SimpleMutex mSizeMutex; |
| |
| std::vector<vk::PresentMode> mPresentModes; |
| |
| VkSwapchainKHR mSwapchain; // Current swapchain (same as last created or NULL) |
| VkSwapchainKHR mLastSwapchain; // Last created non retired swapchain (or NULL if retired) |
| // Cached information used to recreate swapchains. |
| vk::PresentMode mSwapchainPresentMode; // Current swapchain mode |
| std::atomic<vk::PresentMode> mDesiredSwapchainPresentMode; // Desired swapchain mode |
| uint32_t mMinImageCount; |
| VkSurfaceTransformFlagBitsKHR mPreTransform; |
| VkSurfaceTransformFlagBitsKHR mEmulatedPreTransform; |
| VkCompositeAlphaFlagBitsKHR mCompositeAlpha; |
| VkColorSpaceKHR mSurfaceColorSpace; |
| VkImageCompressionFlagBitsEXT mCompressionFlags; |
| VkImageCompressionFixedRateFlagsEXT mFixedRateFlags; |
| |
| CompatiblePresentModes mCompatiblePresentModes; |
| |
| // A circular buffer that stores the serial of the submission fence of the context on every |
| // swap. The CPU is throttled by waiting for the 2nd previous serial to finish. This should |
| // normally be a no-op, as the application should pace itself to avoid input lag, and is |
| // implemented in ANGLE as a fail safe. Removing this throttling requires untangling it from |
| // acquire semaphore recycling (see mAcquireImageSemaphores above) |
| angle::CircularBuffer<QueueSerial, impl::kSwapHistorySize> mSwapHistory; |
| |
| // The previous swapchain which needs to be scheduled for destruction when appropriate. This |
| // will be done when the first image of the current swapchain is presented or when fences are |
| // signaled (when VK_EXT_swapchain_maintenance1 is supported). If there were older swapchains |
| // pending destruction when the swapchain is recreated, they will accumulate and be destroyed |
| // with the previous swapchain. |
| // |
| // Note that if the user resizes the window such that the swapchain is recreated every frame, |
| // this array can go grow indefinitely. |
| std::deque<impl::SwapchainCleanupData> mOldSwapchains; |
| |
| std::vector<impl::SwapchainImage> mSwapchainImages; |
| std::vector<angle::ObserverBinding> mSwapchainImageBindings; |
| uint32_t mCurrentSwapchainImageIndex; |
| |
| // There is no direct signal from Vulkan regarding when a Present semaphore can be be reused. |
| // During window resizing when swapchains are recreated every frame, the number of in-flight |
| // present semaphores can grow indefinitely. See doc/PresentSemaphores.md. |
| vk::Recycler<vk::Semaphore> mPresentSemaphoreRecycler; |
| // Fences are associated with present semaphores to know when they can be recycled. |
| vk::Recycler<vk::Fence> mPresentFenceRecycler; |
| |
| // The presentation history, used to recycle semaphores and destroy old swapchains. |
| std::deque<impl::ImagePresentOperation> mPresentHistory; |
| |
| // Depth/stencil image. Possibly multisampled. |
| vk::ImageHelper mDepthStencilImage; |
| vk::ImageViewHelper mDepthStencilImageViews; |
| angle::ObserverBinding mDepthStencilImageBinding; |
| |
| // Multisample color image, view and framebuffer, if multisampling enabled. |
| vk::ImageHelper mColorImageMS; |
| vk::ImageViewHelper mColorImageMSViews; |
| angle::ObserverBinding mColorImageMSBinding; |
| vk::Framebuffer mFramebufferMS; |
| |
| impl::ImageAcquireOperation mAcquireOperation; |
| |
| // EGL_EXT_buffer_age: Track frame count. |
| uint64_t mFrameCount; |
| // EGL_ANDROID_presentation_time: Next frame's id and presentation time |
| // used for VK_GOOGLE_display_timing. |
| uint32_t mPresentID; |
| std::optional<EGLnsecsANDROID> mDesiredPresentTime; |
| |
| // EGL_KHR_lock_surface3 |
| vk::BufferHelper mLockBufferHelper; |
| |
| // EGL_KHR_partial_update |
| uint64_t mBufferAgeQueryFrameNumber; |
| |
| // GL_EXT_shader_framebuffer_fetch |
| vk::FramebufferFetchMode mFramebufferFetchMode = vk::FramebufferFetchMode::None; |
| }; |
| |
| } // namespace rx |
| |
| #endif // LIBANGLE_RENDERER_VULKAN_SURFACEVK_H_ |