//
// 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.
//
// TextureVk.h:
//    Defines the class interface for TextureVk, implementing TextureImpl.
//

#ifndef LIBANGLE_RENDERER_VULKAN_TEXTUREVK_H_
#define LIBANGLE_RENDERER_VULKAN_TEXTUREVK_H_

#include "libANGLE/renderer/TextureImpl.h"
#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
#include "libANGLE/renderer/vulkan/SamplerVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
#include "libANGLE/renderer/vulkan/vk_resource.h"

namespace rx
{

enum class TextureUpdateResult
{
    ImageUnaffected,
    ImageRespecified,
};

class TextureVk : public TextureImpl, public angle::ObserverInterface
{
  public:
    TextureVk(const gl::TextureState &state, vk::Renderer *renderer);
    ~TextureVk() override;
    void onDestroy(const gl::Context *context) override;

    angle::Result setImage(const gl::Context *context,
                           const gl::ImageIndex &index,
                           GLenum internalFormat,
                           const gl::Extents &size,
                           GLenum format,
                           GLenum type,
                           const gl::PixelUnpackState &unpack,
                           gl::Buffer *unpackBuffer,
                           const uint8_t *pixels) override;
    angle::Result setSubImage(const gl::Context *context,
                              const gl::ImageIndex &index,
                              const gl::Box &area,
                              GLenum format,
                              GLenum type,
                              const gl::PixelUnpackState &unpack,
                              gl::Buffer *unpackBuffer,
                              const uint8_t *pixels) override;

    angle::Result setCompressedImage(const gl::Context *context,
                                     const gl::ImageIndex &index,
                                     GLenum internalFormat,
                                     const gl::Extents &size,
                                     const gl::PixelUnpackState &unpack,
                                     size_t imageSize,
                                     const uint8_t *pixels) override;
    angle::Result setCompressedSubImage(const gl::Context *context,
                                        const gl::ImageIndex &index,
                                        const gl::Box &area,
                                        GLenum format,
                                        const gl::PixelUnpackState &unpack,
                                        size_t imageSize,
                                        const uint8_t *pixels) override;

    angle::Result copyImage(const gl::Context *context,
                            const gl::ImageIndex &index,
                            const gl::Rectangle &sourceArea,
                            GLenum internalFormat,
                            gl::Framebuffer *source) override;
    angle::Result copySubImage(const gl::Context *context,
                               const gl::ImageIndex &index,
                               const gl::Offset &destOffset,
                               const gl::Rectangle &sourceArea,
                               gl::Framebuffer *source) override;

    angle::Result copyTexture(const gl::Context *context,
                              const gl::ImageIndex &index,
                              GLenum internalFormat,
                              GLenum type,
                              GLint sourceLevelGL,
                              bool unpackFlipY,
                              bool unpackPremultiplyAlpha,
                              bool unpackUnmultiplyAlpha,
                              const gl::Texture *source) override;
    angle::Result copySubTexture(const gl::Context *context,
                                 const gl::ImageIndex &index,
                                 const gl::Offset &destOffset,
                                 GLint sourceLevelGL,
                                 const gl::Box &sourceBox,
                                 bool unpackFlipY,
                                 bool unpackPremultiplyAlpha,
                                 bool unpackUnmultiplyAlpha,
                                 const gl::Texture *source) override;

    angle::Result copyRenderbufferSubData(const gl::Context *context,
                                          const gl::Renderbuffer *srcBuffer,
                                          GLint srcLevel,
                                          GLint srcX,
                                          GLint srcY,
                                          GLint srcZ,
                                          GLint dstLevel,
                                          GLint dstX,
                                          GLint dstY,
                                          GLint dstZ,
                                          GLsizei srcWidth,
                                          GLsizei srcHeight,
                                          GLsizei srcDepth) override;

    angle::Result copyTextureSubData(const gl::Context *context,
                                     const gl::Texture *srcTexture,
                                     GLint srcLevel,
                                     GLint srcX,
                                     GLint srcY,
                                     GLint srcZ,
                                     GLint dstLevel,
                                     GLint dstX,
                                     GLint dstY,
                                     GLint dstZ,
                                     GLsizei srcWidth,
                                     GLsizei srcHeight,
                                     GLsizei srcDepth) override;

    angle::Result copyCompressedTexture(const gl::Context *context,
                                        const gl::Texture *source) override;

    angle::Result clearImage(const gl::Context *context,
                             GLint level,
                             GLenum format,
                             GLenum type,
                             const uint8_t *data) override;

    angle::Result clearSubImage(const gl::Context *context,
                                GLint level,
                                const gl::Box &area,
                                GLenum format,
                                GLenum type,
                                const uint8_t *data) override;

    angle::Result setStorage(const gl::Context *context,
                             gl::TextureType type,
                             size_t levels,
                             GLenum internalFormat,
                             const gl::Extents &size) override;

    angle::Result setStorageExternalMemory(const gl::Context *context,
                                           gl::TextureType type,
                                           size_t levels,
                                           GLenum internalFormat,
                                           const gl::Extents &size,
                                           gl::MemoryObject *memoryObject,
                                           GLuint64 offset,
                                           GLbitfield createFlags,
                                           GLbitfield usageFlags,
                                           const void *imageCreateInfoPNext) override;

    angle::Result setEGLImageTarget(const gl::Context *context,
                                    gl::TextureType type,
                                    egl::Image *image) override;

    angle::Result setImageExternal(const gl::Context *context,
                                   gl::TextureType type,
                                   egl::Stream *stream,
                                   const egl::Stream::GLTextureDescription &desc) override;

    angle::Result setBuffer(const gl::Context *context, GLenum internalFormat) override;

    angle::Result generateMipmap(const gl::Context *context) override;

    angle::Result setBaseLevel(const gl::Context *context, GLuint baseLevel) override;

    angle::Result bindTexImage(const gl::Context *context, egl::Surface *surface) override;
    angle::Result releaseTexImage(const gl::Context *context) override;

    angle::Result getAttachmentRenderTarget(const gl::Context *context,
                                            GLenum binding,
                                            const gl::ImageIndex &imageIndex,
                                            GLsizei samples,
                                            FramebufferAttachmentRenderTarget **rtOut) override;

    angle::Result syncState(const gl::Context *context,
                            const gl::Texture::DirtyBits &dirtyBits,
                            gl::Command source) override;

    angle::Result setStorageMultisample(const gl::Context *context,
                                        gl::TextureType type,
                                        GLsizei samples,
                                        GLint internalformat,
                                        const gl::Extents &size,
                                        bool fixedSampleLocations) override;

    angle::Result setStorageAttribs(const gl::Context *context,
                                    gl::TextureType type,
                                    size_t levels,
                                    GLint internalFormat,
                                    const gl::Extents &size,
                                    const GLint *attribList) override;

    angle::Result initializeContents(const gl::Context *context,
                                     GLenum binding,
                                     const gl::ImageIndex &imageIndex) override;

    angle::Result initializeContentsWithBlack(const gl::Context *context,
                                              GLenum binding,
                                              const gl::ImageIndex &imageIndex);

    GLint getRequiredExternalTextureImageUnits([[maybe_unused]] const gl::Context *context) override
    {
        // For now, we assume that only one image unit is needed to support
        // external GL textures in the Vulkan backend.
        return 1;
    }

    const vk::ImageHelper &getImage() const
    {
        ASSERT(mImage && mImage->valid());
        return *mImage;
    }

    vk::ImageHelper &getImage()
    {
        ASSERT(mImage && mImage->valid());
        return *mImage;
    }

    void retainBufferViews(vk::CommandBufferHelperCommon *commandBufferHelper)
    {
        commandBufferHelper->retainResource(&mBufferViews);
    }

    bool isImmutable() { return mState.getImmutableFormat(); }
    bool imageValid() const { return (mImage && mImage->valid()); }

    void releaseOwnershipOfImage(const gl::Context *context);

    const vk::ImageView &getReadImageView(GLenum srgbDecode,
                                          bool texelFetchStaticUse,
                                          bool samplerExternal2DY2YEXT) const;

    angle::Result getBufferView(vk::Context *context,
                                const vk::Format *imageUniformFormat,
                                const gl::SamplerBinding *samplerBinding,
                                bool isImage,
                                const vk::BufferView **viewOut);

    // A special view used for texture copies that shouldn't perform swizzle.
    const vk::ImageView &getCopyImageView() const;
    angle::Result getStorageImageView(vk::Context *context,
                                      const gl::ImageUnit &binding,
                                      const vk::ImageView **imageViewOut);

    const vk::SamplerHelper &getSampler(bool isSamplerExternalY2Y) const
    {
        if (isSamplerExternalY2Y)
        {
            ASSERT(mY2YSampler->valid());
            return *mY2YSampler.get();
        }
        ASSERT(mSampler->valid());
        return *mSampler.get();
    }

    void resetSampler()
    {
        mSampler.reset();
        mY2YSampler.reset();
    }

    // Normally, initialize the image with enabled mipmap level counts.
    angle::Result ensureImageInitialized(ContextVk *contextVk, ImageMipLevels mipLevels);

    vk::ImageOrBufferViewSubresourceSerial getImageViewSubresourceSerial(
        const gl::SamplerState &samplerState,
        bool staticTexelFetchAccess) const
    {
        ASSERT(mImage != nullptr);
        gl::SrgbDecode srgbDecode = (samplerState.getSRGBDecode() == GL_SKIP_DECODE_EXT)
                                        ? gl::SrgbDecode::Skip
                                        : gl::SrgbDecode::Default;
        mImageView.updateSrgbDecode(*mImage, srgbDecode);
        mImageView.updateStaticTexelFetch(*mImage, staticTexelFetchAccess);

        if (mImageView.getColorspaceForRead() == vk::ImageViewColorspace::SRGB)
        {
            ASSERT(getImageViewSubresourceSerialImpl(vk::ImageViewColorspace::SRGB) ==
                   mCachedImageViewSubresourceSerialSRGBDecode);
            return mCachedImageViewSubresourceSerialSRGBDecode;
        }
        else
        {
            ASSERT(getImageViewSubresourceSerialImpl(vk::ImageViewColorspace::Linear) ==
                   mCachedImageViewSubresourceSerialSkipDecode);
            return mCachedImageViewSubresourceSerialSkipDecode;
        }
    }

    vk::ImageOrBufferViewSubresourceSerial getBufferViewSerial() const;
    vk::ImageOrBufferViewSubresourceSerial getStorageImageViewSerial(
        const gl::ImageUnit &binding) const;

    GLenum getColorReadFormat(const gl::Context *context) override;
    GLenum getColorReadType(const gl::Context *context) override;

    angle::Result getTexImage(const gl::Context *context,
                              const gl::PixelPackState &packState,
                              gl::Buffer *packBuffer,
                              gl::TextureTarget target,
                              GLint level,
                              GLenum format,
                              GLenum type,
                              void *pixels) override;

    angle::Result getCompressedTexImage(const gl::Context *context,
                                        const gl::PixelPackState &packState,
                                        gl::Buffer *packBuffer,
                                        gl::TextureTarget target,
                                        GLint level,
                                        void *pixels) override;

    ANGLE_INLINE bool hasBeenBoundAsImage() const { return mState.hasBeenBoundAsImage(); }
    ANGLE_INLINE const gl::OffsetBindingPointer<gl::Buffer> &getBuffer() const
    {
        return mState.getBuffer();
    }
    vk::BufferHelper *getPossiblyEmulatedTextureBuffer(vk::Context *context) const;

    bool isSRGBOverrideEnabled() const
    {
        return mState.getSRGBOverride() != gl::SrgbOverride::Default;
    }

    angle::Result updateSrgbDecodeState(ContextVk *contextVk, const gl::SamplerState &samplerState)
    {
        ASSERT(mImage != nullptr && mImage->valid());
        gl::SrgbDecode srgbDecode = (samplerState.getSRGBDecode() == GL_SKIP_DECODE_EXT)
                                        ? gl::SrgbDecode::Skip
                                        : gl::SrgbDecode::Default;
        mImageView.updateSrgbDecode(*mImage, srgbDecode);
        if (mImageView.hasColorspaceOverrideForRead(*mImage))
        {
            ANGLE_TRY(ensureMutable(contextVk));
        }
        return angle::Result::Continue;
    }

    angle::Result ensureRenderable(ContextVk *contextVk, TextureUpdateResult *updateResultOut);

    bool getAndResetImmutableSamplerDirtyState()
    {
        bool isDirty           = mImmutableSamplerDirty;
        mImmutableSamplerDirty = false;
        return isDirty;
    }

    angle::Result onLabelUpdate(const gl::Context *context) override;

    void onNewDescriptorSet(const vk::SharedDescriptorSetCacheKey &sharedCacheKey)
    {
        mDescriptorSetCacheManager.addKey(sharedCacheKey);
    }

    // Check if the texture is consistently specified. Used for flushing mutable textures.
    bool isMutableTextureConsistentlySpecifiedForFlush();
    bool isMipImageDescDefined(gl::TextureTarget textureTarget, size_t level);

    GLint getImageCompressionRate(const gl::Context *context) override;
    GLint getFormatSupportedCompressionRates(const gl::Context *context,
                                             GLenum internalformat,
                                             GLsizei bufSize,
                                             GLint *rates) override;

  private:
    // Transform an image index from the frontend into one that can be used on the backing
    // ImageHelper, taking into account mipmap or cube face offsets
    gl::ImageIndex getNativeImageIndex(const gl::ImageIndex &inputImageIndex) const;
    gl::LevelIndex getNativeImageLevel(gl::LevelIndex frontendLevel) const;
    uint32_t getNativeImageLayer(uint32_t frontendLayer) const;

    // Get the layer count for views.
    uint32_t getImageViewLayerCount() const;
    // Get the level count for views.
    uint32_t getImageViewLevelCount() const;

    void releaseAndDeleteImageAndViews(ContextVk *contextVk);
    angle::Result ensureImageAllocated(ContextVk *contextVk, const vk::Format &format);
    void setImageHelper(ContextVk *contextVk,
                        vk::ImageHelper *imageHelper,
                        gl::TextureType imageType,
                        uint32_t imageLevelOffset,
                        uint32_t imageLayerOffset,
                        bool selfOwned,
                        UniqueSerial siblingSerial);

    vk::ImageViewHelper &getImageViews() { return mImageView; }
    const vk::ImageViewHelper &getImageViews() const { return mImageView; }

    angle::Result ensureRenderableWithFormat(ContextVk *contextVk,
                                             const vk::Format &format,
                                             TextureUpdateResult *updateResultOut);
    angle::Result ensureRenderableIfCopyTextureCannotTransfer(ContextVk *contextVk,
                                                              const gl::InternalFormat &dstFormat,
                                                              bool unpackFlipY,
                                                              bool unpackPremultiplyAlpha,
                                                              bool unpackUnmultiplyAlpha,
                                                              TextureVk *source);
    angle::Result ensureRenderableIfCopyTexImageCannotTransfer(
        ContextVk *contextVk,
        const gl::InternalFormat &internalFormat,
        gl::Framebuffer *source);

    // Redefine a mip level of the texture.  If the new size and format don't match the allocated
    // image, the image may be released.  When redefining a mip of a multi-level image, updates are
    // forced to be staged, as another mip of the image may be bound to a framebuffer.  For example,
    // assume texture has two mips, and framebuffer is bound to mip 0.  Redefining mip 1 to an
    // incompatible size shouldn't affect the framebuffer, especially if the redefinition comes from
    // something like glCopyTexSubImage2D() (which simultaneously is reading from said framebuffer,
    // i.e. mip 0 of the texture).
    angle::Result redefineLevel(const gl::Context *context,
                                const gl::ImageIndex &index,
                                const vk::Format &format,
                                const gl::Extents &size);

    angle::Result setImageImpl(const gl::Context *context,
                               const gl::ImageIndex &index,
                               const gl::InternalFormat &formatInfo,
                               const gl::Extents &size,
                               GLenum type,
                               const gl::PixelUnpackState &unpack,
                               gl::Buffer *unpackBuffer,
                               const uint8_t *pixels);
    angle::Result setSubImageImpl(const gl::Context *context,
                                  const gl::ImageIndex &index,
                                  const gl::Box &area,
                                  const gl::InternalFormat &formatInfo,
                                  GLenum type,
                                  const gl::PixelUnpackState &unpack,
                                  gl::Buffer *unpackBuffer,
                                  const uint8_t *pixels,
                                  const vk::Format &vkFormat);

    // Used to clear a texture to a given value in part or whole.
    angle::Result clearSubImageImpl(const gl::Context *context,
                                    GLint level,
                                    const gl::Box &clearArea,
                                    vk::ClearTextureMode clearMode,
                                    GLenum format,
                                    GLenum type,
                                    const uint8_t *data);

    angle::Result ensureImageInitializedIfUpdatesNeedStageOrFlush(ContextVk *contextVk,
                                                                  gl::LevelIndex level,
                                                                  const vk::Format &vkFormat,
                                                                  vk::ApplyImageUpdate applyUpdate,
                                                                  bool usesBufferForUpdate);

    angle::Result copyImageDataToBufferAndGetData(ContextVk *contextVk,
                                                  gl::LevelIndex sourceLevelGL,
                                                  uint32_t layerCount,
                                                  const gl::Box &sourceArea,
                                                  RenderPassClosureReason reason,
                                                  vk::BufferHelper *copyBuffer,
                                                  uint8_t **outDataPtr);

    angle::Result copyBufferDataToImage(ContextVk *contextVk,
                                        vk::BufferHelper *srcBuffer,
                                        const gl::ImageIndex index,
                                        uint32_t rowLength,
                                        uint32_t imageHeight,
                                        const gl::Box &sourceArea,
                                        size_t offset,
                                        VkImageAspectFlags aspectFlags);

    // Called from syncState to prepare the image for mipmap generation.
    void prepareForGenerateMipmap(ContextVk *contextVk);

    // Generate mipmaps from level 0 into the rest of the mips.  This requires the image to have
    // STORAGE usage.
    angle::Result generateMipmapsWithCompute(ContextVk *contextVk);

    angle::Result generateMipmapsWithCPU(const gl::Context *context);

    angle::Result generateMipmapLevelsWithCPU(ContextVk *contextVk,
                                              const angle::Format &sourceFormat,
                                              GLuint layer,
                                              gl::LevelIndex firstMipLevel,
                                              gl::LevelIndex maxMipLevel,
                                              const size_t sourceWidth,
                                              const size_t sourceHeight,
                                              const size_t sourceDepth,
                                              const size_t sourceRowPitch,
                                              const size_t sourceDepthPitch,
                                              uint8_t *sourceData);

    angle::Result copySubImageImpl(const gl::Context *context,
                                   const gl::ImageIndex &index,
                                   const gl::Offset &destOffset,
                                   const gl::Rectangle &sourceArea,
                                   const gl::InternalFormat &internalFormat,
                                   gl::Framebuffer *source);

    angle::Result copySubTextureImpl(ContextVk *contextVk,
                                     const gl::ImageIndex &index,
                                     const gl::Offset &dstOffset,
                                     const gl::InternalFormat &dstFormat,
                                     gl::LevelIndex sourceLevelGL,
                                     const gl::Box &sourceBox,
                                     bool unpackFlipY,
                                     bool unpackPremultiplyAlpha,
                                     bool unpackUnmultiplyAlpha,
                                     TextureVk *source);

    angle::Result copySubImageImplWithTransfer(ContextVk *contextVk,
                                               const gl::ImageIndex &index,
                                               const gl::Offset &dstOffset,
                                               const vk::Format &dstFormat,
                                               gl::LevelIndex sourceLevelGL,
                                               size_t sourceLayer,
                                               const gl::Box &sourceBox,
                                               vk::ImageHelper *srcImage);

    angle::Result copySubImageImplWithDraw(ContextVk *contextVk,
                                           const gl::ImageIndex &index,
                                           const gl::Offset &dstOffset,
                                           const vk::Format &dstFormat,
                                           gl::LevelIndex sourceLevelGL,
                                           const gl::Box &sourceBox,
                                           bool isSrcFlipY,
                                           bool unpackFlipY,
                                           bool unpackPremultiplyAlpha,
                                           bool unpackUnmultiplyAlpha,
                                           vk::ImageHelper *srcImage,
                                           const vk::ImageView *srcView,
                                           SurfaceRotation srcFramebufferRotation);

    angle::Result initImage(ContextVk *contextVk,
                            angle::FormatID intendedImageFormatID,
                            angle::FormatID actualImageFormatID,
                            ImageMipLevels mipLevels);
    void releaseImage(ContextVk *contextVk);
    void releaseImageViews(ContextVk *contextVk);
    void releaseStagedUpdates(ContextVk *contextVk);
    uint32_t getMipLevelCount(ImageMipLevels mipLevels) const;
    uint32_t getMaxLevelCount() const;
    angle::Result copyAndStageImageData(ContextVk *contextVk,
                                        gl::LevelIndex previousFirstAllocateLevel,
                                        vk::ImageHelper *srcImage,
                                        vk::ImageHelper *dstImage);
    angle::Result reinitImageAsRenderable(ContextVk *contextVk, const vk::Format &format);
    angle::Result initImageViews(ContextVk *contextVk, uint32_t levelCount);
    void initSingleLayerRenderTargets(ContextVk *contextVk,
                                      GLuint layerCount,
                                      gl::LevelIndex levelIndexGL,
                                      gl::RenderToTextureImageIndex renderToTextureIndex);
    RenderTargetVk *getMultiLayerRenderTarget(ContextVk *contextVk,
                                              gl::LevelIndex level,
                                              GLuint layerIndex,
                                              GLuint layerCount);
    angle::Result getLevelLayerImageView(vk::Context *context,
                                         gl::LevelIndex levelGL,
                                         size_t layer,
                                         const vk::ImageView **imageViewOut);

    // Flush image's staged updates for all levels and layers.
    angle::Result flushImageStagedUpdates(ContextVk *contextVk);

    angle::Result performImageQueueTransferIfNecessary(ContextVk *contextVk);

    // For various reasons, the underlying image may need to be respecified.  For example because
    // base/max level changed, usage/create flags have changed, the format needs modification to
    // become renderable, generate mipmap is adding levels, etc.  This function is called by
    // syncState and getAttachmentRenderTarget.  The latter calls this function to be able to sync
    // the texture's image while an attached framebuffer is being synced.  Note that we currently
    // sync framebuffers before textures so that the deferred clear optimization works.
    angle::Result respecifyImageStorageIfNecessary(ContextVk *contextVk, gl::Command source);

    const gl::InternalFormat &getImplementationSizedFormat(const gl::Context *context) const;
    const vk::Format &getBaseLevelFormat(vk::Renderer *renderer) const;
    // Queues a flush of any modified image attributes. The image will be reallocated with its new
    // attributes at the next opportunity.
    angle::Result respecifyImageStorage(ContextVk *contextVk);

    // Update base and max levels, and re-create image if needed.
    angle::Result maybeUpdateBaseMaxLevels(ContextVk *contextVk,
                                           TextureUpdateResult *changeResultOut);

    bool isFastUnpackPossible(const vk::Format &vkFormat,
                              size_t offset,
                              const vk::Format &bufferVkFormat) const;

    bool updateMustBeStaged(gl::LevelIndex textureLevelIndexGL, angle::FormatID dstFormatID) const;
    bool updateMustBeFlushed(gl::LevelIndex textureLevelIndexGL, angle::FormatID dstFormatID) const;
    bool shouldUpdateBeFlushed(gl::LevelIndex textureLevelIndexGL,
                               angle::FormatID dstFormatID) const
    {
        return updateMustBeFlushed(textureLevelIndexGL, dstFormatID) ||
               !updateMustBeStaged(textureLevelIndexGL, dstFormatID);
    }

    // We monitor the staging buffer and set dirty bits if the staging buffer changes. Note that we
    // support changes in the staging buffer even outside the TextureVk class.
    void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override;

    ANGLE_INLINE VkImageTiling getTilingMode()
    {
        return mImage != nullptr && mImage->valid() ? mImage->getTilingMode()
                                                    : VK_IMAGE_TILING_OPTIMAL;
    }

    angle::Result ensureMutable(ContextVk *contextVk);
    angle::Result refreshImageViews(ContextVk *contextVk);
    void initImageUsageFlags(ContextVk *contextVk, angle::FormatID actualFormatID);
    void handleImmutableSamplerTransition(const vk::ImageHelper *previousImage,
                                          const vk::ImageHelper *nextImage);

    vk::ImageAccess getRequiredImageAccess() const { return mRequiredImageAccess; }

    void stageSelfAsSubresourceUpdates(ContextVk *contextVk);

    vk::ImageOrBufferViewSubresourceSerial getImageViewSubresourceSerialImpl(
        vk::ImageViewColorspace colorspace) const;

    void updateCachedImageViewSerials();

    angle::Result updateTextureLabel(ContextVk *contextVk);

    vk::BufferHelper *getRGBAConversionBufferHelper(vk::Renderer *renderer,
                                                    angle::FormatID formatID) const;
    angle::Result convertBufferToRGBA(ContextVk *contextVk, size_t &conversionBufferSize);
    bool isCompressedFormatEmulated(const gl::Context *context,
                                    const gl::TextureTarget target,
                                    GLint level);

    angle::Result setStorageImpl(ContextVk *contextVk,
                                 gl::TextureType type,
                                 const vk::Format &format);

    GLint getFormatSupportedCompressionRatesImpl(vk::Renderer *renderer,
                                                 const vk::Format &format,
                                                 GLsizei bufSize,
                                                 GLint *rates);

    bool mOwnsImage;
    // Generated from ImageVk if EGLImage target, or from throw-away generator if Surface target.
    UniqueSerial mImageSiblingSerial;

    bool mRequiresMutableStorage;
    vk::ImageAccess mRequiredImageAccess;
    bool mImmutableSamplerDirty;

    // Only valid if this texture is an "EGLImage target" and the associated EGL Image was
    // originally sourced from an OpenGL texture. Such EGL Images can be a slice of the underlying
    // resource. The layer and level offsets are used to track the location of the slice.
    gl::TextureType mEGLImageNativeType;
    uint32_t mEGLImageLayerOffset;
    uint32_t mEGLImageLevelOffset;

    // If multisampled rendering to texture, an intermediate multisampled image is created for use
    // as renderpass color attachment. A map of an array of images and image views are used where -
    //
    // The map is keyed based on the number of samples used with multisampled rendering to texture.
    // Index 0 corresponds to the non-multisampled-render-to-texture usage of the texture.
    // - index 0: Unused.  See description of |mImage|.
    // - index N: intermediate multisampled image used for multisampled rendering to texture with
    //            1 << N samples
    //
    // Each element in the array corresponds to a mip-level
    //
    // - mMultisampledImages[N][M]: intermediate multisampled image with 1 << N samples
    //                              for level index M
    using MultiSampleImages = gl::RenderToTextureImageMap<gl::TexLevelArray<vk::ImageHelper>>;
    std::unique_ptr<MultiSampleImages> mMultisampledImages;

    // If multisampled rendering to texture, contains views for mMultisampledImages.
    //
    // - index 0: Unused.  See description of |mImageView|.
    // - mMultisampledImageViews[N][M]: views for mMultisampledImages[N][M]
    using MultiSampleImageViews =
        gl::RenderToTextureImageMap<gl::TexLevelArray<vk::ImageViewHelper>>;
    std::unique_ptr<MultiSampleImageViews> mMultisampledImageViews;

    // Texture buffers create texel buffer views instead.  |BufferViewHelper| contains the views
    // corresponding to the attached buffer range.
    vk::BufferViewHelper mBufferViews;

    // Render targets stored as array of vector of vectors
    //
    // - First dimension: index N contains render targets with views from mMultisampledImageViews[N]
    // - Second dimension: level M contains render targets with views from
    // mMultisampledImageViews[N][M]
    // - Third dimension: layer
    gl::RenderToTextureImageMap<std::vector<RenderTargetVector>> mSingleLayerRenderTargets;
    // Multi-layer render targets stored as a hash map.  This is used for layered attachments
    // which covers the entire layer (glFramebufferTextureLayer) or multiview attachments which
    // cover a range of layers (glFramebufferTextureMultiviewOVR).
    angle::HashMap<vk::ImageSubresourceRange, std::unique_ptr<RenderTargetVk>>
        mMultiLayerRenderTargets;

    // |mImage| wraps a VkImage and VkDeviceMemory that represents the gl::Texture. |mOwnsImage|
    // indicates that |TextureVk| owns the image. Otherwise it is a weak pointer shared with another
    // class. Due to this sharing, for example through EGL images, the image must always be
    // dynamically allocated as the texture can release ownership for example and it can be
    // transferred to another |TextureVk|.
    vk::ImageHelper *mImage;
    // The view is always owned by the Texture and is not shared like |mImage|. It also has
    // different lifetimes and can be reallocated independently of |mImage| on state changes.
    vk::ImageViewHelper mImageView;

    // |mSampler| contains the relevant Vulkan sampler states representing the OpenGL Texture
    // sampling states for the Texture.
    vk::SharedSamplerPtr mSampler;
    // |mY2YSampler| contains a version of mSampler that is meant for use with
    // __samplerExternal2DY2YEXT (i.e., skipping conversion of YUV to RGB).
    vk::SharedSamplerPtr mY2YSampler;

    // The created vkImage usage flag.
    VkImageUsageFlags mImageUsageFlags;

    // Additional image create flags
    VkImageCreateFlags mImageCreateFlags;

    // If an image level is incompatibly redefined, the image lives through the call that did this
    // (i.e. set and copy levels), because the image may be used by the framebuffer in the very same
    // call.  As a result, updates to this redefined level are staged (in both the call that
    // redefines it, and any future calls such as subimage updates).  This array flags redefined
    // levels so that their updates will be force-staged until image is recreated.  Each member of
    // the array is a bitmask per level, and it's an array of cube faces because GL allows
    // redefining each cube map face separately.  For other texture types, only index 0 is
    // meaningful as all array levels are redefined simultaneously.
    //
    // In common cases with mipmapped textures, the base/max level would need adjusting as the
    // texture is no longer mip-complete.  However, if every level is redefined such that at the end
    // the image becomes mip-complete again, no reinitialization of the image is done.  This array
    // is additionally used to ensure the image is recreated in the next syncState, if not already.
    //
    // Note: the elements of this array are bitmasks indexed by gl::LevelIndex, not vk::LevelIndex
    gl::CubeFaceArray<gl::TexLevelMask> mRedefinedLevels;

    angle::ObserverBinding mImageObserverBinding;

    // Saved between updates.
    gl::LevelIndex mCurrentBaseLevel;
    gl::LevelIndex mCurrentMaxLevel;

    // Cached subresource indexes.
    vk::ImageOrBufferViewSubresourceSerial mCachedImageViewSubresourceSerialSRGBDecode;
    vk::ImageOrBufferViewSubresourceSerial mCachedImageViewSubresourceSerialSkipDecode;

    // Manages the texture descriptor set cache that created with this texture
    vk::DescriptorSetCacheManager mDescriptorSetCacheManager;
};

}  // namespace rx

#endif  // LIBANGLE_RENDERER_VULKAN_TEXTUREVK_H_
