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 #include "CompressedImageInfo.h"
16
17 #include "aemu/base/ArraySize.h"
18 #include "stream-servers/vulkan/VkFormatUtils.h"
19 #include "stream-servers/vulkan/emulated_textures/shaders/DecompressionShaders.h"
20
21 namespace gfxstream {
22 namespace vk {
23 namespace {
24
25 #define _RETURN_ON_FAILURE(cmd) \
26 { \
27 VkResult result = cmd; \
28 if (result != VK_SUCCESS) { \
29 WARN("Warning: %s %s:%d vulkan failure %d", __func__, __FILE__, __LINE__, result); \
30 return result; \
31 } \
32 }
33
34 using android::base::arraySize;
35
36 struct Etc2PushConstant {
37 uint32_t compFormat;
38 uint32_t baseLayer;
39 };
40
41 struct AstcPushConstant {
42 uint32_t blockSize[2];
43 uint32_t baseLayer;
44 uint32_t smallBlock;
45 };
46
47 struct ShaderData {
48 const uint32_t* code; // Pointer to shader's compiled spir-v code
49 const size_t size; // size of the code in bytes
50 };
51
52 struct ShaderGroup {
53 ShaderData shader1D;
54 ShaderData shader2D;
55 ShaderData shader3D;
56 };
57
58 // Helper macro to declare the shader goups
59 #define DECLARE_SHADER_GROUP(Format) \
60 constexpr ShaderGroup kShader##Format { \
61 .shader1D = {.code = decompression_shaders::Format##_1D, \
62 .size = sizeof(decompression_shaders::Format##_1D)}, \
63 .shader2D = {.code = decompression_shaders::Format##_2D, \
64 .size = sizeof(decompression_shaders::Format##_2D)}, \
65 .shader3D = {.code = decompression_shaders::Format##_3D, \
66 .size = sizeof(decompression_shaders::Format##_3D)}, \
67 }
68
69 DECLARE_SHADER_GROUP(Astc);
70 DECLARE_SHADER_GROUP(EacR11Snorm);
71 DECLARE_SHADER_GROUP(EacR11Unorm);
72 DECLARE_SHADER_GROUP(EacRG11Snorm);
73 DECLARE_SHADER_GROUP(EacRG11Unorm);
74 DECLARE_SHADER_GROUP(Etc2RGB8);
75 DECLARE_SHADER_GROUP(Etc2RGBA8);
76
77 #undef DECLARE_SHADER_GROUP
78
79 // Returns the group of shaders that can decompress a given format, or null if none is found.
getShaderGroup(VkFormat format)80 const ShaderGroup* getShaderGroup(VkFormat format) {
81 switch (format) {
82 case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
83 case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
84 case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
85 case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
86 case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
87 case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
88 case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
89 case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
90 case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
91 case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
92 case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
93 case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
94 case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
95 case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
96 case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
97 case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
98 case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
99 case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
100 case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
101 case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
102 case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
103 case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
104 case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
105 case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
106 case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
107 case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
108 case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
109 case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
110 return &kShaderAstc;
111
112 case VK_FORMAT_EAC_R11_SNORM_BLOCK:
113 return &kShaderEacR11Snorm;
114
115 case VK_FORMAT_EAC_R11_UNORM_BLOCK:
116 return &kShaderEacR11Unorm;
117
118 case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
119 return &kShaderEacRG11Snorm;
120
121 case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
122 return &kShaderEacRG11Unorm;
123
124 case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
125 case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
126 case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
127 case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
128 return &kShaderEtc2RGB8;
129
130 case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
131 case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
132 return &kShaderEtc2RGBA8;
133
134 default:
135 return nullptr;
136 }
137 }
138
139 // Returns the shader that can decompress a given image format and type
getDecompressionShader(VkFormat format,VkImageType imageType)140 const ShaderData* getDecompressionShader(VkFormat format, VkImageType imageType) {
141 const ShaderGroup* group = getShaderGroup(format);
142 if (!group) return nullptr;
143
144 switch (imageType) {
145 case VK_IMAGE_TYPE_1D:
146 return &group->shader1D;
147 case VK_IMAGE_TYPE_2D:
148 return &group->shader2D;
149 case VK_IMAGE_TYPE_3D:
150 return &group->shader3D;
151 default:
152 return nullptr;
153 }
154 }
155
156 // Returns x / y, rounded up. E.g. ceil_div(7, 2) == 4
157 // Note the potential integer overflow for large numbers.
ceil_div(uint32_t x,uint32_t y)158 inline constexpr uint32_t ceil_div(uint32_t x, uint32_t y) { return (x + y - 1) / y; }
159
createDefaultImageView(VulkanDispatch * vk,VkDevice device,VkImage image,VkFormat format,VkImageType imageType,uint32_t mipLevel,uint32_t layerCount)160 VkImageView createDefaultImageView(VulkanDispatch* vk, VkDevice device, VkImage image,
161 VkFormat format, VkImageType imageType, uint32_t mipLevel,
162 uint32_t layerCount) {
163 VkImageViewCreateInfo imageViewInfo = {};
164 imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
165 imageViewInfo.image = image;
166 switch (imageType) {
167 case VK_IMAGE_TYPE_1D:
168 imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
169 break;
170 case VK_IMAGE_TYPE_2D:
171 imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
172 break;
173 case VK_IMAGE_TYPE_3D:
174 imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
175 break;
176 default:
177 imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
178 break;
179 }
180 imageViewInfo.format = format;
181 imageViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
182 imageViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
183 imageViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
184 imageViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
185 imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
186 imageViewInfo.subresourceRange.baseMipLevel = mipLevel;
187 imageViewInfo.subresourceRange.levelCount = 1;
188 imageViewInfo.subresourceRange.baseArrayLayer = 0;
189 imageViewInfo.subresourceRange.layerCount = layerCount;
190 VkImageView imageView;
191 if (vk->vkCreateImageView(device, &imageViewInfo, nullptr, &imageView) != VK_SUCCESS) {
192 WARN("Warning: %s %s:%d failure", __func__, __FILE__, __LINE__);
193 return VK_NULL_HANDLE;
194 }
195 return imageView;
196 }
197
getBlockSize(VkFormat format)198 VkExtent2D getBlockSize(VkFormat format) {
199 switch (format) {
200 case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
201 case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
202 case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
203 case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
204 case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
205 case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
206 case VK_FORMAT_EAC_R11_UNORM_BLOCK:
207 case VK_FORMAT_EAC_R11_SNORM_BLOCK:
208 case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
209 case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
210 return {4, 4};
211 case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
212 case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
213 return {4, 4};
214 case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
215 case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
216 return {5, 4};
217 case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
218 case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
219 return {5, 5};
220 case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
221 case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
222 return {6, 5};
223 case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
224 case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
225 return {6, 6};
226 case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
227 case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
228 return {8, 5};
229 case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
230 case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
231 return {8, 6};
232 case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
233 case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
234 return {8, 8};
235 case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
236 case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
237 return {10, 5};
238 case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
239 case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
240 return {10, 6};
241 case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
242 case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
243 return {10, 8};
244 case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
245 case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
246 return {10, 10};
247 case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
248 case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
249 return {12, 10};
250 case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
251 case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
252 return {12, 12};
253 default:
254 return {1, 1};
255 }
256 }
257
258 // Returns whether a given memory barrier puts the image in a layout where it can be read from.
imageWillBecomeReadable(const VkImageMemoryBarrier & barrier)259 bool imageWillBecomeReadable(const VkImageMemoryBarrier& barrier) {
260 return barrier.oldLayout != VK_IMAGE_LAYOUT_UNDEFINED &&
261 (barrier.newLayout == VK_IMAGE_LAYOUT_GENERAL ||
262 barrier.newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ||
263 barrier.newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
264 }
265
266 } // namespace
267
CompressedImageInfo(VkDevice device)268 CompressedImageInfo::CompressedImageInfo(VkDevice device) : mDevice(device) {}
269
CompressedImageInfo(VkDevice device,const VkImageCreateInfo & createInfo)270 CompressedImageInfo::CompressedImageInfo(VkDevice device, const VkImageCreateInfo& createInfo)
271 : mDevice(device),
272 mCompressedFormat(createInfo.format),
273 mDecompressedFormat(getDecompressedFormat(mCompressedFormat)),
274 mCompressedMipmapsFormat(getCompressedMipmapsFormat(mCompressedFormat)),
275 mImageType(createInfo.imageType),
276 mExtent(createInfo.extent),
277 mBlock(getBlockSize(mCompressedFormat)),
278 mLayerCount(createInfo.arrayLayers),
279 mMipLevels(createInfo.mipLevels) {}
280
281 // static
getDecompressedFormat(VkFormat compFmt)282 VkFormat CompressedImageInfo::getDecompressedFormat(VkFormat compFmt) {
283 switch (compFmt) {
284 case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
285 case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
286 case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
287 return VK_FORMAT_R8G8B8A8_UNORM;
288 case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
289 case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
290 case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
291 return VK_FORMAT_R8G8B8A8_SRGB;
292 case VK_FORMAT_EAC_R11_UNORM_BLOCK:
293 return VK_FORMAT_R16_UNORM;
294 case VK_FORMAT_EAC_R11_SNORM_BLOCK:
295 return VK_FORMAT_R16_SNORM;
296 case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
297 return VK_FORMAT_R16G16_UNORM;
298 case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
299 return VK_FORMAT_R16G16_SNORM;
300 case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
301 case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
302 case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
303 case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
304 case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
305 case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
306 case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
307 case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
308 case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
309 case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
310 case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
311 case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
312 case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
313 case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
314 return VK_FORMAT_R8G8B8A8_UNORM;
315 case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
316 case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
317 case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
318 case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
319 case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
320 case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
321 case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
322 case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
323 case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
324 case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
325 case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
326 case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
327 case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
328 case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
329 return VK_FORMAT_R8G8B8A8_SRGB;
330 default:
331 return compFmt;
332 }
333 }
334
335 // static
getCompressedMipmapsFormat(VkFormat compFmt)336 VkFormat CompressedImageInfo::getCompressedMipmapsFormat(VkFormat compFmt) {
337 switch (compFmt) {
338 case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
339 case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
340 case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
341 case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
342 return VK_FORMAT_R16G16B16A16_UINT;
343 case VK_FORMAT_EAC_R11_UNORM_BLOCK:
344 case VK_FORMAT_EAC_R11_SNORM_BLOCK:
345 return VK_FORMAT_R32G32_UINT;
346 case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
347 case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
348 case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
349 case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
350 case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
351 case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
352 case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
353 case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
354 case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
355 case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
356 case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
357 case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
358 case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
359 case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
360 case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
361 case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
362 case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
363 case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
364 case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
365 case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
366 case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
367 case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
368 case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
369 case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
370 case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
371 case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
372 case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
373 case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
374 case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
375 case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
376 case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
377 case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
378 return VK_FORMAT_R32G32B32A32_UINT;
379 default:
380 return compFmt;
381 }
382 }
383
384 // static
isEtc2(VkFormat format)385 bool CompressedImageInfo::isEtc2(VkFormat format) {
386 switch (format) {
387 case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
388 case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
389 case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
390 case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
391 case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
392 case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
393 case VK_FORMAT_EAC_R11_UNORM_BLOCK:
394 case VK_FORMAT_EAC_R11_SNORM_BLOCK:
395 case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
396 case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
397 return true;
398 default:
399 return false;
400 }
401 }
402
403 // static
isAstc(VkFormat format)404 bool CompressedImageInfo::isAstc(VkFormat format) {
405 switch (format) {
406 case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
407 case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
408 case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
409 case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
410 case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
411 case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
412 case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
413 case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
414 case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
415 case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
416 case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
417 case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
418 case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
419 case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
420 case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
421 case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
422 case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
423 case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
424 case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
425 case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
426 case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
427 case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
428 case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
429 case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
430 case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
431 case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
432 case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
433 case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
434 return true;
435 default:
436 return false;
437 }
438 }
439
440 // static
needEmulatedAlpha(VkFormat format)441 bool CompressedImageInfo::needEmulatedAlpha(VkFormat format) {
442 switch (format) {
443 case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
444 case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
445 return true;
446 default:
447 return false;
448 }
449 }
450
isEtc2() const451 bool CompressedImageInfo::isEtc2() const { return isEtc2(mCompressedFormat); }
452
isAstc() const453 bool CompressedImageInfo::isAstc() const { return isAstc(mCompressedFormat); }
454
getDecompressedCreateInfo(const VkImageCreateInfo & createInfo) const455 VkImageCreateInfo CompressedImageInfo::getDecompressedCreateInfo(
456 const VkImageCreateInfo& createInfo) const {
457 VkImageCreateInfo result = createInfo;
458 result.format = mDecompressedFormat;
459 result.flags &= ~VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR;
460 result.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
461 result.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
462 return result;
463 }
464
createCompressedMipmapImages(VulkanDispatch * vk,const VkImageCreateInfo & createInfo)465 void CompressedImageInfo::createCompressedMipmapImages(VulkanDispatch* vk,
466 const VkImageCreateInfo& createInfo) {
467 if (!mCompressedMipmaps.empty()) {
468 return;
469 }
470
471 VkImageCreateInfo createInfoCopy = createInfo;
472 createInfoCopy.format = mCompressedMipmapsFormat;
473 createInfoCopy.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
474 createInfoCopy.flags &= ~VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR;
475 createInfoCopy.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
476 createInfoCopy.mipLevels = 1;
477
478 mCompressedMipmaps.resize(mMipLevels);
479 for (uint32_t i = 0; i < mMipLevels; i++) {
480 createInfoCopy.extent = compressedMipmapExtent(i);
481 vk->vkCreateImage(mDevice, &createInfoCopy, nullptr, mCompressedMipmaps.data() + i);
482 }
483
484 // Get the size of all images (decompressed image and compressed mipmaps)
485 std::vector<VkDeviceSize> memSizes(mMipLevels + 1);
486 memSizes[0] = getImageSize(vk, mDecompressedImage);
487 for (size_t i = 0; i < mMipLevels; i++) {
488 memSizes[i + 1] = getImageSize(vk, mCompressedMipmaps[i]);
489 }
490
491 // Initialize the memory offsets
492 mMemoryOffsets.resize(mMipLevels + 1);
493 for (size_t i = 0; i < mMipLevels + 1; i++) {
494 VkDeviceSize alignedSize = memSizes[i];
495 if (mAlignment != 0) {
496 alignedSize = ceil_div(alignedSize, mAlignment) * mAlignment;
497 }
498 mMemoryOffsets[i] = (i == 0 ? 0 : mMemoryOffsets[i - 1]) + alignedSize;
499 }
500 }
501
initAstcCpuDecompression(VulkanDispatch * vk,VkPhysicalDevice physicalDevice)502 void CompressedImageInfo::initAstcCpuDecompression(VulkanDispatch* vk,
503 VkPhysicalDevice physicalDevice) {
504 mAstcTexture = std::make_unique<AstcTexture>(vk, mDevice, physicalDevice, mExtent, mBlock.width,
505 mBlock.height, &AstcCpuDecompressor::get());
506 }
507
decompressIfNeeded(VulkanDispatch * vk,VkCommandBuffer commandBuffer,VkPipelineStageFlags srcStageMask,VkPipelineStageFlags dstStageMask,const VkImageMemoryBarrier & targetBarrier,std::vector<VkImageMemoryBarrier> & outputBarriers)508 bool CompressedImageInfo::decompressIfNeeded(VulkanDispatch* vk, VkCommandBuffer commandBuffer,
509 VkPipelineStageFlags srcStageMask,
510 VkPipelineStageFlags dstStageMask,
511 const VkImageMemoryBarrier& targetBarrier,
512 std::vector<VkImageMemoryBarrier>& outputBarriers) {
513 std::vector<VkImageMemoryBarrier> imageBarriers = getImageBarriers(targetBarrier);
514
515 if (!imageWillBecomeReadable(targetBarrier)) {
516 // We're not going to read from the image, no need to decompress it.
517 // Apply the target barrier to the compressed mipmaps and the decompressed image.
518 outputBarriers.insert(outputBarriers.end(), imageBarriers.begin(), imageBarriers.end());
519 return false;
520 }
521
522 VkResult result = initializeDecompressionPipeline(vk, mDevice);
523 if (result != VK_SUCCESS) {
524 WARN("Failed to initialize pipeline for texture decompression");
525 return false;
526 }
527
528 // Transition the layout of all the compressed mipmaps so that the shader can read from them.
529 for (auto& barrier : imageBarriers) {
530 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
531 barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
532 }
533
534 // Transition the layout of the decompressed image so that we can write to it.
535 imageBarriers.back().srcAccessMask = 0;
536 imageBarriers.back().oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
537 imageBarriers.back().dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
538 imageBarriers.back().newLayout = VK_IMAGE_LAYOUT_GENERAL;
539
540 // Do the layout transitions
541 vk->vkCmdPipelineBarrier(commandBuffer, srcStageMask, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0,
542 0, nullptr, 0, nullptr, imageBarriers.size(), imageBarriers.data());
543
544 // Run the decompression shader
545 decompress(vk, commandBuffer, getImageSubresourceRange(targetBarrier.subresourceRange));
546
547 // Finally, transition the layout of all images to match the target barrier.
548 for (auto& barrier : imageBarriers) {
549 barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
550 barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
551 barrier.dstAccessMask = targetBarrier.dstAccessMask;
552 barrier.newLayout = targetBarrier.newLayout;
553 }
554 // (adjust the last barrier since it's for the decompressed image)
555 imageBarriers.back().srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
556
557 // Do the layout transitions
558 vk->vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dstStageMask, 0,
559 0, nullptr, 0, nullptr, imageBarriers.size(), imageBarriers.data());
560
561 return true;
562 }
563
decompressOnCpu(VkCommandBuffer commandBuffer,uint8_t * srcAstcData,size_t astcDataSize,VkImage dstImage,VkImageLayout dstImageLayout,uint32_t regionCount,const VkBufferImageCopy * pRegions,const VkDecoderContext & context)564 void CompressedImageInfo::decompressOnCpu(VkCommandBuffer commandBuffer, uint8_t* srcAstcData,
565 size_t astcDataSize, VkImage dstImage,
566 VkImageLayout dstImageLayout, uint32_t regionCount,
567 const VkBufferImageCopy* pRegions,
568 const VkDecoderContext& context) {
569 mAstcTexture->on_vkCmdCopyBufferToImage(commandBuffer, srcAstcData, astcDataSize, dstImage,
570 dstImageLayout, regionCount, pRegions, context);
571 }
572
getMemoryRequirements() const573 VkMemoryRequirements CompressedImageInfo::getMemoryRequirements() const {
574 return {
575 .size = mMemoryOffsets.back(),
576 .alignment = mAlignment,
577 };
578 }
579
bindCompressedMipmapsMemory(VulkanDispatch * vk,VkDeviceMemory memory,VkDeviceSize memoryOffset)580 VkResult CompressedImageInfo::bindCompressedMipmapsMemory(VulkanDispatch* vk, VkDeviceMemory memory,
581 VkDeviceSize memoryOffset) {
582 VkResult result = VK_SUCCESS;
583 for (size_t i = 0; i < mCompressedMipmaps.size(); i++) {
584 VkResult res = vk->vkBindImageMemory(mDevice, mCompressedMipmaps[i], memory,
585 memoryOffset + mMemoryOffsets[i]);
586 if (res != VK_SUCCESS) result = res;
587 }
588 return result;
589 }
590
getBufferImageCopy(const VkBufferImageCopy & origRegion) const591 VkBufferImageCopy CompressedImageInfo::getBufferImageCopy(
592 const VkBufferImageCopy& origRegion) const {
593 VkBufferImageCopy region = origRegion;
594 uint32_t mipLevel = region.imageSubresource.mipLevel;
595 region.imageSubresource.mipLevel = 0;
596 region.bufferRowLength /= mBlock.width;
597 region.bufferImageHeight /= mBlock.height;
598 region.imageOffset.x /= mBlock.width;
599 region.imageOffset.y /= mBlock.height;
600 region.imageExtent = compressedMipmapPortion(region.imageExtent, mipLevel);
601 return region;
602 }
603
604 // static
getCompressedMipmapsImageCopy(const VkImageCopy & origRegion,const CompressedImageInfo & srcImg,const CompressedImageInfo & dstImg,bool needEmulatedSrc,bool needEmulatedDst)605 VkImageCopy CompressedImageInfo::getCompressedMipmapsImageCopy(const VkImageCopy& origRegion,
606 const CompressedImageInfo& srcImg,
607 const CompressedImageInfo& dstImg,
608 bool needEmulatedSrc,
609 bool needEmulatedDst) {
610 VkImageCopy region = origRegion;
611 if (needEmulatedSrc) {
612 uint32_t mipLevel = region.srcSubresource.mipLevel;
613 region.srcSubresource.mipLevel = 0;
614 region.srcOffset.x /= srcImg.mBlock.width;
615 region.srcOffset.y /= srcImg.mBlock.height;
616 region.extent = srcImg.compressedMipmapPortion(region.extent, mipLevel);
617 }
618 if (needEmulatedDst) {
619 region.dstSubresource.mipLevel = 0;
620 region.dstOffset.x /= dstImg.mBlock.width;
621 region.dstOffset.y /= dstImg.mBlock.height;
622 }
623 return region;
624 }
625
destroy(VulkanDispatch * vk)626 void CompressedImageInfo::destroy(VulkanDispatch* vk) {
627 for (const auto& image : mCompressedMipmaps) {
628 vk->vkDestroyImage(mDevice, image, nullptr);
629 }
630 vk->vkDestroyDescriptorSetLayout(mDevice, mDecompDescriptorSetLayout, nullptr);
631 vk->vkDestroyDescriptorPool(mDevice, mDecompDescriptorPool, nullptr);
632 vk->vkDestroyShaderModule(mDevice, mDecompShader, nullptr);
633 vk->vkDestroyPipelineLayout(mDevice, mDecompPipelineLayout, nullptr);
634 vk->vkDestroyPipeline(mDevice, mDecompPipeline, nullptr);
635 for (const auto& imageView : mCompressedMipmapsImageViews) {
636 vk->vkDestroyImageView(mDevice, imageView, nullptr);
637 }
638 for (const auto& imageView : mDecompImageViews) {
639 vk->vkDestroyImageView(mDevice, imageView, nullptr);
640 }
641 vk->vkDestroyImage(mDevice, mDecompressedImage, nullptr);
642 }
643
getImageSize(VulkanDispatch * vk,VkImage image)644 VkDeviceSize CompressedImageInfo::getImageSize(VulkanDispatch* vk, VkImage image) {
645 VkMemoryRequirements memRequirements;
646 vk->vkGetImageMemoryRequirements(mDevice, image, &memRequirements);
647 mAlignment = std::max(mAlignment, memRequirements.alignment);
648 return memRequirements.size;
649 }
650
getImageBarriers(const VkImageMemoryBarrier & srcBarrier)651 std::vector<VkImageMemoryBarrier> CompressedImageInfo::getImageBarriers(
652 const VkImageMemoryBarrier& srcBarrier) {
653 const VkImageSubresourceRange range = getImageSubresourceRange(srcBarrier.subresourceRange);
654
655 std::vector<VkImageMemoryBarrier> imageBarriers;
656 imageBarriers.reserve(range.levelCount + 1);
657
658 // Add the barriers for the compressed mipmaps
659 VkImageMemoryBarrier mipmapBarrier = srcBarrier;
660 mipmapBarrier.subresourceRange.baseMipLevel = 0;
661 mipmapBarrier.subresourceRange.levelCount = 1;
662 imageBarriers.insert(imageBarriers.begin(), range.levelCount, mipmapBarrier);
663 for (uint32_t j = 0; j < range.levelCount; j++) {
664 imageBarriers[j].image = mCompressedMipmaps[range.baseMipLevel + j];
665 }
666
667 // Add a barrier for the decompressed image
668 imageBarriers.push_back(srcBarrier);
669 imageBarriers.back().image = mDecompressedImage;
670
671 return imageBarriers;
672 }
673
getImageSubresourceRange(const VkImageSubresourceRange & range) const674 VkImageSubresourceRange CompressedImageInfo::getImageSubresourceRange(
675 const VkImageSubresourceRange& range) const {
676 VkImageSubresourceRange result = range;
677 if (result.levelCount == VK_REMAINING_MIP_LEVELS) {
678 result.levelCount = mMipLevels - range.baseMipLevel;
679 }
680 if (result.layerCount == VK_REMAINING_ARRAY_LAYERS) {
681 result.layerCount = mLayerCount - range.baseArrayLayer;
682 }
683 return result;
684 }
685
initializeDecompressionPipeline(VulkanDispatch * vk,VkDevice device)686 VkResult CompressedImageInfo::initializeDecompressionPipeline(VulkanDispatch* vk, VkDevice device) {
687 if (mDecompPipeline != nullptr) {
688 return VK_SUCCESS;
689 }
690
691 const ShaderData* shader = getDecompressionShader(mCompressedFormat, mImageType);
692 if (!shader) {
693 WARN("No decompression shader found for format %s and img type %s",
694 string_VkFormat(mCompressedFormat), string_VkImageType(mImageType));
695 return VK_ERROR_FORMAT_NOT_SUPPORTED;
696 }
697
698 VkShaderModuleCreateInfo shaderInfo = {
699 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
700 .codeSize = shader->size,
701 .pCode = shader->code,
702 };
703 _RETURN_ON_FAILURE(vk->vkCreateShaderModule(device, &shaderInfo, nullptr, &mDecompShader));
704
705 VkDescriptorSetLayoutBinding dsLayoutBindings[] = {
706 {
707 .binding = 0,
708 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
709 .descriptorCount = 1,
710 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
711 },
712 {
713 .binding = 1,
714 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
715 .descriptorCount = 1,
716 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
717 },
718 };
719 VkDescriptorSetLayoutCreateInfo dsLayoutInfo = {
720 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
721 .bindingCount = 2,
722 .pBindings = dsLayoutBindings,
723 };
724 _RETURN_ON_FAILURE(vk->vkCreateDescriptorSetLayout(device, &dsLayoutInfo, nullptr,
725 &mDecompDescriptorSetLayout));
726
727 VkDescriptorPoolSize poolSize = {
728 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
729 .descriptorCount = 2 * mMipLevels,
730 };
731 VkDescriptorPoolCreateInfo dsPoolInfo = {
732 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
733 .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
734 .maxSets = mMipLevels,
735 .poolSizeCount = 1,
736 .pPoolSizes = &poolSize,
737 };
738 _RETURN_ON_FAILURE(
739 vk->vkCreateDescriptorPool(device, &dsPoolInfo, nullptr, &mDecompDescriptorPool));
740
741 std::vector<VkDescriptorSetLayout> layouts(mMipLevels, mDecompDescriptorSetLayout);
742
743 VkDescriptorSetAllocateInfo dsInfo = {
744 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
745 .descriptorPool = mDecompDescriptorPool,
746 .descriptorSetCount = mMipLevels,
747 .pSetLayouts = layouts.data(),
748 };
749 mDecompDescriptorSets.resize(mMipLevels);
750 _RETURN_ON_FAILURE(vk->vkAllocateDescriptorSets(device, &dsInfo, mDecompDescriptorSets.data()));
751
752 VkPushConstantRange pushConstant = {.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT};
753 if (isEtc2()) {
754 pushConstant.size = sizeof(Etc2PushConstant);
755 } else if (isAstc()) {
756 pushConstant.size = sizeof(AstcPushConstant);
757 }
758
759 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
760 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
761 .setLayoutCount = 1,
762 .pSetLayouts = &mDecompDescriptorSetLayout,
763 .pushConstantRangeCount = 1,
764 .pPushConstantRanges = &pushConstant,
765 };
766 _RETURN_ON_FAILURE(
767 vk->vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &mDecompPipelineLayout));
768
769 VkComputePipelineCreateInfo computePipelineInfo = {
770 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
771 .stage = {.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
772 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
773 .module = mDecompShader,
774 .pName = "main"},
775 .layout = mDecompPipelineLayout,
776 };
777 _RETURN_ON_FAILURE(vk->vkCreateComputePipelines(device, nullptr, 1, &computePipelineInfo,
778 nullptr, &mDecompPipeline));
779
780 VkFormat intermediateFormat = VK_FORMAT_R8G8B8A8_UINT;
781 switch (mCompressedFormat) {
782 case VK_FORMAT_EAC_R11_UNORM_BLOCK:
783 case VK_FORMAT_EAC_R11_SNORM_BLOCK:
784 case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
785 case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
786 intermediateFormat = mDecompressedFormat;
787 break;
788 default:
789 break;
790 }
791
792 mCompressedMipmapsImageViews.resize(mMipLevels);
793 mDecompImageViews.resize(mMipLevels);
794
795 VkDescriptorImageInfo compressedMipmapsDescriptorImageInfo = {.imageLayout =
796 VK_IMAGE_LAYOUT_GENERAL};
797 VkDescriptorImageInfo mDecompDescriptorImageInfo = {.imageLayout = VK_IMAGE_LAYOUT_GENERAL};
798 VkWriteDescriptorSet writeDescriptorSets[2] = {
799 {
800 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
801 .dstBinding = 0,
802 .descriptorCount = 1,
803 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
804 .pImageInfo = &compressedMipmapsDescriptorImageInfo,
805 },
806 {
807 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
808 .dstBinding = 1,
809 .descriptorCount = 1,
810 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
811 .pImageInfo = &mDecompDescriptorImageInfo,
812 }};
813
814 for (uint32_t i = 0; i < mMipLevels; i++) {
815 mCompressedMipmapsImageViews[i] =
816 createDefaultImageView(vk, device, mCompressedMipmaps[i], mCompressedMipmapsFormat,
817 mImageType, 0, mLayerCount);
818 mDecompImageViews[i] = createDefaultImageView(
819 vk, device, mDecompressedImage, intermediateFormat, mImageType, i, mLayerCount);
820 compressedMipmapsDescriptorImageInfo.imageView = mCompressedMipmapsImageViews[i];
821 mDecompDescriptorImageInfo.imageView = mDecompImageViews[i];
822 writeDescriptorSets[0].dstSet = mDecompDescriptorSets[i];
823 writeDescriptorSets[1].dstSet = mDecompDescriptorSets[i];
824 vk->vkUpdateDescriptorSets(device, 2, writeDescriptorSets, 0, nullptr);
825 }
826 return VK_SUCCESS;
827 }
828
decompress(VulkanDispatch * vk,VkCommandBuffer commandBuffer,const VkImageSubresourceRange & range)829 void CompressedImageInfo::decompress(VulkanDispatch* vk, VkCommandBuffer commandBuffer,
830 const VkImageSubresourceRange& range) {
831 vk->vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, mDecompPipeline);
832 uint32_t dispatchZ = mExtent.depth == 1 ? range.layerCount : mExtent.depth;
833
834 if (isEtc2()) {
835 const Etc2PushConstant pushConstant = {
836 .compFormat = (uint32_t)mCompressedFormat,
837 .baseLayer = mExtent.depth == 1 ? range.baseArrayLayer : 0};
838 vk->vkCmdPushConstants(commandBuffer, mDecompPipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0,
839 sizeof(pushConstant), &pushConstant);
840 } else if (isAstc()) {
841 uint32_t smallBlock = false;
842 switch (mCompressedFormat) {
843 case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
844 case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
845 case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
846 case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
847 case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
848 case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
849 case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
850 case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
851 smallBlock = true;
852 break;
853 default:
854 break;
855 }
856 const AstcPushConstant pushConstant = {
857 .blockSize = {mBlock.width, mBlock.height},
858 .baseLayer = mExtent.depth == 1 ? range.baseArrayLayer : 0,
859 .smallBlock = smallBlock};
860 vk->vkCmdPushConstants(commandBuffer, mDecompPipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0,
861 sizeof(pushConstant), &pushConstant);
862 }
863 for (uint32_t i = range.baseMipLevel; i < range.baseMipLevel + range.levelCount; i++) {
864 vk->vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,
865 mDecompPipelineLayout, 0, 1, mDecompDescriptorSets.data() + i,
866 0, nullptr);
867 VkExtent3D compExtent = compressedMipmapExtent(i);
868 vk->vkCmdDispatch(commandBuffer, compExtent.width, compExtent.height, dispatchZ);
869 }
870 }
871
mipmapExtent(uint32_t level) const872 VkExtent3D CompressedImageInfo::mipmapExtent(uint32_t level) const {
873 return {
874 .width = std::max<uint32_t>(mExtent.width >> level, 1),
875 .height = std::max<uint32_t>(mExtent.height >> level, 1),
876 .depth = std::max<uint32_t>(mExtent.depth >> level, 1),
877 };
878 }
879
compressedMipmapExtent(uint32_t level) const880 VkExtent3D CompressedImageInfo::compressedMipmapExtent(uint32_t level) const {
881 VkExtent3D result = mipmapExtent(level);
882 result.width = ceil_div(result.width, mBlock.width);
883 result.height = ceil_div(result.height, mBlock.height);
884 return result;
885 }
886
compressedMipmapPortion(const VkExtent3D & origExtent,uint32_t level) const887 VkExtent3D CompressedImageInfo::compressedMipmapPortion(const VkExtent3D& origExtent,
888 uint32_t level) const {
889 VkExtent3D maxExtent = compressedMipmapExtent(level);
890 return {
891 .width = std::min(ceil_div(origExtent.width, mBlock.width), maxExtent.width),
892 .height = std::min(ceil_div(origExtent.height, mBlock.height), maxExtent.height),
893 .depth = origExtent.depth,
894 };
895 }
896
897 } // namespace vk
898 } // namespace gfxstream
899