Compressed texture support for Vulkan.

This commit is contained in:
Attila Uygun 2021-01-06 16:46:44 +01:00
parent 823e56ee13
commit bb4c6032fb
2 changed files with 107 additions and 18 deletions

View File

@ -194,7 +194,63 @@ VkIndexType GetIndexType(eng::DataType data_type) {
default: default:
break; break;
} }
NOTREACHED << "Invalid index type"; NOTREACHED << "Invalid index type: " << data_type;
return VK_INDEX_TYPE_UINT16;
}
VkFormat GetImageFormat(eng::Image::Format format) {
switch (format) {
case eng::Image::kRGBA32:
return VK_FORMAT_R8G8B8A8_UNORM;
case eng::Image::kDXT1:
return VK_FORMAT_BC1_RGB_UNORM_BLOCK;
case eng::Image::kDXT5:
return VK_FORMAT_BC3_UNORM_BLOCK;
case eng::Image::kETC1:
case eng::Image::kATC:
case eng::Image::kATCIA:
default:
break;
}
NOTREACHED << "Invalid format: " << format;
return VK_FORMAT_R8G8B8A8_UNORM;
}
void GetBlockSizeForImageFormat(VkFormat format, int& size, int& height) {
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
size = 4;
height = 1;
return;
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
size = 8;
height = 4;
return;
case VK_FORMAT_BC3_UNORM_BLOCK:
size = 16;
height = 4;
return;
default:
break;
}
NOTREACHED << "Invalid format: " << format;
}
void GetNumBlocksForImageFormat(VkFormat format,
int& num_blocks_x,
int& num_blocks_y) {
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
return;
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
case VK_FORMAT_BC3_UNORM_BLOCK:
num_blocks_x = (num_blocks_x + 3) / 4;
num_blocks_y = (num_blocks_y + 3) / 4;
return;
default:
break;
}
NOTREACHED << "Invalid format: " << format;
} }
} // namespace } // namespace
@ -276,6 +332,7 @@ void RendererVulkan::UpdateTexture(std::shared_ptr<void> impl_data,
std::unique_ptr<Image> image) { std::unique_ptr<Image> image) {
auto texture = reinterpret_cast<TextureVulkan*>(impl_data.get()); auto texture = reinterpret_cast<TextureVulkan*>(impl_data.get());
VkImageLayout old_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkImageLayout old_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkFormat format = GetImageFormat(image->GetFormat());
if (texture->view != VK_NULL_HANDLE && if (texture->view != VK_NULL_HANDLE &&
(texture->width != image->GetWidth() || (texture->width != image->GetWidth() ||
@ -287,7 +344,7 @@ void RendererVulkan::UpdateTexture(std::shared_ptr<void> impl_data,
} }
if (texture->view == VK_NULL_HANDLE) { if (texture->view == VK_NULL_HANDLE) {
CreateTexture(texture->image, texture->view, texture->desc_set, CreateTexture(texture->image, texture->view, texture->desc_set, format,
image->GetWidth(), image->GetHeight(), image->GetWidth(), image->GetHeight(),
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VMA_MEMORY_USAGE_GPU_ONLY); VMA_MEMORY_USAGE_GPU_ONLY);
@ -304,9 +361,9 @@ void RendererVulkan::UpdateTexture(std::shared_ptr<void> impl_data,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
old_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)); old_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL));
task_runner_.PostTask( task_runner_.PostTask(
HERE, HERE, std::bind(&RendererVulkan::UpdateImage, this,
std::bind(&RendererVulkan::UpdateImage, this, std::get<0>(texture->image), std::get<0>(texture->image), format, image->GetBuffer(),
image->GetBuffer(), image->GetWidth(), image->GetHeight())); image->GetWidth(), image->GetHeight()));
task_runner_.PostTask( task_runner_.PostTask(
HERE, std::bind(&RendererVulkan::ImageMemoryBarrier, this, HERE, std::bind(&RendererVulkan::ImageMemoryBarrier, this,
std::get<0>(texture->image), VK_ACCESS_TRANSFER_WRITE_BIT, std::get<0>(texture->image), VK_ACCESS_TRANSFER_WRITE_BIT,
@ -718,6 +775,15 @@ bool RendererVulkan::InitializeInternal() {
return false; return false;
} }
texture_compression_.dxt1 = IsFormatSupported(VK_FORMAT_BC1_RGB_UNORM_BLOCK);
texture_compression_.s3tc = IsFormatSupported(VK_FORMAT_BC3_UNORM_BLOCK);
LOG << "TextureCompression:";
LOG << " atc: " << texture_compression_.atc;
LOG << " dxt1: " << texture_compression_.dxt1;
LOG << " etc1: " << texture_compression_.etc1;
LOG << " s3tc: " << texture_compression_.s3tc;
// Use a background thread for filling up staging buffers and recording setup // Use a background thread for filling up staging buffers and recording setup
// commands. // commands.
setup_thread_ = setup_thread_ =
@ -1192,10 +1258,10 @@ void RendererVulkan::BufferMemoryBarrier(VkBuffer buffer,
&buffer_mem_barrier, 0, nullptr); &buffer_mem_barrier, 0, nullptr);
} }
// TODO: Support for compressed textures.
bool RendererVulkan::CreateTexture(Buffer<VkImage>& image, bool RendererVulkan::CreateTexture(Buffer<VkImage>& image,
VkImageView& view, VkImageView& view,
DescSet& desc_set, DescSet& desc_set,
VkFormat format,
int width, int width,
int height, int height,
VkImageUsageFlags usage, VkImageUsageFlags usage,
@ -1210,7 +1276,7 @@ bool RendererVulkan::CreateTexture(Buffer<VkImage>& image,
image_create_info.extent.depth = 1; image_create_info.extent.depth = 1;
image_create_info.mipLevels = 1; image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1; image_create_info.arrayLayers = 1;
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_create_info.format = format;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_create_info.usage = usage; image_create_info.usage = usage;
@ -1244,7 +1310,7 @@ bool RendererVulkan::CreateTexture(Buffer<VkImage>& image,
image_view_create_info.flags = 0; image_view_create_info.flags = 0;
image_view_create_info.image = vk_image; image_view_create_info.image = vk_image;
image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
image_view_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_view_create_info.format = format;
image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
@ -1318,22 +1384,29 @@ void RendererVulkan::FreeTexture(Buffer<VkImage> image,
} }
void RendererVulkan::UpdateImage(VkImage image, void RendererVulkan::UpdateImage(VkImage image,
VkFormat format,
const uint8_t* data, const uint8_t* data,
int width, int width,
int height) { int height) {
constexpr uint32_t pixel_size = 4; int num_blocks_x = width;
// Ensure a single row is small enough to fit in a staging buffer. int num_blocks_y = height;
DCHECK(staging_buffer_size_ >= width * pixel_size); GetNumBlocksForImageFormat(format, num_blocks_x, num_blocks_y);
size_t to_submit = width * height * pixel_size; int block_size, block_height;
GetBlockSizeForImageFormat(format, block_size, block_height);
size_t to_submit = num_blocks_x * num_blocks_y * block_size;
size_t submit_from = 0; size_t submit_from = 0;
uint32_t segment = width * pixel_size; uint32_t segment = num_blocks_x * block_size;
uint32_t max_size = staging_buffer_size_ - (staging_buffer_size_ % segment); uint32_t max_size = staging_buffer_size_ - (staging_buffer_size_ % segment);
uint32_t region_offset_y = 0;
// A segment must fit in a single staging buffer.
DCHECK(staging_buffer_size_ >= segment);
while (to_submit > 0) { while (to_submit > 0) {
uint32_t block_write_offset; uint32_t block_write_offset;
uint32_t block_write_amount; uint32_t block_write_amount;
if (!AllocateStagingBuffer(std::min((uint32_t)to_submit, max_size), segment, if (!AllocateStagingBuffer(std::min((uint32_t)to_submit, max_size), segment,
block_write_offset, block_write_amount)) block_write_offset, block_write_amount))
return; return;
@ -1346,6 +1419,8 @@ void RendererVulkan::UpdateImage(VkImage image,
memcpy(((uint8_t*)data_ptr) + block_write_offset, (char*)data + submit_from, memcpy(((uint8_t*)data_ptr) + block_write_offset, (char*)data + submit_from,
block_write_amount); block_write_amount);
uint32_t region_height = (block_write_amount / segment) * block_height;
// Insert a command to copy to GPU buffer. // Insert a command to copy to GPU buffer.
VkBufferImageCopy buffer_image_copy; VkBufferImageCopy buffer_image_copy;
buffer_image_copy.bufferOffset = block_write_offset; buffer_image_copy.bufferOffset = block_write_offset;
@ -1356,11 +1431,10 @@ void RendererVulkan::UpdateImage(VkImage image,
buffer_image_copy.imageSubresource.baseArrayLayer = 0; buffer_image_copy.imageSubresource.baseArrayLayer = 0;
buffer_image_copy.imageSubresource.layerCount = 1; buffer_image_copy.imageSubresource.layerCount = 1;
buffer_image_copy.imageOffset.x = 0; buffer_image_copy.imageOffset.x = 0;
buffer_image_copy.imageOffset.y = (submit_from / pixel_size) / width; buffer_image_copy.imageOffset.y = region_offset_y;
buffer_image_copy.imageOffset.z = 0; buffer_image_copy.imageOffset.z = 0;
buffer_image_copy.imageExtent.width = width; buffer_image_copy.imageExtent.width = width;
buffer_image_copy.imageExtent.height = buffer_image_copy.imageExtent.height = region_height;
(block_write_amount / pixel_size) / width;
buffer_image_copy.imageExtent.depth = 1; buffer_image_copy.imageExtent.depth = 1;
vkCmdCopyBufferToImage(frames_[current_frame_].setup_command_buffer, vkCmdCopyBufferToImage(frames_[current_frame_].setup_command_buffer,
@ -1370,6 +1444,7 @@ void RendererVulkan::UpdateImage(VkImage image,
to_submit -= block_write_amount; to_submit -= block_write_amount;
submit_from += block_write_amount; submit_from += block_write_amount;
region_offset_y += region_height;
} }
} }
@ -1744,6 +1819,13 @@ bool RendererVulkan::SetUniformInternal(ShaderVulkan* shader,
return true; return true;
} }
bool RendererVulkan::IsFormatSupported(VkFormat format) {
VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(context_.GetPhysicalDevice(), format,
&properties);
return properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
}
void RendererVulkan::ContextLost() { void RendererVulkan::ContextLost() {
LOG << "Context lost."; LOG << "Context lost.";
InvalidateAllResources(); InvalidateAllResources();

View File

@ -234,6 +234,7 @@ class RendererVulkan : public Renderer {
bool CreateTexture(Buffer<VkImage>& image, bool CreateTexture(Buffer<VkImage>& image,
VkImageView& view, VkImageView& view,
DescSet& desc_set, DescSet& desc_set,
VkFormat format,
int width, int width,
int height, int height,
VkImageUsageFlags usage, VkImageUsageFlags usage,
@ -241,7 +242,11 @@ class RendererVulkan : public Renderer {
void FreeTexture(Buffer<VkImage> image, void FreeTexture(Buffer<VkImage> image,
VkImageView image_view, VkImageView image_view,
DescSet desc_set); DescSet desc_set);
void UpdateImage(VkImage image, const uint8_t* data, int width, int height); void UpdateImage(VkImage image,
VkFormat format,
const uint8_t* data,
int width,
int height);
void ImageMemoryBarrier(VkImage image, void ImageMemoryBarrier(VkImage image,
VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags src_stage_mask,
VkPipelineStageFlags dst_stage_mask, VkPipelineStageFlags dst_stage_mask,
@ -262,6 +267,8 @@ class RendererVulkan : public Renderer {
template <typename T> template <typename T>
bool SetUniformInternal(ShaderVulkan* shader, const std::string& name, T val); bool SetUniformInternal(ShaderVulkan* shader, const std::string& name, T val);
bool IsFormatSupported(VkFormat format);
void ContextLost(); void ContextLost();
void InvalidateAllResources(); void InvalidateAllResources();