Fixed image memory barriers, code cleanup, additional comments

Refs #494
This commit is contained in:
saschawillems 2018-06-30 16:48:27 +02:00
parent ba332da0bc
commit 756c70f089

View file

@ -109,73 +109,23 @@ public:
}; };
} }
// Create an image memory barrier used to change the layout of an image and put it into an active command buffer /*
void setImageLayout(VkCommandBuffer cmdBuffer, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkImageSubresourceRange subresourceRange) Upload texture image data to the GPU
{
// Create an image barrier object
VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier();;
imageMemoryBarrier.oldLayout = oldImageLayout;
imageMemoryBarrier.newLayout = newImageLayout;
imageMemoryBarrier.image = image;
imageMemoryBarrier.subresourceRange = subresourceRange;
// Only sets masks for layouts used in this example Vulkan offers two types of image tiling (memory layout):
// For a more complete version that can be used with other layouts see vks::tools::setImageLayout
// Source layouts (old) Linear tiled images:
switch (oldImageLayout) These are stored as is and can be copied directly to. But due to the linear nature they're not a good match for GPUs and format and feature support is very limited.
{ It's not advised to use linear tiled images for anything else than copying from host to GPU if buffer copies are not an option.
case VK_IMAGE_LAYOUT_UNDEFINED: Linear tiling is thus only implemented for learning purposes, one should always prefer optimal tiled image.
// Only valid as initial layout, memory contents are not preserved
// Can be accessed directly, no source dependency required
imageMemoryBarrier.srcAccessMask = 0;
break;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
// Only valid as initial layout for linear images, preserves memory contents
// Make sure host writes to the image have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
// Old layout is transfer destination
// Make sure any writes to the image have been finished
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
}
// Target layouts (new) Optimal tiled images:
switch (newImageLayout) These are stored in an implementation specific layout matching the capability of the hardware. They usually support more formats and features and are much faster.
{ Optimal tiled images are stored on the device and not accessible by the host. So they can't be written directly to (like liner tiled images) and always require
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: some sort of data copy, either from a buffer or a linear tiled image.
// Transfer source (copy, blit)
// Make sure any reads from the image have been finished
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
// Transfer destination (copy, blit)
// Make sure any writes to the image have been finished
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
// Shader read (sampler, input attachment)
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
}
// Put barrier on top of pipeline
VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags destStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
// Put barrier inside setup command buffer
vkCmdPipelineBarrier(
cmdBuffer,
srcStageFlags,
destStageFlags,
VK_FLAGS_NONE,
0, nullptr,
0, nullptr,
1, &imageMemoryBarrier);
}
In Short: Always use optimal tiled images for rendering.
*/
void loadTexture() void loadTexture()
{ {
// We use the Khronos texture format (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/) // We use the Khronos texture format (https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/)
@ -183,10 +133,6 @@ public:
// Texture data contains 4 channels (RGBA) with unnormalized 8-bit values, this is the most commonly supported format // Texture data contains 4 channels (RGBA) with unnormalized 8-bit values, this is the most commonly supported format
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
// Set to true to use linear tiled images
// This is just for learning purposes and not suggested, as linear tiled images are pretty restricted and often only support a small set of features (e.g. no mips, etc.)
bool forceLinearTiling = false;
#if defined(__ANDROID__) #if defined(__ANDROID__)
// Textures are stored inside the apk on Android (compressed) // Textures are stored inside the apk on Android (compressed)
// So they need to be loaded via the asset manager // So they need to be loaded via the asset manager
@ -206,35 +152,32 @@ public:
assert(!tex2D.empty()); assert(!tex2D.empty());
VkFormatProperties formatProperties;
texture.width = static_cast<uint32_t>(tex2D[0].extent().x); texture.width = static_cast<uint32_t>(tex2D[0].extent().x);
texture.height = static_cast<uint32_t>(tex2D[0].extent().y); texture.height = static_cast<uint32_t>(tex2D[0].extent().y);
texture.mipLevels = static_cast<uint32_t>(tex2D.levels()); texture.mipLevels = static_cast<uint32_t>(tex2D.levels());
// Get device properites for the requested texture format // We prefer using staging to copy the texture data to a device local optimal image
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties);
// Only use linear tiling if requested (and supported by the device)
// Support for linear tiling is mostly limited, so prefer to use
// optimal tiling instead
// On most implementations linear tiling will only support a very
// limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
VkBool32 useStaging = true; VkBool32 useStaging = true;
// Only use linear tiling if forced // Only use linear tiling if forced
if (forceLinearTiling) bool forceLinearTiling = false;
{ if (forceLinearTiling) {
// Don't use linear if format is not supported for (linear) shader sampling // Don't use linear if format is not supported for (linear) shader sampling
// Get device properites for the requested texture format
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties);
useStaging = !(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); useStaging = !(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
} }
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
VkMemoryRequirements memReqs = {}; VkMemoryRequirements memReqs = {};
if (useStaging) if (useStaging) {
{ // Copy data to an optimal tiled image
// This loads the texture data into a host local buffer that is copied to the optimal tiled image on the device
// Create a host-visible staging buffer that contains the raw image data // Create a host-visible staging buffer that contains the raw image data
// This buffer will be the data source for copying texture data to the optimal tiled image on the device
VkBuffer stagingBuffer; VkBuffer stagingBuffer;
VkDeviceMemory stagingMemory; VkDeviceMemory stagingMemory;
@ -243,20 +186,17 @@ public:
// This buffer is used as a transfer source for the buffer copy // This buffer is used as a transfer source for the buffer copy
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer)); VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer));
// Get memory requirements for the staging buffer (alignment, memory type bits) // Get memory requirements for the staging buffer (alignment, memory type bits)
vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs);
memAllocInfo.allocationSize = memReqs.size; memAllocInfo.allocationSize = memReqs.size;
// Get memory type index for a host visible buffer // Get memory type index for a host visible buffer
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory)); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory));
VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0));
// Copy texture data into staging buffer // Copy texture data into host local staging buffer
uint8_t *data; uint8_t *data;
VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data));
memcpy(data, tex2D.data(), tex2D.size()); memcpy(data, tex2D.data(), tex2D.size());
@ -266,8 +206,7 @@ public:
std::vector<VkBufferImageCopy> bufferCopyRegions; std::vector<VkBufferImageCopy> bufferCopyRegions;
uint32_t offset = 0; uint32_t offset = 0;
for (uint32_t i = 0; i < texture.mipLevels; i++) for (uint32_t i = 0; i < texture.mipLevels; i++) {
{
VkBufferImageCopy bufferCopyRegion = {}; VkBufferImageCopy bufferCopyRegion = {};
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
bufferCopyRegion.imageSubresource.mipLevel = i; bufferCopyRegion.imageSubresource.mipLevel = i;
@ -283,7 +222,7 @@ public:
offset += static_cast<uint32_t>(tex2D[i].size()); offset += static_cast<uint32_t>(tex2D[i].size());
} }
// Create optimal tiled target image // Create optimal tiled target image on the device
VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = format; imageCreateInfo.format = format;
@ -296,22 +235,19 @@ public:
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageCreateInfo.extent = { texture.width, texture.height, 1 }; imageCreateInfo.extent = { texture.width, texture.height, 1 };
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image)); VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &texture.image));
vkGetImageMemoryRequirements(device, texture.image, &memReqs); vkGetImageMemoryRequirements(device, texture.image, &memReqs);
memAllocInfo.allocationSize = memReqs.size; memAllocInfo.allocationSize = memReqs.size;
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &texture.deviceMemory)); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &texture.deviceMemory));
VK_CHECK_RESULT(vkBindImageMemory(device, texture.image, texture.deviceMemory, 0)); VK_CHECK_RESULT(vkBindImageMemory(device, texture.image, texture.deviceMemory, 0));
VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
// Image barrier for optimal image // Image memory barriers for the texture image
// The sub resource range describes the regions of the image we will be transition // The sub resource range describes the regions of the image that will be transitioned using the memory barriers below
VkImageSubresourceRange subresourceRange = {}; VkImageSubresourceRange subresourceRange = {};
// Image only contains color data // Image only contains color data
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@ -322,15 +258,26 @@ public:
// The 2D texture only has one layer // The 2D texture only has one layer
subresourceRange.layerCount = 1; subresourceRange.layerCount = 1;
// Optimal image will be used as destination for the copy, so we must transfer from our // Transition the texture image layout to transfer target, so we can safely copy our buffer data to it.
// initial undefined image layout to the transfer destination layout VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier();;
setImageLayout( imageMemoryBarrier.image = texture.image;
imageMemoryBarrier.subresourceRange = subresourceRange;
imageMemoryBarrier.srcAccessMask = 0;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
// Insert a memory dependency at the proper pipeline stages that will execute the image layout transition
// Source pipeline stage is host write/read exection (VK_PIPELINE_STAGE_HOST_BIT)
// Destination pipeline stage is copy command exection (VK_PIPELINE_STAGE_TRANSFER_BIT)
vkCmdPipelineBarrier(
copyCmd, copyCmd,
texture.image, VK_PIPELINE_STAGE_HOST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, 0,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, nullptr,
subresourceRange); 0, nullptr,
1, &imageMemoryBarrier);
// Copy mip levels from staging buffer // Copy mip levels from staging buffer
vkCmdCopyBufferToImage( vkCmdCopyBufferToImage(
@ -341,24 +288,35 @@ public:
static_cast<uint32_t>(bufferCopyRegions.size()), static_cast<uint32_t>(bufferCopyRegions.size()),
bufferCopyRegions.data()); bufferCopyRegions.data());
// Change texture image layout to shader read after all mip levels have been copied // Once the data has been uploaded we transfer to the texture image to the shader read layout, so it can be sampled from
texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
setImageLayout( imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Insert a memory dependency at the proper pipeline stages that will execute the image layout transition
// Source pipeline stage stage is copy command exection (VK_PIPELINE_STAGE_TRANSFER_BIT)
// Destination pipeline stage fragment shader access (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
vkCmdPipelineBarrier(
copyCmd, copyCmd,
texture.image, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_ASPECT_COLOR_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0,
texture.imageLayout, 0, nullptr,
subresourceRange); 0, nullptr,
1, &imageMemoryBarrier);
// Store current layout for later reuse
texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true);
// Clean up staging resources // Clean up staging resources
vkFreeMemory(device, stagingMemory, nullptr); vkFreeMemory(device, stagingMemory, nullptr);
vkDestroyBuffer(device, stagingBuffer, nullptr); vkDestroyBuffer(device, stagingBuffer, nullptr);
} } else {
else // Copy data to a linear tiled image
{
VkImage mappableImage; VkImage mappableImage;
VkDeviceMemory mappableMemory; VkDeviceMemory mappableMemory;
@ -376,69 +334,57 @@ public:
imageCreateInfo.extent = { texture.width, texture.height, 1 }; imageCreateInfo.extent = { texture.width, texture.height, 1 };
VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &mappableImage)); VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &mappableImage));
// Get memory requirements for this image // Get memory requirements for this image like size and alignment
// like size and alignment
vkGetImageMemoryRequirements(device, mappableImage, &memReqs); vkGetImageMemoryRequirements(device, mappableImage, &memReqs);
// Set memory allocation size to required memory size // Set memory allocation size to required memory size
memAllocInfo.allocationSize = memReqs.size; memAllocInfo.allocationSize = memReqs.size;
// Get memory type that can be mapped to host memory // Get memory type that can be mapped to host memory
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
// Allocate host memory
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &mappableMemory)); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &mappableMemory));
// Bind allocated image for use
VK_CHECK_RESULT(vkBindImageMemory(device, mappableImage, mappableMemory, 0)); VK_CHECK_RESULT(vkBindImageMemory(device, mappableImage, mappableMemory, 0));
// Get sub resource layout
// Mip map count, array layer, etc.
VkImageSubresource subRes = {};
subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkSubresourceLayout subResLayout;
void *data;
// Get sub resources layout
// Includes row pitch, size offsets, etc.
vkGetImageSubresourceLayout(device, mappableImage, &subRes, &subResLayout);
// Map image memory // Map image memory
void *data;
VK_CHECK_RESULT(vkMapMemory(device, mappableMemory, 0, memReqs.size, 0, &data)); VK_CHECK_RESULT(vkMapMemory(device, mappableMemory, 0, memReqs.size, 0, &data));
// Copy image data of the first mip level into memory
// Copy image data into memory memcpy(data, tex2D[0].data(), tex2D[0].size());
memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size());
vkUnmapMemory(device, mappableMemory); vkUnmapMemory(device, mappableMemory);
// Linear tiled images don't need to be staged // Linear tiled images don't need to be staged and can be directly used as textures
// and can be directly used as textures
texture.image = mappableImage; texture.image = mappableImage;
texture.deviceMemory = mappableMemory; texture.deviceMemory = mappableMemory;
texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
// Setup image memory barrier transfer image to shader read layout // Setup image memory barrier transfer image to shader read layout
VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
// The sub resource range describes the regions of the image we will be transition // The sub resource range describes the regions of the image we will be transition
VkImageSubresourceRange subresourceRange = {}; VkImageSubresourceRange subresourceRange = {};
// Image only contains color data
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
// Start at first mip level
subresourceRange.baseMipLevel = 0; subresourceRange.baseMipLevel = 0;
// Only one mip level, most implementations won't support more for linear tiled images
subresourceRange.levelCount = 1; subresourceRange.levelCount = 1;
// The 2D texture only has one layer
subresourceRange.layerCount = 1; subresourceRange.layerCount = 1;
setImageLayout( // Transition the texture image layout to shader read, so it can be sampled from
VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier();;
imageMemoryBarrier.image = texture.image;
imageMemoryBarrier.subresourceRange = subresourceRange;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Insert a memory dependency at the proper pipeline stages that will execute the image layout transition
// Source pipeline stage is host write/read exection (VK_PIPELINE_STAGE_HOST_BIT)
// Destination pipeline stage fragment shader access (VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
vkCmdPipelineBarrier(
copyCmd, copyCmd,
texture.image, VK_PIPELINE_STAGE_HOST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_IMAGE_LAYOUT_PREINITIALIZED, 0,
texture.imageLayout, 0, nullptr,
subresourceRange); 0, nullptr,
1, &imageMemoryBarrier);
VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true);
} }
@ -461,14 +407,11 @@ public:
sampler.maxLod = (useStaging) ? (float)texture.mipLevels : 0.0f; sampler.maxLod = (useStaging) ? (float)texture.mipLevels : 0.0f;
// Enable anisotropic filtering // Enable anisotropic filtering
// This feature is optional, so we must check if it's supported on the device // This feature is optional, so we must check if it's supported on the device
if (vulkanDevice->features.samplerAnisotropy) if (vulkanDevice->features.samplerAnisotropy) {
{
// Use max. level of anisotropy for this example // Use max. level of anisotropy for this example
sampler.maxAnisotropy = vulkanDevice->properties.limits.maxSamplerAnisotropy; sampler.maxAnisotropy = vulkanDevice->properties.limits.maxSamplerAnisotropy;
sampler.anisotropyEnable = VK_TRUE; sampler.anisotropyEnable = VK_TRUE;
} } else {
else
{
// The device does not support anisotropic filtering // The device does not support anisotropic filtering
sampler.maxAnisotropy = 1.0; sampler.maxAnisotropy = 1.0;
sampler.anisotropyEnable = VK_FALSE; sampler.anisotropyEnable = VK_FALSE;