[dxvk] Added proper support for block-compressed image formats

This commit is contained in:
Philip Rebohle 2017-12-10 18:14:28 +01:00
parent cd4f21a0c3
commit c0f5b46f81
3 changed files with 121 additions and 53 deletions

View File

@ -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<char*>(slice.mapPtr);
auto srcData = reinterpret_cast<const char*>(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<char*>(slice.mapPtr);
auto srcData = reinterpret_cast<const char*>(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,

View File

@ -4,7 +4,7 @@ namespace dxvk {
const std::array<DxvkFormatInfo, 147> 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) {

View File

@ -4,6 +4,12 @@
namespace dxvk {
enum class DxvkFormatFlag {
BlockCompressed,
};
using DxvkFormatFlags = Flags<DxvkFormatFlag>;
/**
* \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);
}