PeriDyno 1.0.0
Loading...
Searching...
No Matches
VulkanTexture.cpp
Go to the documentation of this file.
1/*
2* Vulkan texture loader
3*
4* Copyright(C) by Sascha Willems - www.saschawillems.de
5*
6* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT)
7*/
8
9#include "VulkanTexture.h"
10#include "VkSystem.h"
11
12namespace vks
13{
14
16 {
18
19 if (ctx == VK_NULL_HANDLE) {
20 vks::tools::exitFatal("Vulkan libraray should be initialized first before Texture is created! \n", 0);
21 }
22 }
23
25 {
26 descriptor.sampler = sampler;
27 descriptor.imageView = view;
28 descriptor.imageLayout = imageLayout;
29 }
30
32 {
33 vkDestroyImageView(ctx->deviceHandle(), view, nullptr);
34 vkDestroyImage(ctx->deviceHandle(), image, nullptr);
35 if (sampler)
36 {
37 vkDestroySampler(ctx->deviceHandle(), sampler, nullptr);
38 }
39 vkFreeMemory(ctx->deviceHandle(), deviceMemory, nullptr);
40 }
41
42 ktxResult Texture::loadKTXFile(std::string filename, ktxTexture **target)
43 {
44 ktxResult result = KTX_SUCCESS;
45#if defined(__ANDROID__)
46 AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING);
47 if (!asset) {
48 vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1);
49 }
50 size_t size = AAsset_getLength(asset);
51 assert(size > 0);
52 ktx_uint8_t *textureData = new ktx_uint8_t[size];
53 AAsset_read(asset, textureData, size);
54 AAsset_close(asset);
55 result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target);
56 delete[] textureData;
57#else
58 if (!vks::tools::fileExists(filename)) {
59 vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1);
60 }
61 result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, target);
62#endif
63 return result;
64 }
65
78 void Texture2D::loadFromFile(std::string filename, VkFormat format, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, bool forceLinear)
79 {
80 ktxTexture* ktxTexture;
81 ktxResult result = loadKTXFile(filename, &ktxTexture);
82 assert(result == KTX_SUCCESS);
83
84 width = ktxTexture->baseWidth;
85 height = ktxTexture->baseHeight;
86 mipLevels = ktxTexture->numLevels;
87
88 ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture);
89 ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
90
91 // Get device properties for the requested texture format
92 VkFormatProperties formatProperties;
93 vkGetPhysicalDeviceFormatProperties(ctx->physicalDeviceHandle(), format, &formatProperties);
94
95 // Only use linear tiling if requested (and supported by the device)
96 // Support for linear tiling is mostly limited, so prefer to use
97 // optimal tiling instead
98 // On most implementations linear tiling will only support a very
99 // limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
100 VkBool32 useStaging = !forceLinear;
101
102 VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
103 VkMemoryRequirements memReqs;
104
105 // Use a separate command buffer for texture loading
106 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
107
108 if (useStaging)
109 {
110 // Create a host-visible staging buffer that contains the raw image data
111 VkBuffer stagingBuffer;
112 VkDeviceMemory stagingMemory;
113
114 VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
115 bufferCreateInfo.size = ktxTextureSize;
116 // This buffer is used as a transfer source for the buffer copy
117 bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
118 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
119
120 VK_CHECK_RESULT(vkCreateBuffer(ctx->deviceHandle(), &bufferCreateInfo, nullptr, &stagingBuffer));
121
122 // Get memory requirements for the staging buffer (alignment, memory type bits)
123 vkGetBufferMemoryRequirements(ctx->deviceHandle(), stagingBuffer, &memReqs);
124
125 memAllocInfo.allocationSize = memReqs.size;
126 // Get memory type index for a host visible buffer
127 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
128
129 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &stagingMemory));
130 VK_CHECK_RESULT(vkBindBufferMemory(ctx->deviceHandle(), stagingBuffer, stagingMemory, 0));
131
132 // Copy texture data into staging buffer
133 uint8_t *data;
134 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), stagingMemory, 0, memReqs.size, 0, (void **)&data));
135 memcpy(data, ktxTextureData, ktxTextureSize);
136 vkUnmapMemory(ctx->deviceHandle(), stagingMemory);
137
138 // Setup buffer copy regions for each mip level
139 std::vector<VkBufferImageCopy> bufferCopyRegions;
140
141 for (uint32_t i = 0; i < mipLevels; i++)
142 {
143 ktx_size_t offset;
144 KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset);
145 assert(result == KTX_SUCCESS);
146
147 VkBufferImageCopy bufferCopyRegion = {};
148 bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
149 bufferCopyRegion.imageSubresource.mipLevel = i;
150 bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
151 bufferCopyRegion.imageSubresource.layerCount = 1;
152 bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i);
153 bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i);
154 bufferCopyRegion.imageExtent.depth = 1;
155 bufferCopyRegion.bufferOffset = offset;
156
157 bufferCopyRegions.push_back(bufferCopyRegion);
158 }
159
160 // Create optimal tiled target image
161 VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
162 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
163 imageCreateInfo.format = format;
164 imageCreateInfo.mipLevels = mipLevels;
165 imageCreateInfo.arrayLayers = 1;
166 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
167 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
168 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
169 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
170 imageCreateInfo.extent = { width, height, 1 };
171 imageCreateInfo.usage = imageUsageFlags;
172 // Ensure that the TRANSFER_DST bit is set for staging
173 if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
174 {
175 imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
176 }
177 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &image));
178
179 vkGetImageMemoryRequirements(ctx->deviceHandle(), image, &memReqs);
180
181 memAllocInfo.allocationSize = memReqs.size;
182
183 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
184 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &deviceMemory));
185 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), image, deviceMemory, 0));
186
187 VkImageSubresourceRange subresourceRange = {};
188 subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
189 subresourceRange.baseMipLevel = 0;
190 subresourceRange.levelCount = mipLevels;
191 subresourceRange.layerCount = 1;
192
193 // Image barrier for optimal image (target)
194 // Optimal image will be used as destination for the copy
196 copyCmd,
197 image,
198 VK_IMAGE_LAYOUT_UNDEFINED,
199 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
200 subresourceRange);
201
202 // Copy mip levels from staging buffer
203 vkCmdCopyBufferToImage(
204 copyCmd,
205 stagingBuffer,
206 image,
207 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
208 static_cast<uint32_t>(bufferCopyRegions.size()),
209 bufferCopyRegions.data()
210 );
211
212 // Change texture image layout to shader read after all mip levels have been copied
213 this->imageLayout = imageLayout;
215 copyCmd,
216 image,
217 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
219 subresourceRange);
220
221 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle());
222
223 // Clean up staging resources
224 vkFreeMemory(ctx->deviceHandle(), stagingMemory, nullptr);
225 vkDestroyBuffer(ctx->deviceHandle(), stagingBuffer, nullptr);
226 }
227 else
228 {
229 // Prefer using optimal tiling, as linear tiling
230 // may support only a small set of features
231 // depending on implementation (e.g. no mip maps, only one layer, etc.)
232
233 // Check if this support is supported for linear tiling
234 assert(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
235
236 VkImage mappableImage;
237 VkDeviceMemory mappableMemory;
238
239 VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
240 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
241 imageCreateInfo.format = format;
242 imageCreateInfo.extent = { width, height, 1 };
243 imageCreateInfo.mipLevels = 1;
244 imageCreateInfo.arrayLayers = 1;
245 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
246 imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
247 imageCreateInfo.usage = imageUsageFlags;
248 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
249 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
250
251 // Load mip map level 0 to linear tiling image
252 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &mappableImage));
253
254 // Get memory requirements for this image
255 // like size and alignment
256 vkGetImageMemoryRequirements(ctx->deviceHandle(), mappableImage, &memReqs);
257 // Set memory allocation size to required memory size
258 memAllocInfo.allocationSize = memReqs.size;
259
260 // Get memory type that can be mapped to host memory
261 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
262
263 // Allocate host memory
264 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &mappableMemory));
265
266 // Bind allocated image for use
267 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), mappableImage, mappableMemory, 0));
268
269 // Get sub resource layout
270 // Mip map count, array layer, etc.
271 VkImageSubresource subRes = {};
272 subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
273 subRes.mipLevel = 0;
274
275 VkSubresourceLayout subResLayout;
276 void *data;
277
278 // Get sub resources layout
279 // Includes row pitch, size offsets, etc.
280 vkGetImageSubresourceLayout(ctx->deviceHandle(), mappableImage, &subRes, &subResLayout);
281
282 // Map image memory
283 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), mappableMemory, 0, memReqs.size, 0, &data));
284
285 // Copy image data into memory
286 memcpy(data, ktxTextureData, memReqs.size);
287
288 vkUnmapMemory(ctx->deviceHandle(), mappableMemory);
289
290 // Linear tiled images don't need to be staged
291 // and can be directly used as textures
292 image = mappableImage;
293 deviceMemory = mappableMemory;
294 this->imageLayout = imageLayout;
295
296 // Setup image memory barrier
297 vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout);
298
299 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle());
300 }
301
302 ktxTexture_Destroy(ktxTexture);
303
304 // Create a default sampler
305 VkSamplerCreateInfo samplerCreateInfo = {};
306 samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
307 samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
308 samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
309 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
310 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
311 samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
312 samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
313 samplerCreateInfo.mipLodBias = 0.0f;
314 samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
315 samplerCreateInfo.minLod = 0.0f;
316 // Max level-of-detail should match mip level count
317 samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f;
318 // Only enable anisotropic filtering if enabled on the device
319 samplerCreateInfo.maxAnisotropy = ctx->enabledFeatures.samplerAnisotropy ? ctx->properties.limits.maxSamplerAnisotropy : 1.0f;
320 samplerCreateInfo.anisotropyEnable = ctx->enabledFeatures.samplerAnisotropy;
321 samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
322 VK_CHECK_RESULT(vkCreateSampler(ctx->deviceHandle(), &samplerCreateInfo, nullptr, &sampler));
323
324 // Create image view
325 // Textures are not directly accessed by the shaders and
326 // are abstracted by image views containing additional
327 // information and sub resource ranges
328 VkImageViewCreateInfo viewCreateInfo = {};
329 viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
330 viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
331 viewCreateInfo.format = format;
332 viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
333 viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
334 // Linear tiling usually won't support mip maps
335 // Only set mip map count if optimal tiling is used
336 viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1;
337 viewCreateInfo.image = image;
338 VK_CHECK_RESULT(vkCreateImageView(ctx->deviceHandle(), &viewCreateInfo, nullptr, &view));
339
340 // Update descriptor image info member that can be used for setting up descriptor sets
342 }
343
358 void Texture2D::fromBuffer(void* buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight, VkFilter filter, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout)
359 {
360 assert(buffer);
361
362 width = texWidth;
363 height = texHeight;
364 mipLevels = 1;
365
366 VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
367 VkMemoryRequirements memReqs;
368
369 // Use a separate command buffer for texture loading
370 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
371
372 // Create a host-visible staging buffer that contains the raw image data
373 VkBuffer stagingBuffer;
374 VkDeviceMemory stagingMemory;
375
376 VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
377 bufferCreateInfo.size = bufferSize;
378 // This buffer is used as a transfer source for the buffer copy
379 bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
380 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
381
382 VK_CHECK_RESULT(vkCreateBuffer(ctx->deviceHandle(), &bufferCreateInfo, nullptr, &stagingBuffer));
383
384 // Get memory requirements for the staging buffer (alignment, memory type bits)
385 vkGetBufferMemoryRequirements(ctx->deviceHandle(), stagingBuffer, &memReqs);
386
387 memAllocInfo.allocationSize = memReqs.size;
388 // Get memory type index for a host visible buffer
389 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
390
391 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &stagingMemory));
392 VK_CHECK_RESULT(vkBindBufferMemory(ctx->deviceHandle(), stagingBuffer, stagingMemory, 0));
393
394 // Copy texture data into staging buffer
395 uint8_t *data;
396 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), stagingMemory, 0, memReqs.size, 0, (void **)&data));
397 memcpy(data, buffer, bufferSize);
398 vkUnmapMemory(ctx->deviceHandle(), stagingMemory);
399
400 VkBufferImageCopy bufferCopyRegion = {};
401 bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
402 bufferCopyRegion.imageSubresource.mipLevel = 0;
403 bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
404 bufferCopyRegion.imageSubresource.layerCount = 1;
405 bufferCopyRegion.imageExtent.width = width;
406 bufferCopyRegion.imageExtent.height = height;
407 bufferCopyRegion.imageExtent.depth = 1;
408 bufferCopyRegion.bufferOffset = 0;
409
410 // Create optimal tiled target image
411 VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
412 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
413 imageCreateInfo.format = format;
414 imageCreateInfo.mipLevels = mipLevels;
415 imageCreateInfo.arrayLayers = 1;
416 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
417 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
418 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
419 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
420 imageCreateInfo.extent = { width, height, 1 };
421 imageCreateInfo.usage = imageUsageFlags;
422 // Ensure that the TRANSFER_DST bit is set for staging
423 if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
424 {
425 imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
426 }
427 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &image));
428
429 vkGetImageMemoryRequirements(ctx->deviceHandle(), image, &memReqs);
430
431 memAllocInfo.allocationSize = memReqs.size;
432
433 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
434 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &deviceMemory));
435 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), image, deviceMemory, 0));
436
437 VkImageSubresourceRange subresourceRange = {};
438 subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
439 subresourceRange.baseMipLevel = 0;
440 subresourceRange.levelCount = mipLevels;
441 subresourceRange.layerCount = 1;
442
443 // Image barrier for optimal image (target)
444 // Optimal image will be used as destination for the copy
446 copyCmd,
447 image,
448 VK_IMAGE_LAYOUT_UNDEFINED,
449 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
450 subresourceRange);
451
452 // Copy mip levels from staging buffer
453 vkCmdCopyBufferToImage(
454 copyCmd,
455 stagingBuffer,
456 image,
457 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
458 1,
459 &bufferCopyRegion
460 );
461
462 // Change texture image layout to shader read after all mip levels have been copied
463 this->imageLayout = imageLayout;
465 copyCmd,
466 image,
467 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
469 subresourceRange);
470
471 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle());
472
473 // Clean up staging resources
474 vkFreeMemory(ctx->deviceHandle(), stagingMemory, nullptr);
475 vkDestroyBuffer(ctx->deviceHandle(), stagingBuffer, nullptr);
476
477 // Create sampler
478 VkSamplerCreateInfo samplerCreateInfo = {};
479 samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
480 samplerCreateInfo.magFilter = filter;
481 samplerCreateInfo.minFilter = filter;
482 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
483 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
484 samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
485 samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
486 samplerCreateInfo.mipLodBias = 0.0f;
487 samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
488 samplerCreateInfo.minLod = 0.0f;
489 samplerCreateInfo.maxLod = 0.0f;
490 samplerCreateInfo.maxAnisotropy = 1.0f;
491 VK_CHECK_RESULT(vkCreateSampler(ctx->deviceHandle(), &samplerCreateInfo, nullptr, &sampler));
492
493 // Create image view
494 VkImageViewCreateInfo viewCreateInfo = {};
495 viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
496 viewCreateInfo.pNext = NULL;
497 viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
498 viewCreateInfo.format = format;
499 viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
500 viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
501 viewCreateInfo.subresourceRange.levelCount = 1;
502 viewCreateInfo.image = image;
503 VK_CHECK_RESULT(vkCreateImageView(ctx->deviceHandle(), &viewCreateInfo, nullptr, &view));
504
505 // Update descriptor image info member that can be used for setting up descriptor sets
507 }
508
520 void Texture2DArray::loadFromFile(std::string filename, VkFormat format, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout)
521 {
522 ktxTexture* ktxTexture;
523 ktxResult result = loadKTXFile(filename, &ktxTexture);
524 assert(result == KTX_SUCCESS);
525
526 width = ktxTexture->baseWidth;
527 height = ktxTexture->baseHeight;
528 layerCount = ktxTexture->numLayers;
529 mipLevels = ktxTexture->numLevels;
530
531 ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture);
532 ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
533
534 VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
535 VkMemoryRequirements memReqs;
536
537 // Create a host-visible staging buffer that contains the raw image data
538 VkBuffer stagingBuffer;
539 VkDeviceMemory stagingMemory;
540
541 VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
542 bufferCreateInfo.size = ktxTextureSize;
543 // This buffer is used as a transfer source for the buffer copy
544 bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
545 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
546
547 VK_CHECK_RESULT(vkCreateBuffer(ctx->deviceHandle(), &bufferCreateInfo, nullptr, &stagingBuffer));
548
549 // Get memory requirements for the staging buffer (alignment, memory type bits)
550 vkGetBufferMemoryRequirements(ctx->deviceHandle(), stagingBuffer, &memReqs);
551
552 memAllocInfo.allocationSize = memReqs.size;
553 // Get memory type index for a host visible buffer
554 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
555
556 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &stagingMemory));
557 VK_CHECK_RESULT(vkBindBufferMemory(ctx->deviceHandle(), stagingBuffer, stagingMemory, 0));
558
559 // Copy texture data into staging buffer
560 uint8_t *data;
561 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), stagingMemory, 0, memReqs.size, 0, (void **)&data));
562 memcpy(data, ktxTextureData, ktxTextureSize);
563 vkUnmapMemory(ctx->deviceHandle(), stagingMemory);
564
565 // Setup buffer copy regions for each layer including all of its miplevels
566 std::vector<VkBufferImageCopy> bufferCopyRegions;
567
568 for (uint32_t layer = 0; layer < layerCount; layer++)
569 {
570 for (uint32_t level = 0; level < mipLevels; level++)
571 {
572 ktx_size_t offset;
573 KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, layer, 0, &offset);
574 assert(result == KTX_SUCCESS);
575
576 VkBufferImageCopy bufferCopyRegion = {};
577 bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
578 bufferCopyRegion.imageSubresource.mipLevel = level;
579 bufferCopyRegion.imageSubresource.baseArrayLayer = layer;
580 bufferCopyRegion.imageSubresource.layerCount = 1;
581 bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level;
582 bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level;
583 bufferCopyRegion.imageExtent.depth = 1;
584 bufferCopyRegion.bufferOffset = offset;
585
586 bufferCopyRegions.push_back(bufferCopyRegion);
587 }
588 }
589
590 // Create optimal tiled target image
591 VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
592 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
593 imageCreateInfo.format = format;
594 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
595 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
596 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
597 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
598 imageCreateInfo.extent = { width, height, 1 };
599 imageCreateInfo.usage = imageUsageFlags;
600 // Ensure that the TRANSFER_DST bit is set for staging
601 if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
602 {
603 imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
604 }
605 imageCreateInfo.arrayLayers = layerCount;
606 imageCreateInfo.mipLevels = mipLevels;
607
608 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &image));
609
610 vkGetImageMemoryRequirements(ctx->deviceHandle(), image, &memReqs);
611
612 memAllocInfo.allocationSize = memReqs.size;
613 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
614
615 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &deviceMemory));
616 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), image, deviceMemory, 0));
617
618 // Use a separate command buffer for texture loading
619 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
620
621 // Image barrier for optimal image (target)
622 // Set initial layout for all array layers (faces) of the optimal (target) tiled texture
623 VkImageSubresourceRange subresourceRange = {};
624 subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
625 subresourceRange.baseMipLevel = 0;
626 subresourceRange.levelCount = mipLevels;
627 subresourceRange.layerCount = layerCount;
628
630 copyCmd,
631 image,
632 VK_IMAGE_LAYOUT_UNDEFINED,
633 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
634 subresourceRange);
635
636 // Copy the layers and mip levels from the staging buffer to the optimal tiled image
637 vkCmdCopyBufferToImage(
638 copyCmd,
639 stagingBuffer,
640 image,
641 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
642 static_cast<uint32_t>(bufferCopyRegions.size()),
643 bufferCopyRegions.data());
644
645 // Change texture image layout to shader read after all faces have been copied
646 this->imageLayout = imageLayout;
648 copyCmd,
649 image,
650 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
652 subresourceRange);
653
654 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle());
655
656 // Create sampler
657 VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo();
658 samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
659 samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
660 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
661 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
662 samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU;
663 samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU;
664 samplerCreateInfo.mipLodBias = 0.0f;
665 samplerCreateInfo.maxAnisotropy = ctx->enabledFeatures.samplerAnisotropy ? ctx->properties.limits.maxSamplerAnisotropy : 1.0f;
666 samplerCreateInfo.anisotropyEnable = ctx->enabledFeatures.samplerAnisotropy;
667 samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
668 samplerCreateInfo.minLod = 0.0f;
669 samplerCreateInfo.maxLod = (float)mipLevels;
670 samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
671 VK_CHECK_RESULT(vkCreateSampler(ctx->deviceHandle(), &samplerCreateInfo, nullptr, &sampler));
672
673 // Create image view
674 VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo();
675 viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
676 viewCreateInfo.format = format;
677 viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
678 viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
679 viewCreateInfo.subresourceRange.layerCount = layerCount;
680 viewCreateInfo.subresourceRange.levelCount = mipLevels;
681 viewCreateInfo.image = image;
682 VK_CHECK_RESULT(vkCreateImageView(ctx->deviceHandle(), &viewCreateInfo, nullptr, &view));
683
684 // Clean up staging resources
685 ktxTexture_Destroy(ktxTexture);
686 vkFreeMemory(ctx->deviceHandle(), stagingMemory, nullptr);
687 vkDestroyBuffer(ctx->deviceHandle(), stagingBuffer, nullptr);
688
689 // Update descriptor image info member that can be used for setting up descriptor sets
691 }
692
704 void TextureCubeMap::loadFromFile(std::string filename, VkFormat format, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout)
705 {
706 ktxTexture* ktxTexture;
707 ktxResult result = loadKTXFile(filename, &ktxTexture);
708 assert(result == KTX_SUCCESS);
709
710 width = ktxTexture->baseWidth;
711 height = ktxTexture->baseHeight;
712 mipLevels = ktxTexture->numLevels;
713
714 ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture);
715 ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
716
717 VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
718 VkMemoryRequirements memReqs;
719
720 // Create a host-visible staging buffer that contains the raw image data
721 VkBuffer stagingBuffer;
722 VkDeviceMemory stagingMemory;
723
724 VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
725 bufferCreateInfo.size = ktxTextureSize;
726 // This buffer is used as a transfer source for the buffer copy
727 bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
728 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
729
730 VK_CHECK_RESULT(vkCreateBuffer(ctx->deviceHandle(), &bufferCreateInfo, nullptr, &stagingBuffer));
731
732 // Get memory requirements for the staging buffer (alignment, memory type bits)
733 vkGetBufferMemoryRequirements(ctx->deviceHandle(), stagingBuffer, &memReqs);
734
735 memAllocInfo.allocationSize = memReqs.size;
736 // Get memory type index for a host visible buffer
737 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
738
739 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &stagingMemory));
740 VK_CHECK_RESULT(vkBindBufferMemory(ctx->deviceHandle(), stagingBuffer, stagingMemory, 0));
741
742 // Copy texture data into staging buffer
743 uint8_t *data;
744 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), stagingMemory, 0, memReqs.size, 0, (void **)&data));
745 memcpy(data, ktxTextureData, ktxTextureSize);
746 vkUnmapMemory(ctx->deviceHandle(), stagingMemory);
747
748 // Setup buffer copy regions for each face including all of its mip levels
749 std::vector<VkBufferImageCopy> bufferCopyRegions;
750
751 for (uint32_t face = 0; face < 6; face++)
752 {
753 for (uint32_t level = 0; level < mipLevels; level++)
754 {
755 ktx_size_t offset;
756 KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, level, 0, face, &offset);
757 assert(result == KTX_SUCCESS);
758
759 VkBufferImageCopy bufferCopyRegion = {};
760 bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
761 bufferCopyRegion.imageSubresource.mipLevel = level;
762 bufferCopyRegion.imageSubresource.baseArrayLayer = face;
763 bufferCopyRegion.imageSubresource.layerCount = 1;
764 bufferCopyRegion.imageExtent.width = ktxTexture->baseWidth >> level;
765 bufferCopyRegion.imageExtent.height = ktxTexture->baseHeight >> level;
766 bufferCopyRegion.imageExtent.depth = 1;
767 bufferCopyRegion.bufferOffset = offset;
768
769 bufferCopyRegions.push_back(bufferCopyRegion);
770 }
771 }
772
773 // Create optimal tiled target image
774 VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
775 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
776 imageCreateInfo.format = format;
777 imageCreateInfo.mipLevels = mipLevels;
778 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
779 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
780 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
781 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
782 imageCreateInfo.extent = { width, height, 1 };
783 imageCreateInfo.usage = imageUsageFlags;
784 // Ensure that the TRANSFER_DST bit is set for staging
785 if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
786 {
787 imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
788 }
789 // Cube faces count as array layers in Vulkan
790 imageCreateInfo.arrayLayers = 6;
791 // This flag is required for cube map images
792 imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
793
794
795 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &image));
796
797 vkGetImageMemoryRequirements(ctx->deviceHandle(), image, &memReqs);
798
799 memAllocInfo.allocationSize = memReqs.size;
800 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
801
802 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &deviceMemory));
803 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), image, deviceMemory, 0));
804
805 // Use a separate command buffer for texture loading
806 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
807
808 // Image barrier for optimal image (target)
809 // Set initial layout for all array layers (faces) of the optimal (target) tiled texture
810 VkImageSubresourceRange subresourceRange = {};
811 subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
812 subresourceRange.baseMipLevel = 0;
813 subresourceRange.levelCount = mipLevels;
814 subresourceRange.layerCount = 6;
815
817 copyCmd,
818 image,
819 VK_IMAGE_LAYOUT_UNDEFINED,
820 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
821 subresourceRange);
822
823 // Copy the cube map faces from the staging buffer to the optimal tiled image
824 vkCmdCopyBufferToImage(
825 copyCmd,
826 stagingBuffer,
827 image,
828 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
829 static_cast<uint32_t>(bufferCopyRegions.size()),
830 bufferCopyRegions.data());
831
832 // Change texture image layout to shader read after all faces have been copied
833 this->imageLayout = imageLayout;
835 copyCmd,
836 image,
837 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
839 subresourceRange);
840
841 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle());
842
843 // Create sampler
844 VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo();
845 samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
846 samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
847 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
848 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
849 samplerCreateInfo.addressModeV = samplerCreateInfo.addressModeU;
850 samplerCreateInfo.addressModeW = samplerCreateInfo.addressModeU;
851 samplerCreateInfo.mipLodBias = 0.0f;
852 samplerCreateInfo.maxAnisotropy = ctx->enabledFeatures.samplerAnisotropy ? ctx->properties.limits.maxSamplerAnisotropy : 1.0f;
853 samplerCreateInfo.anisotropyEnable = ctx->enabledFeatures.samplerAnisotropy;
854 samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
855 samplerCreateInfo.minLod = 0.0f;
856 samplerCreateInfo.maxLod = (float)mipLevels;
857 samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
858 VK_CHECK_RESULT(vkCreateSampler(ctx->deviceHandle(), &samplerCreateInfo, nullptr, &sampler));
859
860 // Create image view
861 VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo();
862 viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
863 viewCreateInfo.format = format;
864 viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
865 viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
866 viewCreateInfo.subresourceRange.layerCount = 6;
867 viewCreateInfo.subresourceRange.levelCount = mipLevels;
868 viewCreateInfo.image = image;
869 VK_CHECK_RESULT(vkCreateImageView(ctx->deviceHandle(), &viewCreateInfo, nullptr, &view));
870
871 // Clean up staging resources
872 ktxTexture_Destroy(ktxTexture);
873 vkFreeMemory(ctx->deviceHandle(), stagingMemory, nullptr);
874 vkDestroyBuffer(ctx->deviceHandle(), stagingBuffer, nullptr);
875
876 // Update descriptor image info member that can be used for setting up descriptor sets
878 }
879
880}
assert(queueCount >=1)
#define VK_CHECK_RESULT(f)
Definition VulkanTools.h:55
VkContext * currentContext()
Definition VkSystem.h:21
static VkSystem * instance()
Definition VkSystem.cpp:10
void loadFromFile(std::string filename, VkFormat format, VkImageUsageFlags imageUsageFlags=VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout=VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
void loadFromFile(std::string filename, VkFormat format, VkImageUsageFlags imageUsageFlags=VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout=VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, bool forceLinear=false)
void fromBuffer(void *buffer, VkDeviceSize bufferSize, VkFormat format, uint32_t texWidth, uint32_t texHeight, VkFilter filter=VK_FILTER_LINEAR, VkImageUsageFlags imageUsageFlags=VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout=VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
void loadFromFile(std::string filename, VkFormat format, VkImageUsageFlags imageUsageFlags=VK_IMAGE_USAGE_SAMPLED_BIT, VkImageLayout imageLayout=VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
VkDescriptorImageInfo descriptor
ktxResult loadKTXFile(std::string filename, ktxTexture **target)
uint32_t mipLevels
uint32_t height
dyno::VkContext * ctx
uint32_t width
VkImageLayout imageLayout
uint32_t layerCount
void updateDescriptor()
VkImageView view
VkSampler sampler
VkDeviceMemory deviceMemory
VkMemoryAllocateInfo memoryAllocateInfo()
VkImageCreateInfo imageCreateInfo()
VkSamplerCreateInfo samplerCreateInfo()
VkImageViewCreateInfo imageViewCreateInfo()
VkBufferCreateInfo bufferCreateInfo()
bool fileExists(const std::string &filename)
Checks if a file exists.
void setImageLayout(VkCommandBuffer cmdbuffer, VkImage image, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkImageSubresourceRange subresourceRange, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask)
void exitFatal(const std::string &message, int32_t exitCode)