Texture corruption issue: add tests for a few rendering types
The original tests for texture corruption only contain WriteTexture and B2TCopy, this change adds more write types like rendering a constant color, rendering from texture sampling, and rendering from textureLoad, all of which need a render pipeline and shaders. The new tests manifest that: For a given 2d-array texture dimension, if WriteTexture and B2TCopy fail, rendering, sampling and textureLoad also fail, and vice versa. Bug: dawn:949, dawn:1507 Change-Id: I28035fe3dd84d36c01befd7fd8ff9d78b312446a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/100061 Commit-Queue: Yunchao He <yunchao.he@intel.com> Reviewed-by: Kai Ninomiya <kainino@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
92943b2831
commit
81d62d7ca9
|
@ -16,10 +16,27 @@
|
|||
|
||||
#include "dawn/common/Math.h"
|
||||
#include "dawn/tests/DawnTest.h"
|
||||
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
|
||||
#include "dawn/utils/WGPUHelpers.h"
|
||||
|
||||
// 2D array textures with particular dimensions may corrupt on some devices. This test creates some
|
||||
// 2d-array textures with different dimensions, and test them one by one. For each sub-test, the
|
||||
// tested texture is written via different methods, then read back from the texture and verify the
|
||||
// data.
|
||||
|
||||
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||
|
||||
namespace {
|
||||
enum class WriteType { WriteTexture, B2TCopy };
|
||||
enum class WriteType {
|
||||
WriteTexture, // Write the tested texture via writeTexture API
|
||||
B2TCopy, // Write the tested texture via B2T copy
|
||||
RenderConstant, // Write the tested texture via rendering the whole rectangle with solid color
|
||||
// (0xFFFFFFFF)
|
||||
RenderFromTextureSample, // Write the tested texture via sampling from a temp texture and
|
||||
// writing the sampled data
|
||||
RenderFromTextureLoad // Write the tested texture via textureLoad() from a temp texture and
|
||||
// writing the loaded data
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, WriteType writeType) {
|
||||
switch (writeType) {
|
||||
|
@ -29,6 +46,15 @@ std::ostream& operator<<(std::ostream& o, WriteType writeType) {
|
|||
case WriteType::B2TCopy:
|
||||
o << "B2TCopy";
|
||||
break;
|
||||
case WriteType::RenderConstant:
|
||||
o << "RenderConstant";
|
||||
break;
|
||||
case WriteType::RenderFromTextureSample:
|
||||
o << "RenderFromTextureSample";
|
||||
break;
|
||||
case WriteType::RenderFromTextureLoad:
|
||||
o << "RenderFromTextureLoad";
|
||||
break;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
@ -37,11 +63,7 @@ using TextureFormat = wgpu::TextureFormat;
|
|||
using TextureWidth = uint32_t;
|
||||
using TextureHeight = uint32_t;
|
||||
|
||||
DAWN_TEST_PARAM_STRUCT(TextureCorruptionTestsParams,
|
||||
TextureFormat,
|
||||
TextureWidth,
|
||||
TextureHeight,
|
||||
WriteType);
|
||||
DAWN_TEST_PARAM_STRUCT(TextureCorruptionTestsParams, TextureWidth, TextureHeight, WriteType);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -50,8 +72,8 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
|
|||
std::ostringstream& DoTest(wgpu::Texture texture,
|
||||
const wgpu::Extent3D textureSize,
|
||||
uint32_t depthOrArrayLayer,
|
||||
uint32_t copyValue,
|
||||
uint32_t bytesPerTexel) {
|
||||
uint32_t srcValue) {
|
||||
uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kFormat);
|
||||
uint32_t bytesPerRow = Align(textureSize.width * bytesPerTexel, 256);
|
||||
uint64_t bufferSize = bytesPerRow * textureSize.height;
|
||||
wgpu::BufferDescriptor descriptor;
|
||||
|
@ -67,27 +89,36 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
|
|||
wgpu::ImageCopyBuffer imageCopyResult =
|
||||
utils::CreateImageCopyBuffer(resultBuffer, 0, bytesPerRow);
|
||||
|
||||
// Initialize the data to be copied
|
||||
WriteType type = GetParam().mWriteType;
|
||||
|
||||
// Fill data into a buffer
|
||||
wgpu::Extent3D copySize = {textureSize.width, textureSize.height, 1};
|
||||
|
||||
// Data is stored in a uint32_t vector, so a single texel may require multiple vector
|
||||
// elements for some formats
|
||||
uint32_t elementNumPerTexel = bytesPerTexel / sizeof(uint32_t);
|
||||
ASSERT(bytesPerTexel = sizeof(uint32_t));
|
||||
uint32_t elementNumPerRow = bytesPerRow / sizeof(uint32_t);
|
||||
uint32_t elementNumInTotal = bufferSize / sizeof(uint32_t);
|
||||
std::vector<uint32_t> data(elementNumInTotal, 0);
|
||||
for (uint32_t i = 0; i < copySize.height; ++i) {
|
||||
for (uint32_t j = 0; j < copySize.width; ++j) {
|
||||
for (uint32_t k = 0; k < elementNumPerTexel; ++k) {
|
||||
data[i * elementNumPerRow + j * elementNumPerTexel + k] = copyValue;
|
||||
copyValue++;
|
||||
if (type == WriteType::RenderFromTextureSample ||
|
||||
type == WriteType::RenderConstant) {
|
||||
// Fill a simple and constant value (0xFFFFFFFF) in the whole buffer for
|
||||
// texture sampling and rendering because either sampling operation will
|
||||
// lead to precision loss or rendering a solid color is easier to implement and
|
||||
// compare.
|
||||
data[i * elementNumPerRow + j] = 0xFFFFFFFF;
|
||||
} else {
|
||||
data[i * elementNumPerRow + j] = srcValue;
|
||||
srcValue++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy data into the texture via B2T copy or WriteTexture
|
||||
// Write data into the given layer via various write types
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
switch (GetParam().mWriteType) {
|
||||
switch (type) {
|
||||
case WriteType::B2TCopy: {
|
||||
queue.WriteBuffer(buffer, 0, data.data(), bufferSize);
|
||||
encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
|
||||
|
@ -100,54 +131,149 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
|
|||
©Size);
|
||||
break;
|
||||
}
|
||||
case WriteType::RenderConstant:
|
||||
case WriteType::RenderFromTextureSample:
|
||||
case WriteType::RenderFromTextureLoad: {
|
||||
// Write data into a single layer temp texture and read from this texture if needed
|
||||
wgpu::TextureView tempView;
|
||||
if (type != WriteType::RenderConstant) {
|
||||
wgpu::Texture tempTexture = Create2DTexture(copySize);
|
||||
wgpu::ImageCopyTexture imageCopyTempTexture =
|
||||
utils::CreateImageCopyTexture(tempTexture, 0, {0, 0, 0});
|
||||
wgpu::TextureDataLayout textureDataLayout =
|
||||
utils::CreateTextureDataLayout(0, bytesPerRow);
|
||||
queue.WriteTexture(&imageCopyTempTexture, data.data(), bufferSize,
|
||||
&textureDataLayout, ©Size);
|
||||
tempView = tempTexture.CreateView();
|
||||
}
|
||||
|
||||
// Write into the specified layer of a 2D array texture
|
||||
wgpu::TextureViewDescriptor viewDesc;
|
||||
viewDesc.format = kFormat;
|
||||
viewDesc.dimension = wgpu::TextureViewDimension::e2D;
|
||||
viewDesc.baseMipLevel = 0;
|
||||
viewDesc.mipLevelCount = 1;
|
||||
viewDesc.baseArrayLayer = depthOrArrayLayer;
|
||||
viewDesc.arrayLayerCount = 1;
|
||||
CreatePipelineAndRender(texture.CreateView(&viewDesc), tempView, encoder, type);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Verify the data in copied texture via a T2B copy and comparison
|
||||
// Verify the data in texture via a T2B copy and comparison
|
||||
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyResult, ©Size);
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
return EXPECT_BUFFER_U32_RANGE_EQ(data.data(), resultBuffer, 0, elementNumInTotal);
|
||||
}
|
||||
|
||||
void CreatePipelineAndRender(wgpu::TextureView renderView,
|
||||
wgpu::TextureView samplerView,
|
||||
wgpu::CommandEncoder encoder,
|
||||
WriteType type) {
|
||||
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
|
||||
pipelineDescriptor.cTargets[0].format = kFormat;
|
||||
|
||||
// Draw the whole texture (a rectangle) via two triangles
|
||||
pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
|
||||
@vertex
|
||||
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
|
||||
var pos = array<vec2<f32>, 6>(
|
||||
vec2<f32>(-1.0, 1.0),
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>( 1.0, 1.0),
|
||||
vec2<f32>( 1.0, 1.0),
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>( 1.0, -1.0));
|
||||
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
||||
})");
|
||||
|
||||
if (type == WriteType::RenderConstant) {
|
||||
pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
|
||||
@fragment
|
||||
fn main(@builtin(position) FragCoord : vec4<f32>) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
|
||||
})");
|
||||
} else if (type == WriteType::RenderFromTextureSample) {
|
||||
pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
|
||||
@group(0) @binding(0) var samp : sampler;
|
||||
@group(0) @binding(1) var tex : texture_2d<f32>;
|
||||
|
||||
@fragment
|
||||
fn main(@builtin(position) FragCoord : vec4<f32>) -> @location(0) vec4<f32> {
|
||||
return textureSample(tex, samp, FragCoord.xy);
|
||||
})");
|
||||
} else {
|
||||
pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
|
||||
@group(0) @binding(0) var tex : texture_2d<f32>;
|
||||
|
||||
@fragment
|
||||
fn main(@builtin(position) Fragcoord: vec4<f32>) -> @location(0) vec4<f32> {
|
||||
return textureLoad(tex, vec2<i32>(Fragcoord.xy), 0);
|
||||
})");
|
||||
}
|
||||
|
||||
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
|
||||
utils::ComboRenderPassDescriptor renderPassDescriptor({renderView});
|
||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
pass.SetPipeline(pipeline);
|
||||
if (type != WriteType::RenderConstant) {
|
||||
wgpu::BindGroup bindGroup;
|
||||
if (type == WriteType::RenderFromTextureLoad) {
|
||||
bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
||||
{{0, samplerView}});
|
||||
} else {
|
||||
bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
||||
{{0, device.CreateSampler()}, {1, samplerView}});
|
||||
}
|
||||
pass.SetBindGroup(0, bindGroup);
|
||||
}
|
||||
pass.Draw(6);
|
||||
pass.End();
|
||||
}
|
||||
|
||||
wgpu::Texture Create2DTexture(const wgpu::Extent3D size) {
|
||||
wgpu::TextureDescriptor texDesc = {};
|
||||
texDesc.dimension = wgpu::TextureDimension::e2D;
|
||||
texDesc.size = size;
|
||||
texDesc.mipLevelCount = 1;
|
||||
texDesc.format = kFormat;
|
||||
texDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc |
|
||||
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding;
|
||||
return device.CreateTexture(&texDesc);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(TextureCorruptionTests, CopyTests) {
|
||||
TEST_P(TextureCorruptionTests, Tests) {
|
||||
DAWN_SUPPRESS_TEST_IF(IsWARP());
|
||||
uint32_t width = GetParam().mTextureWidth;
|
||||
uint32_t height = GetParam().mTextureHeight;
|
||||
uint32_t depthOrArrayLayerCount = 2;
|
||||
wgpu::TextureFormat format = GetParam().mTextureFormat;
|
||||
wgpu::Extent3D textureSize = {width, height, depthOrArrayLayerCount};
|
||||
|
||||
// Pre-allocate textures. The incorrect copy may corrupt neighboring textures or layers.
|
||||
wgpu::TextureDescriptor texDesc = {};
|
||||
texDesc.dimension = wgpu::TextureDimension::e2D;
|
||||
texDesc.size = textureSize;
|
||||
texDesc.mipLevelCount = 1;
|
||||
texDesc.format = format;
|
||||
texDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
|
||||
// Pre-allocate textures. The incorrect write type may corrupt neighboring textures or layers.
|
||||
std::vector<wgpu::Texture> textures;
|
||||
uint32_t texNum = 2;
|
||||
for (uint32_t i = 0; i < texNum; ++i) {
|
||||
textures.push_back(device.CreateTexture(&texDesc));
|
||||
textures.push_back(Create2DTexture(textureSize));
|
||||
}
|
||||
|
||||
// Copy data and verify the result one by one for every layer of every texture
|
||||
uint32_t copyValue = 100000000;
|
||||
// Write data and verify the result one by one for every layer of every texture
|
||||
uint32_t srcValue = 100000000;
|
||||
for (uint32_t i = 0; i < texNum; ++i) {
|
||||
for (uint32_t j = 0; j < depthOrArrayLayerCount; ++j) {
|
||||
DoTest(textures[i], textureSize, j, copyValue, utils::GetTexelBlockSizeInBytes(format))
|
||||
<< "texNum: " << i << ", layer: " << j;
|
||||
copyValue += 100000000;
|
||||
DoTest(textures[i], textureSize, j, srcValue) << "texNum: " << i << ", layer: " << j;
|
||||
srcValue += 100000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests,
|
||||
{D3D12Backend()},
|
||||
{wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA16Uint,
|
||||
wgpu::TextureFormat::RGBA32Uint},
|
||||
{100u, 200u, 300u, 400u, 500u, 600u, 700u, 800u, 900u, 1000u, 1200u},
|
||||
{100u, 200u},
|
||||
{WriteType::WriteTexture, WriteType::B2TCopy});
|
||||
{WriteType::WriteTexture, WriteType::B2TCopy, WriteType::RenderConstant,
|
||||
WriteType::RenderFromTextureSample, WriteType::RenderFromTextureLoad});
|
||||
|
|
Loading…
Reference in New Issue