1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include <cstdint>
18 #include <memory>
19 #include <string>
20 #include <vector>
21 
22 #include "vulkan/emulated_textures/AstcTexture.h"
23 #include "vulkan/emulated_textures/GpuDecompressionPipeline.h"
24 #include "goldfish_vk_dispatch.h"
25 #include "vulkan/vulkan.h"
26 
27 namespace gfxstream {
28 namespace vk {
29 
30 class CompressedImageInfo {
31    public:
32     // Static methods
33 
34     // Returns the format that this image will be converted to, if the original format isn't
35     // natively supported by the GPU.
36     static VkFormat getOutputFormat(VkFormat compFmt);
37 
38     // Returns the image format used to store the compressed data. Each pixel in the compressed
39     // mipmaps will hold an entire compressed block.
40     static VkFormat getCompressedMipmapsFormat(VkFormat compFmt);
41 
42     static bool needEmulatedAlpha(VkFormat format);
43 
44     // Returns a VkImageCopy to copy to/from the compressed data
45     static VkImageCopy getCompressedMipmapsImageCopy(const VkImageCopy& origRegion,
46                                                      const CompressedImageInfo& srcImg,
47                                                      const CompressedImageInfo& dstImg,
48                                                      bool needEmulatedSrc, bool needEmulatedDst);
49     static VkImageCopy2 getCompressedMipmapsImageCopy(const VkImageCopy2& origRegion,
50                                                       const CompressedImageInfo& srcImg,
51                                                       const CompressedImageInfo& dstImg,
52                                                       bool needEmulatedSrc, bool needEmulatedDst);
53 
54 
55     // Constructors
56 
57     // TODO(gregschlom) Delete these constructors once we switch to holding a
58     //  std::unique_ptr<CompressedImageInfo>
59     CompressedImageInfo() = default;
60     explicit CompressedImageInfo(VkDevice device);
61 
62     CompressedImageInfo(VkDevice device, const VkImageCreateInfo& createInfo,
63                         GpuDecompressionPipelineManager* pipelineManager);
64 
65     // Public methods
66 
67     // Returns the VkImageCreateInfo needed to create the output image
68     VkImageCreateInfo getOutputCreateInfo(const VkImageCreateInfo& createInfo) const;
69 
70     // Creates the compressed mipmap images, that is the VkImages holding the compressed data
71     void createCompressedMipmapImages(VulkanDispatch* vk, const VkImageCreateInfo& createInfo);
72 
73     // Initializes the resources needed to perform CPU decompression of ASTC textures
74     void initAstcCpuDecompression(VulkanDispatch* vk, VkPhysicalDevice physicalDevice);
75 
76     // Should be called when the guest calls vkCmdPipelineBarrier.
77     // This function checks if the image barrier transitions the compressed image to a layout where
78     // it will be read from, and if so, it decompresses the image.
79     //
80     // outputBarriers: any barrier that needs to be passed to the vkCmdPipelineBarrier call will be
81     // added to this vector.
82     // Returns whether image decompression happened.
83     // Note: the global lock must be held when calling this method, because we call into
84     // GpuDecompressionPipelineManager.
85     bool decompressIfNeeded(VulkanDispatch* vk, VkCommandBuffer commandBuffer,
86                             VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
87                             const VkImageMemoryBarrier& targetBarrier,
88                             std::vector<VkImageMemoryBarrier>& outputBarriers);
89 
90     void decompressOnCpu(VkCommandBuffer commandBuffer, uint8_t* srcAstcData, size_t astcDataSize,
91                          VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
92                          const VkBufferImageCopy* pRegions, const VkDecoderContext& context);
93 
94     void decompressOnCpu(VkCommandBuffer commandBuffer, uint8_t* srcAstcData, size_t astcDataSize,
95                          const VkCopyBufferToImageInfo2* pCopyBufferToImageInfo, const VkDecoderContext& context);
96 
97     VkMemoryRequirements getMemoryRequirements() const;
98 
99     VkResult bindCompressedMipmapsMemory(VulkanDispatch* vk, VkDeviceMemory memory,
100                                          VkDeviceSize memoryOffset);
101 
102     // Given a VkBufferImageCopy object for the original image, returns a new
103     // VkBufferImageCopy that points to the same location in the compressed mipmap images.
104     VkBufferImageCopy getBufferImageCopy(const VkBufferImageCopy& origRegion) const;
105     VkBufferImageCopy2 getBufferImageCopy(const VkBufferImageCopy2& origRegion) const;
106 
107     // Releases all the resources used by this class. It may no longer be used after calling this.
108     void destroy(VulkanDispatch* vk);
109 
110     // Accessors
111 
112     bool isEtc2() const;
113     bool isAstc() const;
device()114     VkDevice device() const { return mDevice; }
compressedMipmap(uint32_t level)115     VkImage compressedMipmap(uint32_t level) { return mCompressedMipmaps[level]; }
outputImage()116     VkImage outputImage() { return mOutputImage; }
setOutputImage(VkImage image)117     void setOutputImage(VkImage image) { mOutputImage = image; }
canDecompressOnCpu()118     bool canDecompressOnCpu() { return mAstcTexture != nullptr; }
successfullyDecompressedOnCpu()119     bool successfullyDecompressedOnCpu() const {
120         return mAstcTexture && mAstcTexture->successfullyDecompressed();
121     }
122 
123    private:
124     // Returns a vector of image barriers for the compressed mipmap images and the decompressed
125     // image.
126     std::vector<VkImageMemoryBarrier> getImageBarriers(const VkImageMemoryBarrier& srcBarrier);
127 
128     VkImageSubresourceRange getImageSubresourceRange(const VkImageSubresourceRange& range) const;
129 
130     // Initializes the compute shader pipeline to decompress the image.
131     // No-op if this was already called successfully.
132     VkResult initializeDecompressionPipeline(VulkanDispatch* vk, VkDevice device);
133 
134     // Runs the decompression shader
135     void decompress(VulkanDispatch* vk, VkCommandBuffer commandBuffer,
136                     const VkImageSubresourceRange& range);
137 
138     // Returns the size of the image at a given mip level
139     VkExtent3D mipmapExtent(uint32_t level) const;
140     // Returns the size of the compressed mipmaps at a given mip level. This is mipmapExtent divided
141     // by the block size, and rounded up.
142     VkExtent3D compressedMipmapExtent(uint32_t level) const;
143     // Returns an extent into the compressed mipmaps. This divides the components of origExtent by
144     // the block size, and the result is clamped to not exceed the compressed mipmap size.
145     VkExtent3D compressedMipmapPortion(const VkExtent3D& origExtent, uint32_t level) const;
146 
147     // Member variables
148 
149     // The original compressed format of this image. E.g.: VK_FORMAT_ASTC_4x4_UNORM_BLOCK
150     VkFormat mCompressedFormat = VK_FORMAT_UNDEFINED;
151     // The format that we decompressed the image to. E.g.: VK_FORMAT_R8G8B8A8_UINT
152     VkFormat mOutputFormat = VK_FORMAT_UNDEFINED;
153     // The format that we use to store the compressed data, since the original compressed format
154     // isn't available. This holds one compressed block per pixel. E.g.: VK_FORMAT_R32G32B32A32_UINT
155     VkFormat mCompressedMipmapsFormat = VK_FORMAT_UNDEFINED;
156 
157     VkImageType mImageType = VK_IMAGE_TYPE_MAX_ENUM;
158     uint32_t mMipLevels = 1;     // Number of mip levels in the image
159     VkExtent3D mExtent = {};     // Size of the image
160     VkExtent2D mBlock = {1, 1};  // Size of the compressed blocks
161     uint32_t mLayerCount = 1;
162 
163     VkDevice mDevice = VK_NULL_HANDLE;
164     VkImage mOutputImage = VK_NULL_HANDLE;
165 
166     // Compressed data. Each mip level of the original image is stored as a separate VkImage, and
167     // each pixel in those images contains an entire compressed block.
168     std::vector<VkImage> mCompressedMipmaps;
169 
170     // The memory offset that we will use for each compressed mipmap.
171     std::vector<VkDeviceSize> mMipmapOffsets;
172 
173     VkMemoryRequirements mMemoryRequirements;
174 
175     // Used to perform CPU decompression of ASTC textures. Null for non-ASTC images.
176     std::unique_ptr<AstcTexture> mAstcTexture = nullptr;
177 
178     // Vulkan resources used by the decompression pipeline
179     GpuDecompressionPipelineManager* mPipelineManager = nullptr;
180     GpuDecompressionPipeline* mDecompPipeline = nullptr;
181     std::vector<VkDescriptorSet> mDecompDescriptorSets;
182     VkDescriptorPool mDecompDescriptorPool = VK_NULL_HANDLE;
183     std::vector<VkImageView> mCompressedMipmapsImageViews;
184     std::vector<VkImageView> mOutputImageViews;
185     bool mDecompPipelineInitialized = false;
186 };
187 
188 }  // namespace vk
189 }  // namespace gfxstream
190