1
1
/*
2
- * Vulkan texture loader
2
+ * Vulkan texture loader for KTX files
3
3
*
4
- * Copyright(C) by Sascha Willems - www.saschawillems.de
4
+ * Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
5
5
*
6
6
* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT)
7
7
*/
@@ -61,10 +61,9 @@ namespace vks
61
61
* @param copyQueue Queue used for the texture staging copy commands (must support transfer)
62
62
* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
63
63
* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
64
- * @param (Optional) forceLinear Force linear tiling (not advised, defaults to false)
65
64
*
66
65
*/
67
- void Texture2D::loadFromFile (std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout, bool forceLinear )
66
+ void Texture2D::loadFromFile (std::string filename, VkFormat format, vks::VulkanDevice *device, VkQueue copyQueue, VkImageUsageFlags imageUsageFlags, VkImageLayout imageLayout)
68
67
{
69
68
ktxTexture* ktxTexture;
70
69
ktxResult result = loadKTXFile (filename, &ktxTexture);
@@ -82,212 +81,128 @@ namespace vks
82
81
VkFormatProperties formatProperties;
83
82
vkGetPhysicalDeviceFormatProperties (device->physicalDevice , format, &formatProperties);
84
83
85
- // Only use linear tiling if requested (and supported by the device)
86
- // Support for linear tiling is mostly limited, so prefer to use
87
- // optimal tiling instead
88
- // On most implementations linear tiling will only support a very
89
- // limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
90
- VkBool32 useStaging = !forceLinear;
91
-
92
84
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo ();
93
85
VkMemoryRequirements memReqs;
94
86
95
87
// Use a separate command buffer for texture loading
96
88
VkCommandBuffer copyCmd = device->createCommandBuffer (VK_COMMAND_BUFFER_LEVEL_PRIMARY, true );
97
89
98
- if (useStaging)
99
- {
100
- // Create a host-visible staging buffer that contains the raw image data
101
- VkBuffer stagingBuffer;
102
- VkDeviceMemory stagingMemory;
103
-
104
- VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo ();
105
- bufferCreateInfo.size = ktxTextureSize;
106
- // This buffer is used as a transfer source for the buffer copy
107
- bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
108
- bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
109
-
110
- VK_CHECK_RESULT (vkCreateBuffer (device->logicalDevice , &bufferCreateInfo, nullptr , &stagingBuffer));
111
-
112
- // Get memory requirements for the staging buffer (alignment, memory type bits)
113
- vkGetBufferMemoryRequirements (device->logicalDevice , stagingBuffer, &memReqs);
90
+ // Create a host-visible staging buffer that contains the raw image data
91
+ VkBuffer stagingBuffer;
92
+ VkDeviceMemory stagingMemory;
114
93
115
- memAllocInfo.allocationSize = memReqs.size ;
116
- // Get memory type index for a host visible buffer
117
- memAllocInfo.memoryTypeIndex = device->getMemoryType (memReqs.memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
94
+ VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo ();
95
+ bufferCreateInfo.size = ktxTextureSize;
96
+ // This buffer is used as a transfer source for the buffer copy
97
+ bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
98
+ bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
118
99
119
- VK_CHECK_RESULT (vkAllocateMemory (device->logicalDevice , &memAllocInfo, nullptr , &stagingMemory));
120
- VK_CHECK_RESULT (vkBindBufferMemory (device->logicalDevice , stagingBuffer, stagingMemory, 0 ));
100
+ VK_CHECK_RESULT (vkCreateBuffer (device->logicalDevice , &bufferCreateInfo, nullptr , &stagingBuffer));
121
101
122
- // Copy texture data into staging buffer
123
- uint8_t *data;
124
- VK_CHECK_RESULT (vkMapMemory (device->logicalDevice , stagingMemory, 0 , memReqs.size , 0 , (void **)&data));
125
- memcpy (data, ktxTextureData, ktxTextureSize);
126
- vkUnmapMemory (device->logicalDevice , stagingMemory);
102
+ // Get memory requirements for the staging buffer (alignment, memory type bits)
103
+ vkGetBufferMemoryRequirements (device->logicalDevice , stagingBuffer, &memReqs);
127
104
128
- // Setup buffer copy regions for each mip level
129
- std::vector<VkBufferImageCopy> bufferCopyRegions;
105
+ memAllocInfo.allocationSize = memReqs.size ;
106
+ // Get memory type index for a host visible buffer
107
+ memAllocInfo.memoryTypeIndex = device->getMemoryType (memReqs.memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
130
108
131
- for (uint32_t i = 0 ; i < mipLevels; i++)
132
- {
133
- ktx_size_t offset;
134
- KTX_error_code result = ktxTexture_GetImageOffset (ktxTexture, i, 0 , 0 , &offset);
135
- assert (result == KTX_SUCCESS);
109
+ VK_CHECK_RESULT (vkAllocateMemory (device->logicalDevice , &memAllocInfo, nullptr , &stagingMemory));
110
+ VK_CHECK_RESULT (vkBindBufferMemory (device->logicalDevice , stagingBuffer, stagingMemory, 0 ));
136
111
137
- VkBufferImageCopy bufferCopyRegion = {};
138
- bufferCopyRegion.imageSubresource .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
139
- bufferCopyRegion.imageSubresource .mipLevel = i;
140
- bufferCopyRegion.imageSubresource .baseArrayLayer = 0 ;
141
- bufferCopyRegion.imageSubresource .layerCount = 1 ;
142
- bufferCopyRegion.imageExtent .width = std::max (1u , ktxTexture->baseWidth >> i);
143
- bufferCopyRegion.imageExtent .height = std::max (1u , ktxTexture->baseHeight >> i);
144
- bufferCopyRegion.imageExtent .depth = 1 ;
145
- bufferCopyRegion.bufferOffset = offset;
112
+ // Copy texture data into staging buffer
113
+ uint8_t * data{ nullptr };
114
+ VK_CHECK_RESULT (vkMapMemory (device->logicalDevice , stagingMemory, 0 , memReqs.size , 0 , (void **)&data));
115
+ memcpy (data, ktxTextureData, ktxTextureSize);
116
+ vkUnmapMemory (device->logicalDevice , stagingMemory);
146
117
147
- bufferCopyRegions. push_back (bufferCopyRegion);
148
- }
118
+ // Setup buffer copy regions for each mip level
119
+ std::vector<VkBufferImageCopy> bufferCopyRegions;
149
120
150
- // Create optimal tiled target image
151
- VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo ();
152
- imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
153
- imageCreateInfo.format = format;
154
- imageCreateInfo.mipLevels = mipLevels;
155
- imageCreateInfo.arrayLayers = 1 ;
156
- imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
157
- imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
158
- imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
159
- imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
160
- imageCreateInfo.extent = { width, height, 1 };
161
- imageCreateInfo.usage = imageUsageFlags;
162
- // Ensure that the TRANSFER_DST bit is set for staging
163
- if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
164
- {
165
- imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
166
- }
167
- VK_CHECK_RESULT (vkCreateImage (device->logicalDevice , &imageCreateInfo, nullptr , &image));
168
-
169
- vkGetImageMemoryRequirements (device->logicalDevice , image, &memReqs);
170
-
171
- memAllocInfo.allocationSize = memReqs.size ;
172
-
173
- memAllocInfo.memoryTypeIndex = device->getMemoryType (memReqs.memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
174
- VK_CHECK_RESULT (vkAllocateMemory (device->logicalDevice , &memAllocInfo, nullptr , &deviceMemory));
175
- VK_CHECK_RESULT (vkBindImageMemory (device->logicalDevice , image, deviceMemory, 0 ));
176
-
177
- VkImageSubresourceRange subresourceRange = {};
178
- subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
179
- subresourceRange.baseMipLevel = 0 ;
180
- subresourceRange.levelCount = mipLevels;
181
- subresourceRange.layerCount = 1 ;
182
-
183
- // Image barrier for optimal image (target)
184
- // Optimal image will be used as destination for the copy
185
- vks::tools::setImageLayout (
186
- copyCmd,
187
- image,
188
- VK_IMAGE_LAYOUT_UNDEFINED,
189
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
190
- subresourceRange);
191
-
192
- // Copy mip levels from staging buffer
193
- vkCmdCopyBufferToImage (
194
- copyCmd,
195
- stagingBuffer,
196
- image,
197
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
198
- static_cast <uint32_t >(bufferCopyRegions.size ()),
199
- bufferCopyRegions.data ()
200
- );
201
-
202
- // Change texture image layout to shader read after all mip levels have been copied
203
- this ->imageLayout = imageLayout;
204
- vks::tools::setImageLayout (
205
- copyCmd,
206
- image,
207
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
208
- imageLayout,
209
- subresourceRange);
210
-
211
- device->flushCommandBuffer (copyCmd, copyQueue);
212
-
213
- // Clean up staging resources
214
- vkDestroyBuffer (device->logicalDevice , stagingBuffer, nullptr );
215
- vkFreeMemory (device->logicalDevice , stagingMemory, nullptr );
216
- }
217
- else
121
+ for (uint32_t i = 0 ; i < mipLevels; i++)
218
122
{
219
- // Prefer using optimal tiling, as linear tiling
220
- // may support only a small set of features
221
- // depending on implementation (e.g. no mip maps, only one layer, etc.)
222
-
223
- // Check if this support is supported for linear tiling
224
- assert (formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
225
-
226
- VkImage mappableImage;
227
- VkDeviceMemory mappableMemory;
228
-
229
- VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo ();
230
- imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
231
- imageCreateInfo.format = format;
232
- imageCreateInfo.extent = { width, height, 1 };
233
- imageCreateInfo.mipLevels = 1 ;
234
- imageCreateInfo.arrayLayers = 1 ;
235
- imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
236
- imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
237
- imageCreateInfo.usage = imageUsageFlags;
238
- imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
239
- imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
240
-
241
- // Load mip map level 0 to linear tiling image
242
- VK_CHECK_RESULT (vkCreateImage (device->logicalDevice , &imageCreateInfo, nullptr , &mappableImage));
243
-
244
- // Get memory requirements for this image
245
- // like size and alignment
246
- vkGetImageMemoryRequirements (device->logicalDevice , mappableImage, &memReqs);
247
- // Set memory allocation size to required memory size
248
- memAllocInfo.allocationSize = memReqs.size ;
249
-
250
- // Get memory type that can be mapped to host memory
251
- memAllocInfo.memoryTypeIndex = device->getMemoryType (memReqs.memoryTypeBits , VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
252
-
253
- // Allocate host memory
254
- VK_CHECK_RESULT (vkAllocateMemory (device->logicalDevice , &memAllocInfo, nullptr , &mappableMemory));
123
+ ktx_size_t offset;
124
+ KTX_error_code result = ktxTexture_GetImageOffset (ktxTexture, i, 0 , 0 , &offset);
125
+ assert (result == KTX_SUCCESS);
126
+
127
+ VkBufferImageCopy bufferCopyRegion = {};
128
+ bufferCopyRegion.imageSubresource .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
129
+ bufferCopyRegion.imageSubresource .mipLevel = i;
130
+ bufferCopyRegion.imageSubresource .baseArrayLayer = 0 ;
131
+ bufferCopyRegion.imageSubresource .layerCount = 1 ;
132
+ bufferCopyRegion.imageExtent .width = std::max (1u , ktxTexture->baseWidth >> i);
133
+ bufferCopyRegion.imageExtent .height = std::max (1u , ktxTexture->baseHeight >> i);
134
+ bufferCopyRegion.imageExtent .depth = 1 ;
135
+ bufferCopyRegion.bufferOffset = offset;
136
+
137
+ bufferCopyRegions.push_back (bufferCopyRegion);
138
+ }
255
139
256
- // Bind allocated image for use
257
- VK_CHECK_RESULT (vkBindImageMemory (device->logicalDevice , mappableImage, mappableMemory, 0 ));
140
+ // Create optimal tiled target image
141
+ VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo ();
142
+ imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
143
+ imageCreateInfo.format = format;
144
+ imageCreateInfo.mipLevels = mipLevels;
145
+ imageCreateInfo.arrayLayers = 1 ;
146
+ imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
147
+ imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
148
+ imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
149
+ imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
150
+ imageCreateInfo.extent = { width, height, 1 };
151
+ imageCreateInfo.usage = imageUsageFlags;
152
+ // Ensure that the TRANSFER_DST bit is set for staging
153
+ if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
154
+ {
155
+ imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
156
+ }
157
+ VK_CHECK_RESULT (vkCreateImage (device->logicalDevice , &imageCreateInfo, nullptr , &image));
258
158
259
- // Get sub resource layout
260
- // Mip map count, array layer, etc.
261
- VkImageSubresource subRes = {};
262
- subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
263
- subRes.mipLevel = 0 ;
159
+ vkGetImageMemoryRequirements (device->logicalDevice , image, &memReqs);
264
160
265
- VkSubresourceLayout subResLayout;
266
- void *data;
161
+ memAllocInfo.allocationSize = memReqs.size ;
267
162
268
- // Get sub resources layout
269
- // Includes row pitch, size offsets, etc.
270
- vkGetImageSubresourceLayout ( device->logicalDevice , mappableImage, &subRes, &subResLayout );
163
+ memAllocInfo. memoryTypeIndex = device-> getMemoryType (memReqs. memoryTypeBits , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
164
+ VK_CHECK_RESULT ( vkAllocateMemory (device-> logicalDevice , &memAllocInfo, nullptr , &deviceMemory));
165
+ VK_CHECK_RESULT ( vkBindImageMemory ( device->logicalDevice , image, deviceMemory, 0 ) );
271
166
272
- // Map image memory
273
- VK_CHECK_RESULT (vkMapMemory (device->logicalDevice , mappableMemory, 0 , memReqs.size , 0 , &data));
167
+ VkImageSubresourceRange subresourceRange = {};
168
+ subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
169
+ subresourceRange.baseMipLevel = 0 ;
170
+ subresourceRange.levelCount = mipLevels;
171
+ subresourceRange.layerCount = 1 ;
274
172
275
- // Copy image data into memory
276
- memcpy (data, ktxTextureData, memReqs.size );
173
+ // Image barrier for optimal image (target)
174
+ // Optimal image will be used as destination for the copy
175
+ vks::tools::setImageLayout (
176
+ copyCmd,
177
+ image,
178
+ VK_IMAGE_LAYOUT_UNDEFINED,
179
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
180
+ subresourceRange);
277
181
278
- vkUnmapMemory (device->logicalDevice , mappableMemory);
182
+ // Copy mip levels from staging buffer
183
+ vkCmdCopyBufferToImage (
184
+ copyCmd,
185
+ stagingBuffer,
186
+ image,
187
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
188
+ static_cast <uint32_t >(bufferCopyRegions.size ()),
189
+ bufferCopyRegions.data ()
190
+ );
279
191
280
- // Linear tiled images don't need to be staged
281
- // and can be directly used as textures
282
- image = mappableImage;
283
- deviceMemory = mappableMemory;
284
- this ->imageLayout = imageLayout;
192
+ // Change texture image layout to shader read after all mip levels have been copied
193
+ this ->imageLayout = imageLayout;
194
+ vks::tools::setImageLayout (
195
+ copyCmd,
196
+ image,
197
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
198
+ imageLayout,
199
+ subresourceRange);
285
200
286
- // Setup image memory barrier
287
- vks::tools::setImageLayout (copyCmd, image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, imageLayout);
201
+ device->flushCommandBuffer (copyCmd, copyQueue);
288
202
289
- device->flushCommandBuffer (copyCmd, copyQueue);
290
- }
203
+ // Clean up staging resources
204
+ vkDestroyBuffer (device->logicalDevice , stagingBuffer, nullptr );
205
+ vkFreeMemory (device->logicalDevice , stagingMemory, nullptr );
291
206
292
207
ktxTexture_Destroy (ktxTexture);
293
208
@@ -304,7 +219,7 @@ namespace vks
304
219
samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
305
220
samplerCreateInfo.minLod = 0 .0f ;
306
221
// Max level-of-detail should match mip level count
307
- samplerCreateInfo.maxLod = (useStaging) ? ( float )mipLevels : 0 . 0f ;
222
+ samplerCreateInfo.maxLod = (float )mipLevels;
308
223
// Only enable anisotropic filtering if enabled on the device
309
224
samplerCreateInfo.maxAnisotropy = device->enabledFeatures .samplerAnisotropy ? device->properties .limits .maxSamplerAnisotropy : 1 .0f ;
310
225
samplerCreateInfo.anisotropyEnable = device->enabledFeatures .samplerAnisotropy ;
@@ -322,7 +237,7 @@ namespace vks
322
237
viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0 , 1 , 0 , 1 };
323
238
// Linear tiling usually won't support mip maps
324
239
// Only set mip map count if optimal tiling is used
325
- viewCreateInfo.subresourceRange .levelCount = (useStaging) ? mipLevels : 1 ;
240
+ viewCreateInfo.subresourceRange .levelCount = mipLevels;
326
241
viewCreateInfo.image = image;
327
242
VK_CHECK_RESULT (vkCreateImageView (device->logicalDevice , &viewCreateInfo, nullptr , &view));
328
243
@@ -382,7 +297,7 @@ namespace vks
382
297
VK_CHECK_RESULT (vkBindBufferMemory (device->logicalDevice , stagingBuffer, stagingMemory, 0 ));
383
298
384
299
// Copy texture data into staging buffer
385
- uint8_t *data;
300
+ uint8_t *data{ nullptr } ;
386
301
VK_CHECK_RESULT (vkMapMemory (device->logicalDevice , stagingMemory, 0 , memReqs.size , 0 , (void **)&data));
387
302
memcpy (data, buffer, bufferSize);
388
303
vkUnmapMemory (device->logicalDevice , stagingMemory);
@@ -547,7 +462,7 @@ namespace vks
547
462
VK_CHECK_RESULT (vkBindBufferMemory (device->logicalDevice , stagingBuffer, stagingMemory, 0 ));
548
463
549
464
// Copy texture data into staging buffer
550
- uint8_t *data;
465
+ uint8_t *data{ nullptr } ;
551
466
VK_CHECK_RESULT (vkMapMemory (device->logicalDevice , stagingMemory, 0 , memReqs.size , 0 , (void **)&data));
552
467
memcpy (data, ktxTextureData, ktxTextureSize);
553
468
vkUnmapMemory (device->logicalDevice , stagingMemory);
@@ -730,7 +645,7 @@ namespace vks
730
645
VK_CHECK_RESULT (vkBindBufferMemory (device->logicalDevice , stagingBuffer, stagingMemory, 0 ));
731
646
732
647
// Copy texture data into staging buffer
733
- uint8_t *data;
648
+ uint8_t *data{ nullptr } ;
734
649
VK_CHECK_RESULT (vkMapMemory (device->logicalDevice , stagingMemory, 0 , memReqs.size , 0 , (void **)&data));
735
650
memcpy (data, ktxTextureData, ktxTextureSize);
736
651
vkUnmapMemory (device->logicalDevice , stagingMemory);
0 commit comments