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