mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-19 05:52:11 +01:00
[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:
parent
50f1cf007d
commit
52f0d853c0
@ -316,20 +316,31 @@ namespace dxvk {
|
|||||||
auto dstBuffer = static_cast<D3D11Buffer*>(pDstResource)->GetBufferSlice();
|
auto dstBuffer = static_cast<D3D11Buffer*>(pDstResource)->GetBufferSlice();
|
||||||
auto srcBuffer = static_cast<D3D11Buffer*>(pSrcResource)->GetBufferSlice();
|
auto srcBuffer = static_cast<D3D11Buffer*>(pSrcResource)->GetBufferSlice();
|
||||||
|
|
||||||
|
VkDeviceSize dstOffset = DstX;
|
||||||
VkDeviceSize srcOffset = 0;
|
VkDeviceSize srcOffset = 0;
|
||||||
VkDeviceSize srcLength = srcBuffer.length();
|
VkDeviceSize regLength = srcBuffer.length();
|
||||||
|
|
||||||
|
if (dstOffset >= dstBuffer.length())
|
||||||
|
return;
|
||||||
|
|
||||||
if (pSrcBox != nullptr) {
|
if (pSrcBox != nullptr) {
|
||||||
if (pSrcBox->left > pSrcBox->right)
|
if (pSrcBox->left >= pSrcBox->right)
|
||||||
return; // no-op, but legal
|
return; // no-op, but legal
|
||||||
|
|
||||||
srcOffset = pSrcBox->left;
|
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([
|
EmitCs([
|
||||||
cDstSlice = dstBuffer.subSlice(DstX, srcLength),
|
cDstSlice = dstBuffer.subSlice(dstOffset, regLength),
|
||||||
cSrcSlice = srcBuffer.subSlice(srcOffset, srcLength)
|
cSrcSlice = srcBuffer.subSlice(srcOffset, regLength)
|
||||||
] (DxvkContext* ctx) {
|
] (DxvkContext* ctx) {
|
||||||
ctx->copyBuffer(
|
ctx->copyBuffer(
|
||||||
cDstSlice.buffer(),
|
cDstSlice.buffer(),
|
||||||
@ -351,13 +362,35 @@ namespace dxvk {
|
|||||||
const VkImageSubresource dstSubresource = dstTextureInfo->GetSubresourceFromIndex(dstFormatInfo->aspectMask, DstSubresource);
|
const VkImageSubresource dstSubresource = dstTextureInfo->GetSubresourceFromIndex(dstFormatInfo->aspectMask, DstSubresource);
|
||||||
const VkImageSubresource srcSubresource = srcTextureInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, SrcSubresource);
|
const VkImageSubresource srcSubresource = srcTextureInfo->GetSubresourceFromIndex(srcFormatInfo->aspectMask, SrcSubresource);
|
||||||
|
|
||||||
VkOffset3D srcOffset = { 0, 0, 0 };
|
// Copies are only supported on size-compatible formats
|
||||||
VkOffset3D dstOffset = {
|
if (dstFormatInfo->elementSize != srcFormatInfo->elementSize) {
|
||||||
static_cast<int32_t>(DstX),
|
Logger::err(str::format(
|
||||||
static_cast<int32_t>(DstY),
|
"D3D11: CopySubresourceRegion: Incompatible texel size"
|
||||||
static_cast<int32_t>(DstZ) };
|
"\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 != nullptr) {
|
||||||
if (pSrcBox->left >= pSrcBox->right
|
if (pSrcBox->left >= pSrcBox->right
|
||||||
@ -369,9 +402,14 @@ namespace dxvk {
|
|||||||
srcOffset.y = pSrcBox->top;
|
srcOffset.y = pSrcBox->top;
|
||||||
srcOffset.z = pSrcBox->front;
|
srcOffset.z = pSrcBox->front;
|
||||||
|
|
||||||
extent.width = pSrcBox->right - pSrcBox->left;
|
regExtent.width = pSrcBox->right - pSrcBox->left;
|
||||||
extent.height = pSrcBox->bottom - pSrcBox->top;
|
regExtent.height = pSrcBox->bottom - pSrcBox->top;
|
||||||
extent.depth = pSrcBox->back - pSrcBox->front;
|
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 = {
|
VkImageSubresourceLayers dstLayers = {
|
||||||
@ -387,11 +425,36 @@ namespace dxvk {
|
|||||||
// Copying multiple slices does not
|
// Copying multiple slices does not
|
||||||
// seem to be supported in D3D11
|
// seem to be supported in D3D11
|
||||||
if (copy2Dto3D || copy3Dto2D) {
|
if (copy2Dto3D || copy3Dto2D) {
|
||||||
extent.depth = 1;
|
regExtent.depth = 1;
|
||||||
dstLayers.layerCount = 1;
|
dstLayers.layerCount = 1;
|
||||||
srcLayers.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([
|
EmitCs([
|
||||||
cDstImage = dstImage,
|
cDstImage = dstImage,
|
||||||
cSrcImage = srcImage,
|
cSrcImage = srcImage,
|
||||||
@ -399,7 +462,7 @@ namespace dxvk {
|
|||||||
cSrcLayers = srcLayers,
|
cSrcLayers = srcLayers,
|
||||||
cDstOffset = dstOffset,
|
cDstOffset = dstOffset,
|
||||||
cSrcOffset = srcOffset,
|
cSrcOffset = srcOffset,
|
||||||
cExtent = extent
|
cExtent = regExtent
|
||||||
] (DxvkContext* ctx) {
|
] (DxvkContext* ctx) {
|
||||||
ctx->copyImage(
|
ctx->copyImage(
|
||||||
cDstImage, cDstLayers, cDstOffset,
|
cDstImage, cDstLayers, cDstOffset,
|
||||||
@ -471,11 +534,35 @@ namespace dxvk {
|
|||||||
const DxvkFormatInfo* dstFormatInfo = imageFormatInfo(dstImage->info().format);
|
const DxvkFormatInfo* dstFormatInfo = imageFormatInfo(dstImage->info().format);
|
||||||
const DxvkFormatInfo* srcFormatInfo = imageFormatInfo(srcImage->info().format);
|
const DxvkFormatInfo* srcFormatInfo = imageFormatInfo(srcImage->info().format);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < srcImage->info().mipLevels; i++) {
|
// Copies are only supported on size-compatible formats
|
||||||
VkExtent3D extent = srcImage->mipLevelExtent(i);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
const VkImageSubresourceLayers dstLayers = { dstFormatInfo->aspectMask, i, 0, dstImage->info().numLayers };
|
// Layer count, mip level count, and sample count must match
|
||||||
const VkImageSubresourceLayers srcLayers = { srcFormatInfo->aspectMask, i, 0, srcImage->info().numLayers };
|
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);
|
||||||
|
|
||||||
EmitCs([
|
EmitCs([
|
||||||
cDstImage = dstImage,
|
cDstImage = dstImage,
|
||||||
|
@ -39,20 +39,115 @@ namespace dxvk::util {
|
|||||||
VkDeviceSize pitchPerRow,
|
VkDeviceSize pitchPerRow,
|
||||||
VkDeviceSize pitchPerLayer);
|
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
|
* \brief Computes block count for compressed images
|
||||||
*
|
*
|
||||||
* Convenience function to compute the size, in
|
* Convenience function to compute the size, in
|
||||||
* blocks, of compressed images subresources.
|
* blocks, of compressed images subresources.
|
||||||
* \param [in] imageSize The image size
|
* \param [in] extent The image size
|
||||||
* \param [in] blockSize Size per pixel block
|
* \param [in] blockSize Size of a pixel block
|
||||||
* \returns Number of blocks in the image
|
* \returns Number of blocks in the image
|
||||||
*/
|
*/
|
||||||
inline VkExtent3D computeBlockCount(VkExtent3D imageSize, VkExtent3D blockSize) {
|
inline VkExtent3D computeBlockCount(VkExtent3D extent, VkExtent3D blockSize) {
|
||||||
return VkExtent3D {
|
return VkExtent3D {
|
||||||
(imageSize.width + blockSize.width - 1) / blockSize.width,
|
(extent.width + blockSize.width - 1) / blockSize.width,
|
||||||
(imageSize.height + blockSize.height - 1) / blockSize.height,
|
(extent.height + blockSize.height - 1) / blockSize.height,
|
||||||
(imageSize.depth + blockSize.depth - 1) / blockSize.depth };
|
(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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user