[d3d11] add some resource validation for copying resources (#305)

* [d3d11] add some resource validation for CopyResource and CopyResourceSubregion

combine if statement

* [d3d11] added copy extents validation for compressed formats

* correct return values

* fix incorrect logic operators

* set valid copy extents when possible

* [d3d11] Clamp copy region in CopySubresourceRegion

* [dxvk] Add helper methods to deal with block-compressed images

* [d3d11] Clean up validation in CopySubresourceRegion

* [d3d11] Improve error reporting and validation in CopyResource

* [d3d11] Fix inconsistent error messages
This commit is contained in:
ZeroFault 2018-04-20 15:35:29 -06:00 committed by Philip Rebohle
parent 50f1cf007d
commit 52f0d853c0
2 changed files with 207 additions and 25 deletions

View File

@ -316,20 +316,31 @@ namespace dxvk {
auto dstBuffer = static_cast<D3D11Buffer*>(pDstResource)->GetBufferSlice();
auto srcBuffer = static_cast<D3D11Buffer*>(pSrcResource)->GetBufferSlice();
VkDeviceSize dstOffset = DstX;
VkDeviceSize srcOffset = 0;
VkDeviceSize srcLength = srcBuffer.length();
VkDeviceSize regLength = srcBuffer.length();
if (dstOffset >= dstBuffer.length())
return;
if (pSrcBox != nullptr) {
if (pSrcBox->left > pSrcBox->right)
if (pSrcBox->left >= pSrcBox->right)
return; // no-op, but legal
srcOffset = pSrcBox->left;
srcLength = pSrcBox->right - pSrcBox->left;
regLength = pSrcBox->right - pSrcBox->left;
if (srcOffset >= srcBuffer.length())
return;
}
// Clamp copy region to prevent out-of-bounds access
regLength = std::min(regLength, srcBuffer.length() - srcOffset);
regLength = std::min(regLength, dstBuffer.length() - dstOffset);
EmitCs([
cDstSlice = dstBuffer.subSlice(DstX, srcLength),
cSrcSlice = srcBuffer.subSlice(srcOffset, srcLength)
cDstSlice = dstBuffer.subSlice(dstOffset, regLength),
cSrcSlice = srcBuffer.subSlice(srcOffset, regLength)
] (DxvkContext* ctx) {
ctx->copyBuffer(
cDstSlice.buffer(),
@ -351,13 +362,35 @@ namespace dxvk {
const VkImageSubresource dstSubresource = dstTextureInfo->GetSubresourceFromIndex(dstFormatInfo->aspectMask, DstSubresource);
const VkImageSubresource srcSubresource = srcTextureInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, SrcSubresource);
VkOffset3D srcOffset = { 0, 0, 0 };
VkOffset3D dstOffset = {
static_cast<int32_t>(DstX),
static_cast<int32_t>(DstY),
static_cast<int32_t>(DstZ) };
// Copies are only supported on size-compatible formats
if (dstFormatInfo->elementSize != srcFormatInfo->elementSize) {
Logger::err(str::format(
"D3D11: CopySubresourceRegion: Incompatible texel size"
"\n Dst texel size: ", dstFormatInfo->elementSize,
"\n Src texel size: ", srcFormatInfo->elementSize));
return;
}
VkExtent3D extent = srcImage->mipLevelExtent(srcSubresource.mipLevel);
// Copies are only supported if the sample count matches
if (dstImage->info().sampleCount != srcImage->info().sampleCount) {
Logger::err(str::format(
"D3D11: CopySubresourceRegion: Incompatible sample count",
"\n Dst sample count: ", dstImage->info().sampleCount,
"\n Src sample count: ", srcImage->info().sampleCount));
return;
}
VkOffset3D srcOffset = { 0, 0, 0 };
VkOffset3D dstOffset = { int32_t(DstX), int32_t(DstY), int32_t(DstZ) };
VkExtent3D srcExtent = srcImage->mipLevelExtent(srcSubresource.mipLevel);
VkExtent3D dstExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel);
VkExtent3D regExtent = srcExtent;
if (uint32_t(dstOffset.x) >= dstExtent.width
|| uint32_t(dstOffset.y) >= dstExtent.height
|| uint32_t(dstOffset.z) >= dstExtent.depth)
return;
if (pSrcBox != nullptr) {
if (pSrcBox->left >= pSrcBox->right
@ -369,9 +402,14 @@ namespace dxvk {
srcOffset.y = pSrcBox->top;
srcOffset.z = pSrcBox->front;
extent.width = pSrcBox->right - pSrcBox->left;
extent.height = pSrcBox->bottom - pSrcBox->top;
extent.depth = pSrcBox->back - pSrcBox->front;
regExtent.width = pSrcBox->right - pSrcBox->left;
regExtent.height = pSrcBox->bottom - pSrcBox->top;
regExtent.depth = pSrcBox->back - pSrcBox->front;
if (uint32_t(srcOffset.x) >= srcExtent.width
|| uint32_t(srcOffset.y) >= srcExtent.height
|| uint32_t(srcOffset.z) >= srcExtent.depth)
return;
}
VkImageSubresourceLayers dstLayers = {
@ -387,11 +425,36 @@ namespace dxvk {
// Copying multiple slices does not
// seem to be supported in D3D11
if (copy2Dto3D || copy3Dto2D) {
extent.depth = 1;
regExtent.depth = 1;
dstLayers.layerCount = 1;
srcLayers.layerCount = 1;
}
// Don't perform the copy if the offsets aren't aligned
if (!util::isBlockAligned(srcOffset, srcFormatInfo->blockSize)
|| !util::isBlockAligned(dstOffset, dstFormatInfo->blockSize)) {
Logger::err("D3D11: CopySubresourceRegion: Unaligned block offset");
return;
}
// Clamp the image region in order to avoid out-of-bounds access
VkExtent3D regBlockCount = util::computeBlockCount(regExtent, srcFormatInfo->blockSize);
VkExtent3D dstBlockCount = util::computeMaxBlockCount(dstOffset, dstExtent, dstFormatInfo->blockSize);
VkExtent3D srcBlockCount = util::computeMaxBlockCount(srcOffset, srcExtent, srcFormatInfo->blockSize);
regBlockCount = util::minExtent3D(regBlockCount, dstBlockCount);
regBlockCount = util::minExtent3D(regBlockCount, srcBlockCount);
regExtent = util::minExtent3D(regExtent, util::computeBlockExtent(regBlockCount, srcFormatInfo->blockSize));
// Don't perform the copy if the image extent is not aligned and
// if it does not touch the image border for unaligned dimensons
if (!util::isBlockAligned(srcOffset, regExtent, srcFormatInfo->blockSize, srcExtent)
|| !util::isBlockAligned(dstOffset, regExtent, dstFormatInfo->blockSize, dstExtent)) {
Logger::err("D3D11: CopySubresourceRegion: Unaligned block size");
return;
}
EmitCs([
cDstImage = dstImage,
cSrcImage = srcImage,
@ -399,7 +462,7 @@ namespace dxvk {
cSrcLayers = srcLayers,
cDstOffset = dstOffset,
cSrcOffset = srcOffset,
cExtent = extent
cExtent = regExtent
] (DxvkContext* ctx) {
ctx->copyImage(
cDstImage, cDstLayers, cDstOffset,
@ -470,12 +533,36 @@ namespace dxvk {
const DxvkFormatInfo* dstFormatInfo = imageFormatInfo(dstImage->info().format);
const DxvkFormatInfo* srcFormatInfo = imageFormatInfo(srcImage->info().format);
// Copies are only supported on size-compatible formats
if (dstFormatInfo->elementSize != srcFormatInfo->elementSize) {
Logger::err(str::format(
"D3D11: CopyResource: Incompatible texel size"
"\n Dst texel size: ", dstFormatInfo->elementSize,
"\n Src texel size: ", srcFormatInfo->elementSize));
return;
}
// Layer count, mip level count, and sample count must match
if (srcImage->info().numLayers != dstImage->info().numLayers
|| srcImage->info().mipLevels != dstImage->info().mipLevels
|| srcImage->info().sampleCount != dstImage->info().sampleCount) {
Logger::err(str::format(
"D3D11: CopyResource: Incompatible images"
"\n Dst: (", dstImage->info().numLayers,
",", dstImage->info().mipLevels,
",", dstImage->info().sampleCount, ")",
"\n Src: (", srcImage->info().numLayers,
",", srcImage->info().mipLevels,
",", srcImage->info().sampleCount, ")"));
return;
}
for (uint32_t i = 0; i < srcImage->info().mipLevels; i++) {
VkImageSubresourceLayers dstLayers = { dstFormatInfo->aspectMask, i, 0, dstImage->info().numLayers };
VkImageSubresourceLayers srcLayers = { srcFormatInfo->aspectMask, i, 0, srcImage->info().numLayers };
VkExtent3D extent = srcImage->mipLevelExtent(i);
const VkImageSubresourceLayers dstLayers = { dstFormatInfo->aspectMask, i, 0, dstImage->info().numLayers };
const VkImageSubresourceLayers srcLayers = { srcFormatInfo->aspectMask, i, 0, srcImage->info().numLayers };
EmitCs([
cDstImage = dstImage,

View File

@ -39,20 +39,115 @@ namespace dxvk::util {
VkDeviceSize pitchPerRow,
VkDeviceSize pitchPerLayer);
/**
* \brief Computes minimum extent
*
* \param [in] a First value
* \param [in] b Second value
* \returns Component-wise \c min
*/
inline VkExtent3D minExtent3D(VkExtent3D a, VkExtent3D b) {
return VkExtent3D {
std::min(a.width, b.width),
std::min(a.height, b.height),
std::min(a.depth, b.depth) };
}
/**
* \brief Checks whether an offset is block-aligned
*
* An offset is considered block-aligned if it is
* a multiple of the block size. Only non-negative
* offset values are valid.
* \param [in] offset The offset to check
* \param [in] blockSize Block size
* \returns \c true if \c offset is aligned
*/
inline bool isBlockAligned(VkOffset3D offset, VkExtent3D blockSize) {
return (offset.x % blockSize.width == 0)
&& (offset.y % blockSize.height == 0)
&& (offset.z % blockSize.depth == 0);
}
/**
* \brief Checks whether an extent is block-aligned
*
* A block-aligned extent can be used for image copy
* operations that involve block-compressed images.
* \param [in] offset The base offset
* \param [in] extent The extent to check
* \param [in] blockSize Compressed block size
* \param [in] imageSize Image size
* \returns \c true if all components of \c extent
* are aligned or touch the image border.
*/
inline bool isBlockAligned(VkOffset3D offset, VkExtent3D extent, VkExtent3D blockSize, VkExtent3D imageSize) {
return ((extent.width % blockSize.width == 0) || (uint32_t(offset.x + extent.width) == imageSize.width))
&& ((extent.height % blockSize.height == 0) || (uint32_t(offset.y + extent.height) == imageSize.height))
&& ((extent.depth % blockSize.depth == 0) || (uint32_t(offset.z + extent.depth) == imageSize.depth));
}
/**
* \brief Computes block offset for compressed images
*
* Convenience function to compute the block position
* within a compressed image based on the block size.
* \param [in] offset The offset
* \param [in] blockSize Size of a pixel block
* \returns The block offset
*/
inline VkOffset3D computeBlockOffset(VkOffset3D offset, VkExtent3D blockSize) {
return VkOffset3D {
offset.x / int32_t(blockSize.width),
offset.y / int32_t(blockSize.height),
offset.z / int32_t(blockSize.depth) };
}
/**
* \brief Computes block count for compressed images
*
* Convenience function to compute the size, in
* blocks, of compressed images subresources.
* \param [in] imageSize The image size
* \param [in] blockSize Size per pixel block
* \param [in] extent The image size
* \param [in] blockSize Size of a pixel block
* \returns Number of blocks in the image
*/
inline VkExtent3D computeBlockCount(VkExtent3D imageSize, VkExtent3D blockSize) {
inline VkExtent3D computeBlockCount(VkExtent3D extent, VkExtent3D blockSize) {
return VkExtent3D {
(imageSize.width + blockSize.width - 1) / blockSize.width,
(imageSize.height + blockSize.height - 1) / blockSize.height,
(imageSize.depth + blockSize.depth - 1) / blockSize.depth };
(extent.width + blockSize.width - 1) / blockSize.width,
(extent.height + blockSize.height - 1) / blockSize.height,
(extent.depth + blockSize.depth - 1) / blockSize.depth };
}
/**
* \brief Computes block count for compressed images
*
* Given an aligned offset, this computes
* Convenience function to compute the size, in
* blocks, of compressed images subresources.
* \param [in] extent The image size
* \param [in] blockSize Size of a pixel block
* \returns Number of blocks in the image
*/
inline VkExtent3D computeMaxBlockCount(VkOffset3D offset, VkExtent3D extent, VkExtent3D blockSize) {
return VkExtent3D {
(extent.width + blockSize.width - offset.x - 1) / blockSize.width,
(extent.height + blockSize.height - offset.y - 1) / blockSize.height,
(extent.depth + blockSize.depth - offset.z - 1) / blockSize.depth };
}
/**
* \brief Computes block extent for compressed images
*
* \param [in] blockCount The number of blocks
* \param [in] blockSize Size of a pixel block
* \returns Extent of the given blocks
*/
inline VkExtent3D computeBlockExtent(VkExtent3D blockCount, VkExtent3D blockSize) {
return VkExtent3D {
blockCount.width * blockSize.width,
blockCount.height * blockSize.height,
blockCount.depth * blockSize.depth };
}
/**