1 // Copyright 2023 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 #include "GpuDecompressionPipeline.h"
16 
17 #include "host-common/logging.h"
18 #include "vulkan/VkFormatUtils.h"
19 #include "vulkan/emulated_textures/shaders/DecompressionShaders.h"
20 #include "vulkan/vk_enum_string_helper.h"
21 
22 namespace gfxstream {
23 namespace vk {
24 
25 namespace {
26 
27 // Which GPU decoder we use for ASTC textures.
28 // Note: we currently only enable ASTC decompression for native vulkan apps, and we try CPU
29 // decompression first, before falling back to GPU decompression.
30 static AstcDecoder activeAstcDecoder = AstcDecoder::NewRgb;
31 
32 struct ShaderGroup {
33     ShaderData shader1D;
34     ShaderData shader2D;
35     ShaderData shader3D;
36 };
37 
38 // Helper macro to declare the shader goups
39 #define DECLARE_SHADER_GROUP(Format)                                      \
40     constexpr ShaderGroup kShader##Format {                               \
41         .shader1D = {.code = decompression_shaders::Format##_1D,          \
42                      .size = sizeof(decompression_shaders::Format##_1D)}, \
43         .shader2D = {.code = decompression_shaders::Format##_2D,          \
44                      .size = sizeof(decompression_shaders::Format##_2D)}, \
45         .shader3D = {.code = decompression_shaders::Format##_3D,          \
46                      .size = sizeof(decompression_shaders::Format##_3D)}, \
47     }
48 
49 DECLARE_SHADER_GROUP(Astc);
50 DECLARE_SHADER_GROUP(AstcToRgb);
51 DECLARE_SHADER_GROUP(AstcToBc3);
52 DECLARE_SHADER_GROUP(EacR11Snorm);
53 DECLARE_SHADER_GROUP(EacR11Unorm);
54 DECLARE_SHADER_GROUP(EacRG11Snorm);
55 DECLARE_SHADER_GROUP(EacRG11Unorm);
56 DECLARE_SHADER_GROUP(Etc2RGB8);
57 DECLARE_SHADER_GROUP(Etc2RGBA8);
58 
59 #undef DECLARE_SHADER_GROUP
60 
61 // Returns the group of shaders that can decompress a given format, or null if none is found.
getShaderGroup(VkFormat format)62 const ShaderGroup* getShaderGroup(VkFormat format) {
63     switch (format) {
64         case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
65         case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
66         case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
67         case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
68         case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
69         case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
70         case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
71         case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
72         case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
73         case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
74         case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
75         case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
76         case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
77         case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
78         case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
79         case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
80         case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
81         case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
82         case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
83         case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
84         case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
85         case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
86         case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
87         case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
88         case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
89         case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
90         case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
91         case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
92             switch (activeAstcDecoder) {
93                 case AstcDecoder::Old:
94                     return &kShaderAstc;
95                 case AstcDecoder::NewRgb:
96                     return &kShaderAstcToRgb;
97                 case AstcDecoder::NewBc3:
98                     return &kShaderAstcToBc3;
99             }
100         case VK_FORMAT_EAC_R11_SNORM_BLOCK:
101             return &kShaderEacR11Snorm;
102 
103         case VK_FORMAT_EAC_R11_UNORM_BLOCK:
104             return &kShaderEacR11Unorm;
105 
106         case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
107             return &kShaderEacRG11Snorm;
108 
109         case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
110             return &kShaderEacRG11Unorm;
111 
112         case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
113         case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
114         case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
115         case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
116             return &kShaderEtc2RGB8;
117 
118         case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
119         case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
120             return &kShaderEtc2RGBA8;
121 
122         default:
123             return nullptr;
124     }
125 }
126 
127 // Returns the shader that can decompress a given image format and type
getDecompressionShader(VkFormat format,VkImageType imageType)128 const ShaderData* getDecompressionShader(VkFormat format, VkImageType imageType) {
129     const ShaderGroup* group = getShaderGroup(format);
130     if (!group) return nullptr;
131 
132     switch (imageType) {
133         case VK_IMAGE_TYPE_1D:
134             return &group->shader1D;
135         case VK_IMAGE_TYPE_2D:
136             return &group->shader2D;
137         case VK_IMAGE_TYPE_3D:
138             return &group->shader3D;
139         default:
140             return nullptr;
141     }
142 }
143 
string_AstcDecoder(AstcDecoder decoder)144 const char* string_AstcDecoder(AstcDecoder decoder) {
145     switch (decoder) {
146         case AstcDecoder::Old:
147             return "Old";
148         case AstcDecoder::NewRgb:
149             return "NewRgb";
150         case AstcDecoder::NewBc3:
151             return "NewBc3";
152     }
153     return "Unknown";
154 }
155 
156 }  // namespace
157 
158 // static
create(VulkanDispatch * vk,VkDevice device,VkFormat compressedFormat,VkImageType imageType,VkDescriptorSetLayout descriptorSetLayout,VkPipelineLayout pipelineLayout)159 std::unique_ptr<GpuDecompressionPipeline> GpuDecompressionPipeline::create(
160     VulkanDispatch* vk, VkDevice device, VkFormat compressedFormat,
161     VkImageType imageType, VkDescriptorSetLayout descriptorSetLayout,
162     VkPipelineLayout pipelineLayout) {
163     auto pipeline = std::unique_ptr<GpuDecompressionPipeline>(new GpuDecompressionPipeline(
164         vk, device, compressedFormat, imageType, descriptorSetLayout, pipelineLayout));
165     if (!pipeline->initialize()) {
166         return nullptr;
167     }
168     return pipeline;
169 }
170 
GpuDecompressionPipeline(VulkanDispatch * vk,VkDevice device,VkFormat compressedFormat,VkImageType imageType,VkDescriptorSetLayout descriptorSetLayout,VkPipelineLayout pipelineLayout)171 GpuDecompressionPipeline::GpuDecompressionPipeline(VulkanDispatch* vk, VkDevice device,
172                                                    VkFormat compressedFormat, VkImageType imageType,
173                                                    VkDescriptorSetLayout descriptorSetLayout,
174                                                    VkPipelineLayout pipelineLayout)
175     : mVk(vk),
176       mDevice(device),
177       mCompressedFormat(compressedFormat),
178       mImageType(imageType),
179       mDescriptorSetLayout(descriptorSetLayout),
180       mPipelineLayout(pipelineLayout) {
181     INFO("Created GPU decompression pipeline for format %s %s. ASTC decoder: %s",
182          string_VkFormat(mCompressedFormat), string_VkImageType(imageType),
183          string_AstcDecoder(activeAstcDecoder));
184 }
185 
initialize()186 bool GpuDecompressionPipeline::initialize() {
187     const ShaderData* shaderData = getDecompressionShader(mCompressedFormat, mImageType);
188     if (!shaderData) {
189         WARN("No decompression shader found for format %s and img type %s",
190              string_VkFormat(mCompressedFormat), string_VkImageType(mImageType));
191         return false;
192     }
193 
194     VkShaderModuleCreateInfo shaderInfo = {
195         .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
196         .codeSize = shaderData->size,
197         .pCode = shaderData->code,
198     };
199     VkShaderModule shader;
200     VkResult result = mVk->vkCreateShaderModule(mDevice, &shaderInfo, nullptr, &shader);
201     if (result != VK_SUCCESS) {
202         WARN("GPU decompression: error calling vkCreateShaderModule: %d", result);
203         return false;
204     }
205 
206     VkComputePipelineCreateInfo computePipelineInfo = {
207         .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
208         .stage = {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
209                   .stage = VK_SHADER_STAGE_COMPUTE_BIT,
210                   .module = shader,
211                   .pName = "main"},
212         .layout = mPipelineLayout,
213     };
214     result = mVk->vkCreateComputePipelines(mDevice, nullptr, 1, &computePipelineInfo, nullptr,
215                                            &mPipeline);
216     mVk->vkDestroyShaderModule(mDevice, shader, nullptr);
217     if (result != VK_SUCCESS) {
218         WARN("GPU decompression: error calling vkCreateComputePipelines: %d", result);
219         return false;
220     }
221     return true;
222 }
223 
~GpuDecompressionPipeline()224 GpuDecompressionPipeline::~GpuDecompressionPipeline() {
225     if (!mVk || !mDevice) return;
226     mVk->vkDestroyPipeline(mDevice, mPipeline, nullptr);
227 }
228 
229 // static
setAstcDecoder(AstcDecoder value)230 void GpuDecompressionPipelineManager::setAstcDecoder(AstcDecoder value) {
231     activeAstcDecoder = value;
232 }
astcDecoder()233 AstcDecoder GpuDecompressionPipelineManager::astcDecoder() { return activeAstcDecoder; }
234 
GpuDecompressionPipelineManager(VulkanDispatch * vk,VkDevice device)235 GpuDecompressionPipelineManager::GpuDecompressionPipelineManager(VulkanDispatch* vk,
236                                                                  VkDevice device)
237     : mVk(vk), mDevice(device) {}
238 
get(VkFormat compressedFormat,VkImageType imageType)239 GpuDecompressionPipeline* GpuDecompressionPipelineManager::get(VkFormat compressedFormat,
240                                                                VkImageType imageType) {
241     auto& pipeline = mPipelines[getDecompressionShader(compressedFormat, imageType)];
242     if (!pipeline) {
243         VkDescriptorSetLayout descriptorSetLayout = getDescriptorSetLayout();
244         if (descriptorSetLayout == VK_NULL_HANDLE) return nullptr;
245 
246         VkPipelineLayout pipelineLayout = getPipelineLayout(compressedFormat);
247         if (pipelineLayout == VK_NULL_HANDLE) return nullptr;
248 
249         pipeline = GpuDecompressionPipeline::create(mVk, mDevice, compressedFormat, imageType,
250                                                     descriptorSetLayout, pipelineLayout);
251     }
252     return pipeline.get();
253 }
254 
getDescriptorSetLayout()255 VkDescriptorSetLayout GpuDecompressionPipelineManager::getDescriptorSetLayout() {
256     VkDescriptorSetLayoutBinding dsLayoutBindings[] = {
257         {
258             .binding = 0,
259             .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
260             .descriptorCount = 1,
261             .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
262         },
263         {
264             .binding = 1,
265             .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
266             .descriptorCount = 1,
267             .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
268         },
269     };
270     VkDescriptorSetLayoutCreateInfo dsLayoutInfo = {
271         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
272         .bindingCount = std::size(dsLayoutBindings),
273         .pBindings = dsLayoutBindings,
274     };
275     VkResult result =
276         mVk->vkCreateDescriptorSetLayout(mDevice, &dsLayoutInfo, nullptr, &mDescriptorSetLayout);
277     if (result != VK_SUCCESS) {
278         WARN("GPU decompression: error calling vkCreateDescriptorSetLayout: %d", result);
279         return VK_NULL_HANDLE;
280     }
281     return mDescriptorSetLayout;
282 }
283 
getPipelineLayout(VkFormat format)284 VkPipelineLayout GpuDecompressionPipelineManager::getPipelineLayout(VkFormat format) {
285     VkPipelineLayout* pipelineLayout;
286     VkPushConstantRange pushConstant = {.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT};
287 
288     if (isAstc(format)) {
289         pipelineLayout = &mAstcPipelineLayout;
290         pushConstant.size = sizeof(AstcPushConstant);
291     } else {
292         pipelineLayout = &mEtc2PipelineLayout;
293         pushConstant.size = sizeof(Etc2PushConstant);
294     }
295 
296     VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
297         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
298         .setLayoutCount = 1,
299         .pSetLayouts = &mDescriptorSetLayout,
300         .pushConstantRangeCount = 1,
301         .pPushConstantRanges = &pushConstant,
302     };
303     VkResult result =
304         mVk->vkCreatePipelineLayout(mDevice, &pipelineLayoutInfo, nullptr, pipelineLayout);
305     if (result != VK_SUCCESS) {
306         WARN("GPU decompression: error calling vkCreatePipelineLayout for format %s: %d",
307              string_VkFormat(format), result);
308         return VK_NULL_HANDLE;
309     }
310     return *pipelineLayout;
311 }
312 
clear()313 void GpuDecompressionPipelineManager::clear() {
314     mPipelines.clear();
315     if (mVk && mDevice) {
316         mVk->vkDestroyDescriptorSetLayout(mDevice, mDescriptorSetLayout, nullptr);
317         mVk->vkDestroyPipelineLayout(mDevice, mAstcPipelineLayout, nullptr);
318         mVk->vkDestroyPipelineLayout(mDevice, mEtc2PipelineLayout, nullptr);
319 
320         mDescriptorSetLayout = VK_NULL_HANDLE;
321         mAstcPipelineLayout = VK_NULL_HANDLE;
322         mEtc2PipelineLayout = VK_NULL_HANDLE;
323     }
324 }
325 
~GpuDecompressionPipelineManager()326 GpuDecompressionPipelineManager::~GpuDecompressionPipelineManager() {
327     if (!mPipelines.empty() || mDescriptorSetLayout != VK_NULL_HANDLE ||
328         mAstcPipelineLayout != VK_NULL_HANDLE || mEtc2PipelineLayout != VK_NULL_HANDLE) {
329         WARN(
330             "Resource leak: GpuDecompressionPipelineManager is being destroyed but clear() wasn't"
331             " called first");
332     }
333 }
334 
335 }  // namespace vk
336 }  // namespace gfxstream
337