From c0f5b46f8188155fbab1ca0e405a96371023b5f2 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sun, 10 Dec 2017 18:14:28 +0100 Subject: [PATCH] [dxvk] Added proper support for block-compressed image formats --- src/dxvk/dxvk_context.cpp | 87 ++++++++++++++++++++++++--------------- src/dxvk/dxvk_format.cpp | 66 +++++++++++++++++++++-------- src/dxvk/dxvk_format.h | 21 ++++++++-- 3 files changed, 121 insertions(+), 53 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 7306d9f5..85c9e509 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -421,6 +421,56 @@ namespace dxvk { return; } + // Upload data through a staging buffer. Special care needs to + // be taken when dealing with compressed image formats: Rather + // than copying pixels, we'll be copying blocks of pixels. + const DxvkFormatInfo* formatInfo + = imageFormatInfo(image->info().format); + + VkExtent3D elementCount = imageExtent; + elementCount.depth *= subresources.layerCount; + + elementCount.width /= formatInfo->blockSize.width; + elementCount.height /= formatInfo->blockSize.height; + elementCount.depth /= formatInfo->blockSize.depth; + + VkDeviceSize bytesPerRow = elementCount.width * formatInfo->elementSize; + VkDeviceSize bytesPerLayer = elementCount.height * bytesPerRow; + VkDeviceSize bytesTotal = elementCount.depth * bytesPerLayer; + + // Allocate staging buffer memory for the image data. The + // pixels or blocks will be tightly packed within the buffer. + DxvkStagingBufferSlice slice = m_cmd->stagedAlloc(bytesTotal); + + auto dstData = reinterpret_cast(slice.mapPtr); + auto srcData = reinterpret_cast(data); + + // If the application provides tightly packed data as well, + // we can minimize the number of memcpy calls in order to + // improve performance. + bool useDirectCopy = true; + + useDirectCopy &= (pitchPerLayer == bytesPerLayer) || (elementCount.depth == 1); + useDirectCopy &= (pitchPerRow == bytesPerRow) || (elementCount.height == 1); + + if (useDirectCopy) { + std::memcpy(dstData, srcData, bytesTotal); + } else { + for (uint32_t i = 0; i < elementCount.depth; i++) { + for (uint32_t j = 0; j < elementCount.height; j++) { + std::memcpy(dstData, srcData, bytesPerRow); + + dstData += bytesPerRow; + srcData += pitchPerRow; + } + + dstData += bytesPerLayer; + srcData += pitchPerLayer; + } + } + + // Prepare the image layout. If the given extent covers + // the entire image, we may discard its previous contents. VkImageSubresourceRange subresourceRange; subresourceRange.aspectMask = subresources.aspectMask; subresourceRange.baseMipLevel = subresources.mipLevel; @@ -440,39 +490,9 @@ namespace dxvk { VK_ACCESS_TRANSFER_WRITE_BIT); m_barriers.recordCommands(m_cmd); - // TODO support block formats properly - const DxvkFormatInfo* formatInfo - = imageFormatInfo(image->info().format); - - const VkDeviceSize layerCount = imageExtent.depth * subresources.layerCount; - VkDeviceSize bytesPerRow = imageExtent.width * formatInfo->elementSize; - VkDeviceSize bytesPerLayer = imageExtent.height * bytesPerRow; - VkDeviceSize bytesTotal = layerCount * bytesPerLayer; - - auto slice = m_cmd->stagedAlloc(bytesTotal); - auto dstData = reinterpret_cast(slice.mapPtr); - auto srcData = reinterpret_cast(data); - - bool useDirectCopy = true; - useDirectCopy &= (pitchPerLayer == bytesPerLayer) || layerCount == 1; - useDirectCopy &= (pitchPerRow == bytesPerRow) || imageExtent.height == 1; - - if (useDirectCopy) { - std::memcpy(dstData, srcData, bytesTotal); - } else { - for (uint32_t i = 0; i < layerCount; i++) { - for (uint32_t j = 0; j < imageExtent.height; j++) { - std::memcpy(dstData, srcData, bytesPerRow); - - dstData += bytesPerRow; - srcData += pitchPerRow; - } - - dstData += bytesPerLayer; - srcData += pitchPerLayer; - } - } - + // Copy contents of the staging buffer into the image. + // Since our source data is tightly packed, we do not + // need to specify any strides. VkBufferImageCopy region; region.bufferOffset = slice.offset; region.bufferRowLength = 0; @@ -485,6 +505,7 @@ namespace dxvk { VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region, slice); + // Transition image back into its optimal layout m_barriers.accessImage( image, subresourceRange, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, diff --git a/src/dxvk/dxvk_format.cpp b/src/dxvk/dxvk_format.cpp index e3d71ddf..554f6438 100644 --- a/src/dxvk/dxvk_format.cpp +++ b/src/dxvk/dxvk_format.cpp @@ -4,7 +4,7 @@ namespace dxvk { const std::array g_formatInfos = {{ // VK_FORMAT_UNDEFINED - { 0, 0 }, + { }, // VK_FORMAT_R4G4_UNORM_PACK8 { 1, VK_IMAGE_ASPECT_COLOR_BIT }, @@ -397,52 +397,84 @@ namespace dxvk { { 0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT }, // VK_FORMAT_BC1_RGB_UNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 8, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC1_RGB_SRGB_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 8, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC1_RGBA_UNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 8, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC1_RGBA_SRGB_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 8, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC2_UNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC2_SRGB_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC3_UNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC3_SRGB_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC4_UNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 8, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC4_SNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 8, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC5_UNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC5_SNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC6H_UFLOAT_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC6H_SFLOAT_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC7_UNORM_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, // VK_FORMAT_BC7_SRGB_BLOCK - { 0, VK_IMAGE_ASPECT_COLOR_BIT }, + { 16, VK_IMAGE_ASPECT_COLOR_BIT, + DxvkFormatFlag::BlockCompressed, + VkExtent3D { 4, 4, 1 } }, }}; const DxvkFormatInfo* imageFormatInfo(VkFormat format) { diff --git a/src/dxvk/dxvk_format.h b/src/dxvk/dxvk_format.h index e2c2b01f..f0baaa4b 100644 --- a/src/dxvk/dxvk_format.h +++ b/src/dxvk/dxvk_format.h @@ -4,6 +4,12 @@ namespace dxvk { + enum class DxvkFormatFlag { + BlockCompressed, + }; + + using DxvkFormatFlags = Flags; + /** * \brief Format info structure * @@ -11,14 +17,23 @@ namespace dxvk { * about a Vulkan image format. */ struct DxvkFormatInfo { - /// Size of an element in this format - VkDeviceSize elementSize; + /// Size of an element in this format. For compressed + /// formats, this is the size of a block, in bytes. + VkDeviceSize elementSize = 0; /// Available image aspect flags - VkImageAspectFlags aspectMask; + VkImageAspectFlags aspectMask = 0; + + /// Some other format info flags + DxvkFormatFlags flags = 0; + + /// Size, in pixels, of a compressed block. For + /// non-block formats, all these values are 1. + VkExtent3D blockSize = { 1, 1, 1 }; }; + const DxvkFormatInfo* imageFormatInfo(VkFormat format); } \ No newline at end of file