Fix computation of mipmap sizes used in validation

The width and height of the mipmaps level > 0 weren't computed
correctly. Size of mip level of a texture should be greater than
0.

BUG=dawn:176

TEST=dawn_unittests --gtest_filter=CopyCommandTest*.CopyNonSquareTexture

Change-Id: I730aef3fd8c036567f824cd707d1ea1a69b132c0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8242
Commit-Queue: Yizhou Jiang <yizhou.jiang@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Yizhou Jiang 2019-06-25 03:09:26 +00:00 committed by Commit Bot service account
parent 6fa398e682
commit 41c24ee4eb
5 changed files with 221 additions and 132 deletions

View File

@ -39,9 +39,10 @@ namespace dawn_native {
bool IsCompleteSubresourceCopiedTo(const TextureBase* texture, bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
const Extent3D copySize, const Extent3D copySize,
const uint32_t mipLevel) { const uint32_t mipLevel) {
if (texture->GetSize().depth == copySize.depth && Extent3D extent = texture->GetMipLevelSize(mipLevel);
(texture->GetSize().width >> mipLevel) == copySize.width &&
(texture->GetSize().height >> mipLevel) == copySize.height) { if (extent.depth == copySize.depth && extent.width == copySize.width &&
extent.height == copySize.height) {
return true; return true;
} }
return false; return false;

View File

@ -47,24 +47,12 @@ namespace dawn_native {
// overflows. // overflows.
uint64_t level = textureCopy.level; uint64_t level = textureCopy.level;
uint32_t widthAtLevel = texture->GetSize().width >> level; Extent3D extent = texture->GetMipLevelSize(level);
uint32_t heightAtLevel = texture->GetSize().height >> level;
// Compressed Textures will have paddings if their width or height is not a multiple of
// 4 at non-zero mipmap levels.
const Format& textureFormat = texture->GetFormat();
if (textureFormat.isCompressed) {
// TODO(jiawei.shao@intel.com): check if there are any overflows.
uint32_t blockWidth = textureFormat.blockWidth;
uint32_t blockHeight = textureFormat.blockHeight;
widthAtLevel = (widthAtLevel + blockWidth - 1) / blockWidth * blockWidth;
heightAtLevel = (heightAtLevel + blockHeight - 1) / blockHeight * blockHeight;
}
if (uint64_t(textureCopy.origin.x) + uint64_t(copySize.width) > if (uint64_t(textureCopy.origin.x) + uint64_t(copySize.width) >
static_cast<uint64_t>(widthAtLevel) || static_cast<uint64_t>(extent.width) ||
uint64_t(textureCopy.origin.y) + uint64_t(copySize.height) > uint64_t(textureCopy.origin.y) + uint64_t(copySize.height) >
static_cast<uint64_t>(heightAtLevel)) { static_cast<uint64_t>(extent.height)) {
return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture"); return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture");
} }

View File

@ -497,6 +497,25 @@ namespace dawn_native {
return mSampleCount > 1; return mSampleCount > 1;
} }
Extent3D TextureBase::GetMipLevelSize(uint64_t level) const {
Extent3D extent;
extent.width = std::max(mSize.width >> level, 1u);
extent.height = std::max(mSize.height >> level, 1u);
extent.depth = std::max(mSize.depth >> level, 1u);
// Compressed Textures will have paddings if their width or height is not a multiple of
// 4 at non-zero mipmap levels.
if (mFormat.isCompressed) {
// TODO(jiawei.shao@intel.com): check if there are any overflows.
uint32_t blockWidth = mFormat.blockWidth;
uint32_t blockHeight = mFormat.blockHeight;
extent.width = (extent.width + blockWidth - 1) / blockWidth * blockWidth;
extent.height = (extent.height + blockHeight - 1) / blockHeight * blockHeight;
}
return extent;
}
TextureViewBase* TextureBase::CreateDefaultView() { TextureViewBase* TextureBase::CreateDefaultView() {
TextureViewDescriptor descriptor = {}; TextureViewDescriptor descriptor = {};

View File

@ -99,6 +99,8 @@ namespace dawn_native {
bool IsMultisampledTexture() const; bool IsMultisampledTexture() const;
Extent3D GetMipLevelSize(uint64_t level) const;
// Dawn API // Dawn API
TextureViewBase* CreateDefaultView(); TextureViewBase* CreateDefaultView();
TextureViewBase* CreateView(const TextureViewDescriptor* descriptor); TextureViewBase* CreateView(const TextureViewDescriptor* descriptor);

View File

@ -19,129 +19,132 @@
#include "utils/DawnHelpers.h" #include "utils/DawnHelpers.h"
class CopyCommandTest : public ValidationTest { class CopyCommandTest : public ValidationTest {
protected: protected:
dawn::Buffer CreateBuffer(uint64_t size, dawn::BufferUsageBit usage) { dawn::Buffer CreateBuffer(uint64_t size, dawn::BufferUsageBit usage) {
dawn::BufferDescriptor descriptor; dawn::BufferDescriptor descriptor;
descriptor.size = size; descriptor.size = size;
descriptor.usage = usage; descriptor.usage = usage;
return device.CreateBuffer(&descriptor); return device.CreateBuffer(&descriptor);
}
dawn::Texture Create2DTexture(uint32_t width,
uint32_t height,
uint32_t mipLevelCount,
uint32_t arrayLayerCount,
dawn::TextureFormat format,
dawn::TextureUsageBit usage,
uint32_t sampleCount = 1) {
dawn::TextureDescriptor descriptor;
descriptor.dimension = dawn::TextureDimension::e2D;
descriptor.size.width = width;
descriptor.size.height = height;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = arrayLayerCount;
descriptor.sampleCount = sampleCount;
descriptor.format = format;
descriptor.mipLevelCount = mipLevelCount;
descriptor.usage = usage;
dawn::Texture tex = device.CreateTexture(&descriptor);
return tex;
}
// TODO(jiawei.shao@intel.com): support more pixel formats
uint32_t TextureFormatPixelSize(dawn::TextureFormat format) {
switch (format) {
case dawn::TextureFormat::RG8Unorm:
return 2;
case dawn::TextureFormat::RGBA8Unorm:
return 4;
default:
UNREACHABLE();
return 0;
} }
}
dawn::Texture Create2DTexture(uint32_t width, uint32_t height, uint32_t mipLevelCount, uint32_t BufferSizeForTextureCopy(
uint32_t arrayLayerCount, dawn::TextureFormat format, uint32_t width,
dawn::TextureUsageBit usage, uint32_t sampleCount = 1) { uint32_t height,
dawn::TextureDescriptor descriptor; uint32_t depth,
descriptor.dimension = dawn::TextureDimension::e2D; dawn::TextureFormat format = dawn::TextureFormat::RGBA8Unorm) {
descriptor.size.width = width; uint32_t bytesPerPixel = TextureFormatPixelSize(format);
descriptor.size.height = height; uint32_t rowPitch = Align(width * bytesPerPixel, kTextureRowPitchAlignment);
descriptor.size.depth = 1; return (rowPitch * (height - 1) + width * bytesPerPixel) * depth;
descriptor.arrayLayerCount = arrayLayerCount; }
descriptor.sampleCount = sampleCount;
descriptor.format = format; void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) {
descriptor.mipLevelCount = mipLevelCount; if (expectation == utils::Expectation::Success) {
descriptor.usage = usage; encoder.Finish();
dawn::Texture tex = device.CreateTexture(&descriptor); } else {
return tex; ASSERT_DEVICE_ERROR(encoder.Finish());
} }
}
// TODO(jiawei.shao@intel.com): support more pixel formats void TestB2TCopy(utils::Expectation expectation,
uint32_t TextureFormatPixelSize(dawn::TextureFormat format) { dawn::Buffer srcBuffer,
switch (format) { uint64_t srcOffset,
case dawn::TextureFormat::RG8Unorm: uint32_t srcRowPitch,
return 2; uint32_t srcImageHeight,
case dawn::TextureFormat::RGBA8Unorm: dawn::Texture destTexture,
return 4; uint32_t destLevel,
default: uint32_t destSlice,
UNREACHABLE(); dawn::Origin3D destOrigin,
return 0; dawn::Extent3D extent3D) {
} dawn::BufferCopyView bufferCopyView =
} utils::CreateBufferCopyView(srcBuffer, srcOffset, srcRowPitch, srcImageHeight);
dawn::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(destTexture, destLevel, destSlice, destOrigin);
uint32_t BufferSizeForTextureCopy( dawn::CommandEncoder encoder = device.CreateCommandEncoder();
uint32_t width, encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D);
uint32_t height,
uint32_t depth,
dawn::TextureFormat format = dawn::TextureFormat::RGBA8Unorm) {
uint32_t bytesPerPixel = TextureFormatPixelSize(format);
uint32_t rowPitch = Align(width * bytesPerPixel, kTextureRowPitchAlignment);
return (rowPitch * (height - 1) + width * bytesPerPixel) * depth;
}
void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) { ValidateExpectation(encoder, expectation);
if (expectation == utils::Expectation::Success) { }
encoder.Finish();
} else {
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
void TestB2TCopy(utils::Expectation expectation, void TestT2BCopy(utils::Expectation expectation,
dawn::Buffer srcBuffer, dawn::Texture srcTexture,
uint64_t srcOffset, uint32_t srcLevel,
uint32_t srcRowPitch, uint32_t srcSlice,
uint32_t srcImageHeight, dawn::Origin3D srcOrigin,
dawn::Texture destTexture, dawn::Buffer destBuffer,
uint32_t destLevel, uint64_t destOffset,
uint32_t destSlice, uint32_t destRowPitch,
dawn::Origin3D destOrigin, uint32_t destImageHeight,
dawn::Extent3D extent3D) { dawn::Extent3D extent3D) {
dawn::BufferCopyView bufferCopyView = dawn::BufferCopyView bufferCopyView =
utils::CreateBufferCopyView(srcBuffer, srcOffset, srcRowPitch, srcImageHeight); utils::CreateBufferCopyView(destBuffer, destOffset, destRowPitch, destImageHeight);
dawn::TextureCopyView textureCopyView = dawn::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(destTexture, destLevel, destSlice, destOrigin); utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin);
dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D); encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D);
ValidateExpectation(encoder, expectation); ValidateExpectation(encoder, expectation);
} }
void TestT2BCopy(utils::Expectation expectation, void TestT2TCopy(utils::Expectation expectation,
dawn::Texture srcTexture, dawn::Texture srcTexture,
uint32_t srcLevel, uint32_t srcLevel,
uint32_t srcSlice, uint32_t srcSlice,
dawn::Origin3D srcOrigin, dawn::Origin3D srcOrigin,
dawn::Buffer destBuffer, dawn::Texture dstTexture,
uint64_t destOffset, uint32_t dstLevel,
uint32_t destRowPitch, uint32_t dstSlice,
uint32_t destImageHeight, dawn::Origin3D dstOrigin,
dawn::Extent3D extent3D) { dawn::Extent3D extent3D) {
dawn::BufferCopyView bufferCopyView = dawn::TextureCopyView srcTextureCopyView =
utils::CreateBufferCopyView(destBuffer, destOffset, destRowPitch, destImageHeight); utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin);
dawn::TextureCopyView textureCopyView = dawn::TextureCopyView dstTextureCopyView =
utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin); utils::CreateTextureCopyView(dstTexture, dstLevel, dstSlice, dstOrigin);
dawn::CommandEncoder encoder = device.CreateCommandEncoder(); dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D); encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, &extent3D);
ValidateExpectation(encoder, expectation); ValidateExpectation(encoder, expectation);
} }
void TestT2TCopy(utils::Expectation expectation,
dawn::Texture srcTexture,
uint32_t srcLevel,
uint32_t srcSlice,
dawn::Origin3D srcOrigin,
dawn::Texture dstTexture,
uint32_t dstLevel,
uint32_t dstSlice,
dawn::Origin3D dstOrigin,
dawn::Extent3D extent3D) {
dawn::TextureCopyView srcTextureCopyView =
utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin);
dawn::TextureCopyView dstTextureCopyView =
utils::CreateTextureCopyView(dstTexture, dstLevel, dstSlice, dstOrigin);
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, &extent3D);
ValidateExpectation(encoder, expectation);
}
}; };
class CopyCommandTest_B2B : public CopyCommandTest { class CopyCommandTest_B2B : public CopyCommandTest {};
};
// TODO(cwallez@chromium.org): Test that copies are forbidden inside renderpasses // TODO(cwallez@chromium.org): Test that copies are forbidden inside renderpasses
@ -263,8 +266,7 @@ TEST_F(CopyCommandTest_B2B, BuffersInErrorState) {
} }
} }
class CopyCommandTest_B2T : public CopyCommandTest { class CopyCommandTest_B2T : public CopyCommandTest {};
};
// Test a successfull B2T copy // Test a successfull B2T copy
TEST_F(CopyCommandTest_B2T, Success) { TEST_F(CopyCommandTest_B2T, Success) {
@ -344,7 +346,8 @@ TEST_F(CopyCommandTest_B2T, OutOfBoundsOnBuffer) {
{ {
uint32_t sourceBufferSize = BufferSizeForTextureCopy(7, 3, 1); uint32_t sourceBufferSize = BufferSizeForTextureCopy(7, 3, 1);
ASSERT_TRUE(256 * 3 > sourceBufferSize) << "row pitch * height should overflow buffer"; ASSERT_TRUE(256 * 3 > sourceBufferSize) << "row pitch * height should overflow buffer";
dawn::Buffer sourceBuffer = CreateBuffer(sourceBufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Buffer sourceBuffer =
CreateBuffer(sourceBufferSize, dawn::BufferUsageBit::TransferSrc);
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0},
{7, 3, 1}); {7, 3, 1});
@ -577,8 +580,33 @@ TEST_F(CopyCommandTest_B2T, TextureCopyBufferSizeLastRowComputation) {
} }
} }
class CopyCommandTest_T2B : public CopyCommandTest { // Test copy from buffer to mip map of non square texture
}; TEST_F(CopyCommandTest_B2T, CopyToMipmapOfNonSquareTexture) {
uint64_t bufferSize = BufferSizeForTextureCopy(4, 2, 1);
dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc);
uint32_t maxMipmapLevel = 3;
dawn::Texture destination =
Create2DTexture(4, 2, maxMipmapLevel, 1, dawn::TextureFormat::RGBA8Unorm,
dawn::TextureUsageBit::TransferDst);
// Copy to top level mip map
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, maxMipmapLevel - 1, 0,
{0, 0, 0}, {1, 1, 1});
// Copy to high level mip map
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, maxMipmapLevel - 2, 0,
{0, 0, 0}, {2, 1, 1});
// Mip level out of range
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, maxMipmapLevel, 0,
{0, 0, 0}, {1, 1, 1});
// Copy origin out of range
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, maxMipmapLevel - 2, 0,
{1, 0, 0}, {2, 1, 1});
// Copy size out of range
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, maxMipmapLevel - 2, 0,
{0, 0, 0}, {2, 2, 1});
}
class CopyCommandTest_T2B : public CopyCommandTest {};
// Test a successfull T2B copy // Test a successfull T2B copy
TEST_F(CopyCommandTest_T2B, Success) { TEST_F(CopyCommandTest_T2B, Success) {
@ -682,7 +710,8 @@ TEST_F(CopyCommandTest_T2B, OutOfBoundsOnBuffer) {
{ {
uint32_t destinationBufferSize = BufferSizeForTextureCopy(7, 3, 1); uint32_t destinationBufferSize = BufferSizeForTextureCopy(7, 3, 1);
ASSERT_TRUE(256 * 3 > destinationBufferSize) << "row pitch * height should overflow buffer"; ASSERT_TRUE(256 * 3 > destinationBufferSize) << "row pitch * height should overflow buffer";
dawn::Buffer destinationBuffer = CreateBuffer(destinationBufferSize, dawn::BufferUsageBit::TransferDst); dawn::Buffer destinationBuffer =
CreateBuffer(destinationBufferSize, dawn::BufferUsageBit::TransferDst);
TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destinationBuffer, 0, 256, TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destinationBuffer, 0, 256,
0, {7, 3, 1}); 0, {7, 3, 1});
} }
@ -886,6 +915,31 @@ TEST_F(CopyCommandTest_T2B, TextureCopyBufferSizeLastRowComputation) {
} }
} }
// Test copy from mip map of non square texture to buffer
TEST_F(CopyCommandTest_T2B, CopyFromMipmapOfNonSquareTexture) {
uint32_t maxMipmapLevel = 3;
dawn::Texture source = Create2DTexture(4, 2, maxMipmapLevel, 1, dawn::TextureFormat::RGBA8Unorm,
dawn::TextureUsageBit::TransferSrc);
uint64_t bufferSize = BufferSizeForTextureCopy(4, 2, 1);
dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst);
// Copy from top level mip map
TestT2BCopy(utils::Expectation::Success, source, maxMipmapLevel - 1, 0, {0, 0, 0}, destination,
0, 256, 0, {1, 1, 1});
// Copy from high level mip map
TestT2BCopy(utils::Expectation::Success, source, maxMipmapLevel - 2, 0, {0, 0, 0}, destination,
0, 256, 0, {2, 1, 1});
// Mip level out of range
TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel, 0, {0, 0, 0}, destination, 0,
256, 0, {2, 1, 1});
// Copy origin out of range
TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, 0, {2, 0, 0}, destination,
0, 256, 0, {2, 1, 1});
// Copy size out of range
TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, 0, {1, 0, 0}, destination,
0, 256, 0, {2, 1, 1});
}
class CopyCommandTest_T2T : public CopyCommandTest {}; class CopyCommandTest_T2T : public CopyCommandTest {};
TEST_F(CopyCommandTest_T2T, Success) { TEST_F(CopyCommandTest_T2T, Success) {
@ -1080,6 +1134,31 @@ TEST_F(CopyCommandTest_T2T, MultisampledCopies) {
} }
} }
// Test copy to mip map of non square textures
TEST_F(CopyCommandTest_T2T, CopyToMipmapOfNonSquareTexture) {
uint32_t maxMipmapLevel = 3;
dawn::Texture source = Create2DTexture(4, 2, maxMipmapLevel, 1, dawn::TextureFormat::RGBA8Unorm,
dawn::TextureUsageBit::TransferSrc);
dawn::Texture destination =
Create2DTexture(4, 2, maxMipmapLevel, 1, dawn::TextureFormat::RGBA8Unorm,
dawn::TextureUsageBit::TransferDst);
// Copy to top level mip map
TestT2TCopy(utils::Expectation::Success, source, maxMipmapLevel - 1, 0, {0, 0, 0}, destination,
maxMipmapLevel - 1, 0, {0, 0, 0}, {1, 1, 1});
// Copy to high level mip map
TestT2TCopy(utils::Expectation::Success, source, maxMipmapLevel - 2, 0, {0, 0, 0}, destination,
maxMipmapLevel - 2, 0, {0, 0, 0}, {2, 1, 1});
// Mip level out of range
TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel, 0, {0, 0, 0}, destination,
maxMipmapLevel, 0, {0, 0, 0}, {2, 1, 1});
// Copy origin out of range
TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, 0, {2, 0, 0}, destination,
maxMipmapLevel - 2, 0, {2, 0, 0}, {2, 1, 1});
// Copy size out of range
TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, 0, {1, 0, 0}, destination,
maxMipmapLevel - 2, 0, {0, 0, 0}, {2, 1, 1});
}
class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest { class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest {
protected: protected:
dawn::Texture Create2DTexture(dawn::TextureFormat format, dawn::Texture Create2DTexture(dawn::TextureFormat format,