Skip to content

Commit c9bd63b

Browse files
committed
Remove support for loading linear images
Was not used in any sample
1 parent b917923 commit c9bd63b

File tree

2 files changed

+108
-194
lines changed

2 files changed

+108
-194
lines changed

base/VulkanTexture.cpp

Lines changed: 105 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Vulkan texture loader
2+
* Vulkan texture loader for KTX files
33
*
4-
* Copyright(C) by Sascha Willems - www.saschawillems.de
4+
* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
55
*
66
* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT)
77
*/
@@ -61,10 +61,9 @@ namespace vks
6161
* @param copyQueue Queue used for the texture staging copy commands (must support transfer)
6262
* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
6363
* @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)
6564
*
6665
*/
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)
6867
{
6968
ktxTexture* ktxTexture;
7069
ktxResult result = loadKTXFile(filename, &ktxTexture);
@@ -82,212 +81,128 @@ namespace vks
8281
VkFormatProperties formatProperties;
8382
vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties);
8483

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-
9284
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
9385
VkMemoryRequirements memReqs;
9486

9587
// Use a separate command buffer for texture loading
9688
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
9789

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;
11493

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;
11899

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));
121101

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);
127104

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);
130108

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));
136111

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);
146117

147-
bufferCopyRegions.push_back(bufferCopyRegion);
148-
}
118+
// Setup buffer copy regions for each mip level
119+
std::vector<VkBufferImageCopy> bufferCopyRegions;
149120

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++)
218122
{
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+
}
255139

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));
258158

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);
264160

265-
VkSubresourceLayout subResLayout;
266-
void *data;
161+
memAllocInfo.allocationSize = memReqs.size;
267162

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));
271166

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;
274172

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);
277181

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+
);
279191

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);
285200

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);
288202

289-
device->flushCommandBuffer(copyCmd, copyQueue);
290-
}
203+
// Clean up staging resources
204+
vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
205+
vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
291206

292207
ktxTexture_Destroy(ktxTexture);
293208

@@ -304,7 +219,7 @@ namespace vks
304219
samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
305220
samplerCreateInfo.minLod = 0.0f;
306221
// Max level-of-detail should match mip level count
307-
samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f;
222+
samplerCreateInfo.maxLod = (float)mipLevels;
308223
// Only enable anisotropic filtering if enabled on the device
309224
samplerCreateInfo.maxAnisotropy = device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f;
310225
samplerCreateInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy;
@@ -322,7 +237,7 @@ namespace vks
322237
viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
323238
// Linear tiling usually won't support mip maps
324239
// Only set mip map count if optimal tiling is used
325-
viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1;
240+
viewCreateInfo.subresourceRange.levelCount = mipLevels;
326241
viewCreateInfo.image = image;
327242
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &view));
328243

@@ -382,7 +297,7 @@ namespace vks
382297
VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));
383298

384299
// Copy texture data into staging buffer
385-
uint8_t *data;
300+
uint8_t *data{ nullptr };
386301
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
387302
memcpy(data, buffer, bufferSize);
388303
vkUnmapMemory(device->logicalDevice, stagingMemory);
@@ -547,7 +462,7 @@ namespace vks
547462
VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));
548463

549464
// Copy texture data into staging buffer
550-
uint8_t *data;
465+
uint8_t *data{ nullptr };
551466
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
552467
memcpy(data, ktxTextureData, ktxTextureSize);
553468
vkUnmapMemory(device->logicalDevice, stagingMemory);
@@ -730,7 +645,7 @@ namespace vks
730645
VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));
731646

732647
// Copy texture data into staging buffer
733-
uint8_t *data;
648+
uint8_t *data{ nullptr };
734649
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
735650
memcpy(data, ktxTextureData, ktxTextureSize);
736651
vkUnmapMemory(device->logicalDevice, stagingMemory);

base/VulkanTexture.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
2-
* Vulkan texture loader
2+
* Vulkan texture loader for KTX files
33
*
4-
* Copyright(C) by Sascha Willems - www.saschawillems.de
4+
* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
55
*
66
* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT)
77
*/
@@ -56,8 +56,7 @@ class Texture2D : public Texture
5656
vks::VulkanDevice *device,
5757
VkQueue copyQueue,
5858
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
59-
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
60-
bool forceLinear = false);
59+
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
6160
void fromBuffer(
6261
void * buffer,
6362
VkDeviceSize bufferSize,

0 commit comments

Comments
 (0)