Support BC5 formats on Vulkan

This patch supports BC5 formats on Vulkan backends and adds related
Dawn end2end tests.

For the textures with BC formats, they could have non-multiple-of-4
sizes on the non-zero mipmap levels in sampling, but we are still
required to provide texture data in complete 4x4 blocks in texture
copies because that is the size of which they are stored in GPU memory.

In this patch, we refer the term "physical memory size" as the memory
size of the texture subresource in GPU memory, and the term "virtual
memory size" as the size used in texture sampling. As Dawn requires
the Extent3D in texture copies must fit in the physical memory size,
while Vulkan requires it must fit in the virtual memory size, this
patch recalculates the imageExtent to ensure it always follow this
Vulkan validation rules.

For Dawn end2end tests, note that we use pure green and pure red for
the textures because BC5 does not support SRGB formats. Furthermore,
"CopyPartofTextureSubResourceIntoNonZeroMipmapLevel" is skipped in
this patch because there is an issue on the T2T copies from a region
within the virtual size of one texture to another one that exceeds
the virtual size of another texture in Vulkan SPEC.

BUG=dawn:42
TEST=dawn_end2end_tests

Change-Id: I17518cd335fb13125cb753bbf879bc06eb20e426
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8260
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
Jiawei Shao 2019-07-02 23:55:55 +00:00 committed by Commit Bot service account
parent c246494dbf
commit 72508d6d06
11 changed files with 616 additions and 15 deletions

View File

@ -657,6 +657,7 @@ test("dawn_end2end_tests") {
"src/tests/end2end/BufferTests.cpp",
"src/tests/end2end/ClipSpaceTests.cpp",
"src/tests/end2end/ColorStateTests.cpp",
"src/tests/end2end/CompressedTextureFormatTests.cpp",
"src/tests/end2end/ComputeCopyStorageBufferTests.cpp",
"src/tests/end2end/ComputeIndirectTests.cpp",
"src/tests/end2end/ComputeSharedMemoryTests.cpp",

View File

@ -39,7 +39,7 @@ namespace dawn_native {
bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
const Extent3D copySize,
const uint32_t mipLevel) {
Extent3D extent = texture->GetMipLevelSize(mipLevel);
Extent3D extent = texture->GetMipLevelPhysicalSize(mipLevel);
if (extent.depth == copySize.depth && extent.width == copySize.width &&
extent.height == copySize.height) {

View File

@ -47,7 +47,7 @@ namespace dawn_native {
// overflows.
uint64_t level = textureCopy.level;
Extent3D extent = texture->GetMipLevelSize(level);
Extent3D extent = texture->GetMipLevelPhysicalSize(level);
if (uint64_t(textureCopy.origin.x) + uint64_t(copySize.width) >
static_cast<uint64_t>(extent.width) ||

View File

@ -543,11 +543,16 @@ namespace dawn_native {
return mSampleCount > 1;
}
Extent3D TextureBase::GetMipLevelSize(uint64_t level) const {
Extent3D TextureBase::GetMipLevelVirtualSize(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);
return extent;
}
Extent3D TextureBase::GetMipLevelPhysicalSize(uint64_t level) const {
Extent3D extent = GetMipLevelVirtualSize(level);
// Compressed Textures will have paddings if their width or height is not a multiple of
// 4 at non-zero mipmap levels.

View File

@ -99,7 +99,13 @@ namespace dawn_native {
bool IsMultisampledTexture() const;
Extent3D GetMipLevelSize(uint64_t level) const;
// For a texture with non-block-compressed texture format, its physical size is always equal
// to its virtual size. For a texture with block compressed texture format, the physical
// size is the one with paddings if necessary, which is always a multiple of the block size
// and used in texture copying. The virtual size is the one without paddings, which is not
// required to be a multiple of the block size and used in texture sampling.
Extent3D GetMipLevelPhysicalSize(uint64_t level) const;
Extent3D GetMipLevelVirtualSize(uint64_t level) const;
// Dawn API
TextureViewBase* CreateDefaultView();

View File

@ -41,6 +41,28 @@ namespace dawn_native { namespace vulkan {
}
}
// Vulkan SPEC requires the source/destination region specified by each element of
// pRegions must be a region that is contained within srcImage/dstImage. Here the size of
// the image refers to the virtual size, while Dawn validates texture copy extent with the
// physical size, so we need to re-calculate the texture copy extent to ensure it should fit
// in the virtual size of the subresource.
Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy,
const Extent3D& copySize) {
Extent3D validTextureCopyExtent = copySize;
const TextureBase* texture = textureCopy.texture.Get();
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(textureCopy.level);
if (textureCopy.origin.x + copySize.width > virtualSizeAtLevel.width) {
ASSERT(texture->GetFormat().isCompressed);
validTextureCopyExtent.width = virtualSizeAtLevel.width - textureCopy.origin.x;
}
if (textureCopy.origin.y + copySize.height > virtualSizeAtLevel.height) {
ASSERT(texture->GetFormat().isCompressed);
validTextureCopyExtent.height = virtualSizeAtLevel.height - textureCopy.origin.y;
}
return validTextureCopyExtent;
}
VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy,
const TextureCopy& textureCopy,
const Extent3D& copySize) {
@ -50,7 +72,9 @@ namespace dawn_native { namespace vulkan {
region.bufferOffset = bufferCopy.offset;
// In Vulkan the row length is in texels while it is in bytes for Dawn
region.bufferRowLength = bufferCopy.rowPitch / texture->GetFormat().blockByteSize;
const Format& format = texture->GetFormat();
ASSERT(bufferCopy.rowPitch % format.blockByteSize == 0);
region.bufferRowLength = bufferCopy.rowPitch / format.blockByteSize * format.blockWidth;
region.bufferImageHeight = bufferCopy.imageHeight;
region.imageSubresource.aspectMask = texture->GetVkAspectMask();
@ -62,8 +86,9 @@ namespace dawn_native { namespace vulkan {
region.imageOffset.y = textureCopy.origin.y;
region.imageOffset.z = textureCopy.origin.z;
region.imageExtent.width = copySize.width;
region.imageExtent.height = copySize.height;
Extent3D imageExtent = ComputeTextureCopyExtent(textureCopy, copySize);
region.imageExtent.width = imageExtent.width;
region.imageExtent.height = imageExtent.height;
region.imageExtent.depth = copySize.depth;
return region;
@ -95,9 +120,14 @@ namespace dawn_native { namespace vulkan {
region.dstOffset.y = dstCopy.origin.y;
region.dstOffset.z = dstCopy.origin.z;
region.extent.width = copySize.width;
region.extent.height = copySize.height;
region.extent.depth = copySize.depth;
Extent3D imageExtentDst = ComputeTextureCopyExtent(dstCopy, copySize);
// TODO(jiawei.shao@intel.com): add workaround for the case that imageExtentSrc is not
// equal to imageExtentDst. For example when copySize fits in the virtual size of the
// source image but does not fit in the one of the destination image.
Extent3D imageExtent = imageExtentDst;
region.extent.width = imageExtent.width;
region.extent.height = imageExtent.height;
region.extent.depth = imageExtent.depth;
return region;
}

View File

@ -341,6 +341,9 @@ namespace dawn_native { namespace vulkan {
// Always require imageCubeArray because it is a core Dawn feature
usedKnobs.features.imageCubeArray = VK_TRUE;
// TODO(jiawei.shao@intel.com): support BC formats as extension
usedKnobs.features.textureCompressionBC = VK_TRUE;
// Find a universal queue family
{
constexpr uint32_t kUniversalFlags =

View File

@ -577,6 +577,12 @@ namespace dawn_native { namespace vulkan {
}
if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
layerCount)) {
// TODO(jiawei.shao@intel.com): initialize textures in BC formats with Buffer-to-Texture
// copies.
if (GetFormat().isCompressed) {
return;
}
// If subresource has not been initialized, clear it to black as it could contain dirty
// bits from recycled memory
ClearTexture(commands, baseMipLevel, levelCount, baseArrayLayer, layerCount);

View File

@ -0,0 +1,550 @@
// Copyright 2019 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "tests/DawnTest.h"
#include "common/Assert.h"
#include "common/Constants.h"
#include "common/Math.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/DawnHelpers.h"
// Create a 2D texture for sampling in the tests.
dawn::Texture Create2DTexture(dawn::Device device,
dawn::TextureFormat format,
uint32_t width,
uint32_t height,
uint32_t arrayLayerCount = 1,
uint32_t mipLevelCount = 1,
dawn::TextureUsageBit usage = dawn::TextureUsageBit::Sampled |
dawn::TextureUsageBit::TransferDst) {
dawn::TextureDescriptor descriptor;
descriptor.dimension = dawn::TextureDimension::e2D;
descriptor.format = format;
descriptor.size.width = width;
descriptor.size.height = height;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = arrayLayerCount;
descriptor.sampleCount = 1;
descriptor.mipLevelCount = mipLevelCount;
descriptor.usage = usage;
return device.CreateTexture(&descriptor);
}
// The helper struct to configure the copies between buffers and textures.
struct CopyConfig {
dawn::TextureFormat format;
uint32_t textureWidthLevel0;
uint32_t textureHeightLevel0;
dawn::Extent3D copyExtent3D;
dawn::Origin3D copyOrigin3D = {0, 0, 0};
uint32_t arrayLayerCount = 1;
uint32_t mipmapLevelCount = 1;
uint32_t baseMipmapLevel = 0;
uint32_t baseArrayLayer = 0;
uint32_t bufferOffset = 0;
uint32_t rowPitchAlignment = kTextureRowPitchAlignment;
};
class CompressedTextureBCFormatTest : public DawnTest {
protected:
void SetUp() override {
DawnTest::SetUp();
mBindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler},
{1, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}});
}
// Copy the compressed texture data into the destination texture as is specified in copyConfig.
void CopyDataIntoCompressedTexture(dawn::Texture bcCompressedTexture,
const CopyConfig& copyConfig) {
// Compute the upload buffer size with rowPitchAlignment and the copy region.
uint32_t actualWidthAtLevel = copyConfig.textureWidthLevel0 >> copyConfig.baseMipmapLevel;
uint32_t actualHeightAtLevel = copyConfig.textureHeightLevel0 >> copyConfig.baseMipmapLevel;
uint32_t copyWidthInBlockAtLevel =
(actualWidthAtLevel + kBCBlockWidthInTexels - 1) / kBCBlockWidthInTexels;
uint32_t copyHeightInBlockAtLevel =
(actualHeightAtLevel + kBCBlockHeightInTexels - 1) / kBCBlockHeightInTexels;
uint32_t bufferRowPitchInBytes = 0;
if (copyConfig.rowPitchAlignment != 0) {
bufferRowPitchInBytes = copyConfig.rowPitchAlignment;
} else {
bufferRowPitchInBytes =
copyWidthInBlockAtLevel * CompressedFormatBlockSizeInBytes(copyConfig.format);
}
uint32_t uploadBufferSize = bufferRowPitchInBytes * copyHeightInBlockAtLevel;
// Fill uploadData with the pre-prepared one-block compressed texture data.
std::vector<uint8_t> uploadData(uploadBufferSize, 0);
std::vector<uint8_t> oneBlockCompressedTextureData =
GetOneBlockBCFormatTextureData(copyConfig.format);
for (uint32_t h = 0; h < copyHeightInBlockAtLevel; ++h) {
for (uint32_t w = 0; w < copyWidthInBlockAtLevel; ++w) {
uint32_t uploadBufferOffset = copyConfig.bufferOffset + bufferRowPitchInBytes * h +
oneBlockCompressedTextureData.size() * w;
std::memcpy(&uploadData[uploadBufferOffset], oneBlockCompressedTextureData.data(),
oneBlockCompressedTextureData.size() * sizeof(uint8_t));
}
}
// Copy texture data from a staging buffer to the destination texture.
dawn::Buffer stagingBuffer = utils::CreateBufferFromData(
device, uploadData.data(), uploadBufferSize, dawn::BufferUsageBit::TransferSrc);
dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
stagingBuffer, copyConfig.bufferOffset, copyConfig.rowPitchAlignment, 0);
dawn::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(bcCompressedTexture, copyConfig.baseMipmapLevel,
copyConfig.baseArrayLayer, copyConfig.copyOrigin3D);
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copyConfig.copyExtent3D);
dawn::CommandBuffer copy = encoder.Finish();
queue.Submit(1, &copy);
}
// Create the bind group that includes a BC texture and a sampler.
dawn::BindGroup CreateBindGroupForTest(dawn::Texture bcCompressedTexture,
dawn::TextureFormat bcFormat,
uint32_t baseArrayLayer = 0,
uint32_t baseMipLevel = 0) {
dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
samplerDesc.minFilter = dawn::FilterMode::Nearest;
samplerDesc.magFilter = dawn::FilterMode::Nearest;
dawn::Sampler sampler = device.CreateSampler(&samplerDesc);
dawn::TextureViewDescriptor textureViewDescriptor;
textureViewDescriptor.format = bcFormat;
textureViewDescriptor.dimension = dawn::TextureViewDimension::e2D;
textureViewDescriptor.baseMipLevel = baseMipLevel;
textureViewDescriptor.baseArrayLayer = baseArrayLayer;
textureViewDescriptor.arrayLayerCount = 1;
textureViewDescriptor.mipLevelCount = 1;
dawn::TextureView bcTextureView = bcCompressedTexture.CreateView(&textureViewDescriptor);
return utils::MakeBindGroup(device, mBindGroupLayout, {{0, sampler}, {1, bcTextureView}});
}
// Create a render pipeline for sampling from a BC texture and rendering into the render target.
dawn::RenderPipeline CreateRenderPipelineForTest() {
dawn::PipelineLayout pipelineLayout =
utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
dawn::ShaderModule vsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
#version 450
layout(location=0) out vec2 texCoord;
void main() {
const vec2 pos[3] = vec2[3](
vec2(-3.0f, -1.0f),
vec2( 3.0f, -1.0f),
vec2( 0.0f, 2.0f)
);
gl_Position = vec4(pos[gl_VertexIndex], 0.0f, 1.0f);
texCoord = gl_Position.xy / 2.0f + vec2(0.5f);
})");
dawn::ShaderModule fsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
#version 450
layout(set = 0, binding = 0) uniform sampler sampler0;
layout(set = 0, binding = 1) uniform texture2D texture0;
layout(location = 0) in vec2 texCoord;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = texture(sampler2D(texture0, sampler0), texCoord);
})");
renderPipelineDescriptor.cVertexStage.module = vsModule;
renderPipelineDescriptor.cFragmentStage.module = fsModule;
renderPipelineDescriptor.layout = pipelineLayout;
renderPipelineDescriptor.cColorStates[0]->format =
utils::BasicRenderPass::kDefaultColorFormat;
return device.CreateRenderPipeline(&renderPipelineDescriptor);
}
// Run the given render pipeline and bind group and verify the pixels in the render target.
void VerifyCompressedTexturePixelValues(dawn::RenderPipeline renderPipeline,
dawn::BindGroup bindGroup,
const dawn::Extent3D& renderTargetSize,
const dawn::Origin3D& expectedOrigin,
const dawn::Extent3D& expectedExtent,
const std::vector<RGBA8>& expected) {
ASSERT(expected.size() == renderTargetSize.width * renderTargetSize.height);
utils::BasicRenderPass renderPass =
utils::CreateBasicRenderPass(device, renderTargetSize.width, renderTargetSize.height);
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
{
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(renderPipeline);
pass.SetBindGroup(0, bindGroup, 0, nullptr);
pass.Draw(6, 1, 0, 0);
pass.EndPass();
}
dawn::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, expectedOrigin.x,
expectedOrigin.y, expectedExtent.width, expectedExtent.height, 0,
0);
}
// Run the tests that copies pre-prepared BC format data into a BC texture and verifies if we
// can render correctly with the pixel values sampled from the BC texture.
void TestCopyRegionIntoBCFormatTextures(const CopyConfig& config) {
dawn::Texture bcTexture = Create2DTexture(device, config.format, config.textureWidthLevel0,
config.textureHeightLevel0,
config.arrayLayerCount, config.mipmapLevelCount);
CopyDataIntoCompressedTexture(bcTexture, config);
dawn::BindGroup bindGroup = CreateBindGroupForTest(
bcTexture, config.format, config.baseArrayLayer, config.baseMipmapLevel);
dawn::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
dawn::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config);
// The copy region may exceed the subresource size because of the required paddings for BC
// blocks, so we should limit the size of the expectedData to make it match the real size
// of the render target.
dawn::Extent3D noPaddingExtent3D = config.copyExtent3D;
if (config.copyOrigin3D.x + config.copyExtent3D.width > virtualSizeAtLevel.width) {
noPaddingExtent3D.width = virtualSizeAtLevel.width - config.copyOrigin3D.x;
}
if (config.copyOrigin3D.y + config.copyExtent3D.height > virtualSizeAtLevel.height) {
noPaddingExtent3D.height = virtualSizeAtLevel.height - config.copyOrigin3D.y;
}
std::vector<RGBA8> expectedData = GetExpectedData(config.format, virtualSizeAtLevel);
VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel,
config.copyOrigin3D, noPaddingExtent3D, expectedData);
}
// Return the BC block size in bytes.
// TODO(jiawei.shao@intel.com): support all BC formats.
static uint32_t CompressedFormatBlockSizeInBytes(dawn::TextureFormat format) {
switch (format) {
case dawn::TextureFormat::BC5RGSnorm:
case dawn::TextureFormat::BC5RGUnorm:
return 16;
default:
UNREACHABLE();
return 0;
}
}
// Return the pre-prepared one-block BC texture data.
// TODO(jiawei.shao@intel.com): prepare texture data for all BC formats.
static std::vector<uint8_t> GetOneBlockBCFormatTextureData(dawn::TextureFormat bcFormat) {
switch (bcFormat) {
// The expected data represents 4x4 pixel images with the left side red and the right
// side green and was encoded with DirectXTex from Microsoft.
case dawn::TextureFormat::BC5RGSnorm:
return {0x7f, 0x81, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24,
0x7f, 0x81, 0x9, 0x90, 0x0, 0x9, 0x90, 0x0};
case dawn::TextureFormat::BC5RGUnorm:
return {0xff, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24,
0xff, 0x0, 0x9, 0x90, 0x0, 0x9, 0x90, 0x0};
default:
UNREACHABLE();
return {};
}
}
// Return the texture data that is decoded from the result of GetOneBlockBCFormatTextureData in
// RGBA8 formats.
// TODO(jiawei.shao@intel.com): prepare texture data for all BC formats.
static std::vector<RGBA8> GetExpectedData(dawn::TextureFormat bcFormat,
const dawn::Extent3D& testRegion) {
switch (bcFormat) {
case dawn::TextureFormat::BC5RGSnorm:
case dawn::TextureFormat::BC5RGUnorm:
return FillExpectedDataWithPureRedAndPureGreen(testRegion);
default:
UNREACHABLE();
return {};
}
}
// Get one kind of expected data that is filled with pure green and pure red.
static std::vector<RGBA8> FillExpectedDataWithPureRedAndPureGreen(
const dawn::Extent3D& testRegion) {
ASSERT(testRegion.depth == 1);
constexpr RGBA8 kRed(255, 0, 0, 255);
constexpr RGBA8 kGreen(0, 255, 0, 255);
std::vector<RGBA8> expectedData(testRegion.width * testRegion.height, kRed);
for (uint32_t y = 0; y < testRegion.height; ++y) {
for (uint32_t x = 0; x < testRegion.width; ++x) {
if (x % kBCBlockWidthInTexels >= kBCBlockWidthInTexels / 2) {
expectedData[testRegion.width * y + x] = kGreen;
}
}
}
return expectedData;
}
static dawn::Extent3D GetVirtualSizeAtLevel(const CopyConfig& config) {
return {config.textureWidthLevel0 >> config.baseMipmapLevel,
config.textureHeightLevel0 >> config.baseMipmapLevel, 1};
}
static dawn::Extent3D GetPhysicalSizeAtLevel(const CopyConfig& config) {
dawn::Extent3D sizeAtLevel = GetVirtualSizeAtLevel(config);
sizeAtLevel.width = (sizeAtLevel.width + kBCBlockWidthInTexels - 1) /
kBCBlockWidthInTexels * kBCBlockWidthInTexels;
sizeAtLevel.height = (sizeAtLevel.height + kBCBlockHeightInTexels - 1) /
kBCBlockHeightInTexels * kBCBlockHeightInTexels;
return sizeAtLevel;
}
// TODO(jiawei.shao@intel.com): support all BC formats.
const std::array<dawn::TextureFormat, 2> kBCFormats = {dawn::TextureFormat::BC5RGSnorm,
dawn::TextureFormat::BC5RGUnorm};
// Tthe block width and height in texels are 4 for all BC formats.
static constexpr uint32_t kBCBlockWidthInTexels = 4;
static constexpr uint32_t kBCBlockHeightInTexels = 4;
dawn::BindGroupLayout mBindGroupLayout;
};
// Test copying into the whole BC texture with 2x2 blocks and sampling from it.
TEST_P(CompressedTextureBCFormatTest, Basic) {
CopyConfig config;
config.textureWidthLevel0 = 8;
config.textureHeightLevel0 = 8;
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test copying into a sub-region of a texture with BC formats works correctly.
TEST_P(CompressedTextureBCFormatTest, CopyIntoSubRegion) {
CopyConfig config;
config.textureHeightLevel0 = 8;
config.textureWidthLevel0 = 8;
config.rowPitchAlignment = kTextureRowPitchAlignment;
const dawn::Origin3D kOrigin = {4, 4, 0};
const dawn::Extent3D kExtent3D = {4, 4, 1};
config.copyOrigin3D = kOrigin;
config.copyExtent3D = kExtent3D;
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test using rowPitch == 0 in the copies with BC formats works correctly.
TEST_P(CompressedTextureBCFormatTest, CopyWithZeroRowPitch) {
CopyConfig config;
config.textureHeightLevel0 = 8;
config.rowPitchAlignment = 0;
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
config.textureWidthLevel0 = kTextureRowPitchAlignment /
CompressedFormatBlockSizeInBytes(config.format) *
kBCBlockWidthInTexels;
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test copying into the non-zero layer of a 2D array texture with BC formats works correctly.
TEST_P(CompressedTextureBCFormatTest, CopyIntoNonZeroArrayLayer) {
CopyConfig config;
config.textureHeightLevel0 = 8;
config.textureWidthLevel0 = 8;
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
config.rowPitchAlignment = kTextureRowPitchAlignment;
constexpr uint32_t kArrayLayerCount = 3;
config.arrayLayerCount = kArrayLayerCount;
config.baseArrayLayer = kArrayLayerCount - 1;
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test copying into a non-zero mipmap level of a texture with BC texture formats.
TEST_P(CompressedTextureBCFormatTest, CopyBufferIntoNonZeroMipmapLevel) {
CopyConfig config;
config.textureHeightLevel0 = 60;
config.textureWidthLevel0 = 60;
config.rowPitchAlignment = kTextureRowPitchAlignment;
constexpr uint32_t kMipmapLevelCount = 3;
config.mipmapLevelCount = kMipmapLevelCount;
config.baseMipmapLevel = kMipmapLevelCount - 1;
// The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
// required in the copies.
const uint32_t kActualWidthAtLevel = config.textureWidthLevel0 >> config.baseMipmapLevel;
const uint32_t kActualHeightAtLevel = config.textureHeightLevel0 >> config.baseMipmapLevel;
ASSERT(kActualWidthAtLevel % kBCBlockWidthInTexels != 0);
ASSERT(kActualHeightAtLevel % kBCBlockHeightInTexels != 0);
const uint32_t kCopyWidthAtLevel = (kActualWidthAtLevel + kBCBlockWidthInTexels - 1) /
kBCBlockWidthInTexels * kBCBlockWidthInTexels;
const uint32_t kCopyHeightAtLevel = (kActualHeightAtLevel + kBCBlockHeightInTexels - 1) /
kBCBlockHeightInTexels * kBCBlockHeightInTexels;
config.copyExtent3D = {kCopyWidthAtLevel, kCopyHeightAtLevel, 1};
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test texture-to-texture whole-size copies with BC formats.
TEST_P(CompressedTextureBCFormatTest, CopyWholeTextureSubResourceIntoNonZeroMipmapLevel) {
CopyConfig config;
config.textureHeightLevel0 = 60;
config.textureWidthLevel0 = 60;
config.rowPitchAlignment = kTextureRowPitchAlignment;
constexpr uint32_t kMipmapLevelCount = 3;
config.mipmapLevelCount = kMipmapLevelCount;
config.baseMipmapLevel = kMipmapLevelCount - 1;
// The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
// required in the copies.
const dawn::Extent3D kVirtualSize = GetVirtualSizeAtLevel(config);
const dawn::Extent3D kPhysicalSize = GetPhysicalSizeAtLevel(config);
ASSERT(kVirtualSize.width % kBCBlockWidthInTexels != 0);
ASSERT(kVirtualSize.height % kBCBlockHeightInTexels != 0);
config.copyExtent3D = kPhysicalSize;
for (dawn::TextureFormat format : kBCFormats) {
// Create bcTextureSrc as the source texture and initialize it with pre-prepared BC
// compressed data.
config.format = format;
dawn::Texture bcTextureSrc = Create2DTexture(
device, config.format, config.textureWidthLevel0, config.textureHeightLevel0,
config.arrayLayerCount, config.mipmapLevelCount,
dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst);
CopyDataIntoCompressedTexture(bcTextureSrc, config);
// Create bcTexture and copy from the content in bcTextureSrc into it.
dawn::Texture bcTexture = Create2DTexture(device, config.format, config.textureWidthLevel0,
config.textureHeightLevel0,
config.arrayLayerCount, config.mipmapLevelCount);
dawn::TextureCopyView textureCopyViewSrc = utils::CreateTextureCopyView(
bcTextureSrc, config.baseMipmapLevel, config.baseArrayLayer, config.copyOrigin3D);
dawn::TextureCopyView textureCopyViewDst = utils::CreateTextureCopyView(
bcTexture, config.baseMipmapLevel, config.baseArrayLayer, config.copyOrigin3D);
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToTexture(&textureCopyViewSrc, &textureCopyViewDst,
&config.copyExtent3D);
dawn::CommandBuffer copy = encoder.Finish();
queue.Submit(1, &copy);
// Verify if we can use bcTexture as sampled textures correctly.
dawn::BindGroup bindGroup = CreateBindGroupForTest(
bcTexture, config.format, config.baseArrayLayer, config.baseMipmapLevel);
dawn::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
std::vector<RGBA8> expectedData = GetExpectedData(config.format, kVirtualSize);
VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kVirtualSize,
config.copyOrigin3D, kVirtualSize, expectedData);
}
}
// Test BC format texture-to-texture partial copies.
TEST_P(CompressedTextureBCFormatTest, CopyPartofTextureSubResourceIntoNonZeroMipmapLevel) {
// TODO(jiawei.shao@intel.com): add workaround on the T2T copies where Extent3D fits in one
// subresource and does not fit in another one on Vulkan. Currently this test causes an error if
// Vulkan validation layer is enabled.
DAWN_SKIP_TEST_IF(IsVulkan());
CopyConfig srcConfig;
srcConfig.textureHeightLevel0 = 60;
srcConfig.textureWidthLevel0 = 60;
srcConfig.rowPitchAlignment = kTextureRowPitchAlignment;
srcConfig.mipmapLevelCount = 1;
srcConfig.baseMipmapLevel = srcConfig.mipmapLevelCount - 1;
const dawn::Extent3D kSrcVirtualSize = GetVirtualSizeAtLevel(srcConfig);
CopyConfig dstConfig;
dstConfig.textureHeightLevel0 = 60;
dstConfig.textureWidthLevel0 = 60;
dstConfig.rowPitchAlignment = kTextureRowPitchAlignment;
constexpr uint32_t kMipmapLevelCount = 3;
dstConfig.mipmapLevelCount = kMipmapLevelCount;
dstConfig.baseMipmapLevel = kMipmapLevelCount - 1;
// The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
// required in the copies.
const dawn::Extent3D kDstVirtualSize = GetVirtualSizeAtLevel(dstConfig);
ASSERT(kDstVirtualSize.width % kBCBlockWidthInTexels != 0);
ASSERT(kDstVirtualSize.height % kBCBlockHeightInTexels != 0);
const dawn::Extent3D kDstPhysicalSize = GetPhysicalSizeAtLevel(dstConfig);
srcConfig.copyExtent3D = dstConfig.copyExtent3D = kDstPhysicalSize;
ASSERT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width < kSrcVirtualSize.width);
ASSERT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height < kSrcVirtualSize.height);
for (dawn::TextureFormat format : kBCFormats) {
// Create bcTextureSrc as the source texture and initialize it with pre-prepared BC
// compressed data.
srcConfig.format = format;
dawn::Texture bcTextureSrc = Create2DTexture(
device, srcConfig.format, srcConfig.textureWidthLevel0, srcConfig.textureHeightLevel0,
srcConfig.arrayLayerCount, srcConfig.mipmapLevelCount,
dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst);
CopyDataIntoCompressedTexture(bcTextureSrc, srcConfig);
dawn::TextureCopyView textureCopyViewSrc =
utils::CreateTextureCopyView(bcTextureSrc, srcConfig.baseMipmapLevel,
srcConfig.baseArrayLayer, srcConfig.copyOrigin3D);
// Create bcTexture and copy from the content in bcTextureSrc into it.
dstConfig.format = format;
dawn::Texture bcTexture = Create2DTexture(
device, dstConfig.format, dstConfig.textureWidthLevel0, dstConfig.textureHeightLevel0,
dstConfig.arrayLayerCount, dstConfig.mipmapLevelCount);
dawn::TextureCopyView textureCopyViewDst = utils::CreateTextureCopyView(
bcTexture, dstConfig.baseMipmapLevel, dstConfig.baseArrayLayer, dstConfig.copyOrigin3D);
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToTexture(&textureCopyViewSrc, &textureCopyViewDst,
&dstConfig.copyExtent3D);
dawn::CommandBuffer copy = encoder.Finish();
queue.Submit(1, &copy);
// Verify if we can use bcTexture as sampled textures correctly.
dawn::BindGroup bindGroup = CreateBindGroupForTest(
bcTexture, dstConfig.format, dstConfig.baseArrayLayer, dstConfig.baseMipmapLevel);
dawn::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
std::vector<RGBA8> expectedData = GetExpectedData(dstConfig.format, kDstVirtualSize);
VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize,
dstConfig.copyOrigin3D, kDstVirtualSize, expectedData);
}
}
// TODO(jiawei.shao@intel.com): support BC formats on D3D12, Metal and OpenGL backend
DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, VulkanBackend);

View File

@ -215,8 +215,6 @@ namespace utils {
uint32_t height) {
DAWN_ASSERT(width > 0 && height > 0);
dawn::TextureFormat kColorFormat = dawn::TextureFormat::RGBA8Unorm;
dawn::TextureDescriptor descriptor;
descriptor.dimension = dawn::TextureDimension::e2D;
descriptor.size.width = width;
@ -224,13 +222,13 @@ namespace utils {
descriptor.size.depth = 1;
descriptor.arrayLayerCount = 1;
descriptor.sampleCount = 1;
descriptor.format = kColorFormat;
descriptor.format = BasicRenderPass::kDefaultColorFormat;
descriptor.mipLevelCount = 1;
descriptor.usage =
dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc;
dawn::Texture color = device.CreateTexture(&descriptor);
return BasicRenderPass(width, height, color, kColorFormat);
return BasicRenderPass(width, height, color);
}
dawn::BufferCopyView CreateBufferCopyView(dawn::Buffer buffer,

View File

@ -73,7 +73,9 @@ namespace utils {
BasicRenderPass(uint32_t width,
uint32_t height,
dawn::Texture color,
dawn::TextureFormat texture);
dawn::TextureFormat texture = kDefaultColorFormat);
static constexpr dawn::TextureFormat kDefaultColorFormat = dawn::TextureFormat::RGBA8Unorm;
uint32_t width;
uint32_t height;