Test for TEXTURE_BINDING vs STORAGE_BINDING issue
The GL backend has an issue if you used a texture as a TEXTURE_BINDING
with a limited subview and then use the same texture has a
STORAGE_BINDING with a different subview. In the backend the first
usage sets TEXTURE_BASE_LEVEL and TEXTURE_MAX_LEVEL on the texture
and this affected the 2nd usage.
This is just a test for this issue. It's suppressed because it fails
on various backends.
Bug: 392121637,392121643,392121648
Change-Id: I2d048ee3d6a065ffb1e1f397f439c335ce96f4b7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/223594
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: Gregg Tavares <gman@chromium.org>
Commit-Queue: Gregg Tavares <gman@chromium.org>
diff --git a/src/dawn/tests/end2end/StorageTextureTests.cpp b/src/dawn/tests/end2end/StorageTextureTests.cpp
index 3aa99cb..f6868bb 100644
--- a/src/dawn/tests/end2end/StorageTextureTests.cpp
+++ b/src/dawn/tests/end2end/StorageTextureTests.cpp
@@ -646,19 +646,28 @@
queue.Submit(1, &commandBuffer);
}
- void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture,
+ void CheckOutputStorageTexture(wgpu::Texture storageTexture,
wgpu::TextureFormat format,
const wgpu::Extent3D& size) {
const std::vector<uint8_t>& expectedData = GetExpectedData(format, size.depthOrArrayLayers);
- CheckOutputStorageTexture(writeonlyStorageTexture, format, size, expectedData.data(),
+ CheckOutputStorageTexture(storageTexture, format, size, expectedData.data(),
expectedData.size());
}
- void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture,
+ void CheckOutputStorageTexture(wgpu::Texture storageTexture,
wgpu::TextureFormat format,
const wgpu::Extent3D& size,
const uint8_t* expectedData,
size_t expectedDataSize) {
+ CheckOutputStorageTexture(storageTexture, format, 0, size, expectedData, expectedDataSize);
+ }
+
+ void CheckOutputStorageTexture(wgpu::Texture storageTexture,
+ wgpu::TextureFormat format,
+ uint32_t mipLevel,
+ const wgpu::Extent3D& size,
+ const uint8_t* expectedData,
+ size_t expectedDataSize) {
// Copy the content from the write-only storage texture to the result buffer.
wgpu::BufferDescriptor descriptor;
descriptor.size =
@@ -669,7 +678,7 @@
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::ImageCopyTexture imageCopyTexture =
- utils::CreateImageCopyTexture(writeonlyStorageTexture, 0, {0, 0, 0});
+ utils::CreateImageCopyTexture(storageTexture, mipLevel, {0, 0, 0});
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(
resultBuffer, 0, kTextureBytesPerRowAlignment, size.height);
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &size);
@@ -1420,6 +1429,296 @@
expectedData.size() * sizeof(uint32_t));
}
+// Tests reading from mip level 0 of a mipLevelCount = 3 texture using a TEXTURE_BINDING
+// and then writing to mip level 1 as STORAGE_BINDING. This surfaced an issue in the GL
+// backend where the first usage sets TEXTURE_BASE_LEVEL and TEXTURE_MAX_LEVEL on the texture
+// and they end up affecting the 2nd usage.
+TEST_P(ReadWriteStorageTextureTests, ReadMipLevel0WriteMipLevel1) {
+ // https://crbug.com/392121637
+ DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
+
+ wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
+ @binding(0) @group(0) var<storage, read_write> buf : array<f32>;
+ @binding(1) @group(0) var t_in: texture_2d<f32>;
+ @binding(2) @group(0) var t_out: texture_storage_2d<rgba8unorm, write>;
+
+ @compute @workgroup_size(1) fn csLoad() {
+ // just make sure we actually read (don't want this optimized out)
+ buf[0] = textureLoad(t_in, vec2u(0), 0).w;
+ }
+ @compute @workgroup_size(1) fn csStore() {
+ textureStore(t_out, vec2u(0), vec4f(64, 128, 192, 255) / 255);
+ }
+ )");
+
+ wgpu::ComputePipelineDescriptor pipelineDescriptor;
+ pipelineDescriptor.layout = nullptr;
+ pipelineDescriptor.compute.module = csModule;
+
+ pipelineDescriptor.compute.entryPoint = "csLoad";
+ wgpu::ComputePipeline loadPipeline = device.CreateComputePipeline(&pipelineDescriptor);
+
+ pipelineDescriptor.compute.entryPoint = "csStore";
+ wgpu::ComputePipeline storePipeline = device.CreateComputePipeline(&pipelineDescriptor);
+
+ wgpu::BufferDescriptor bufferDesc;
+ bufferDesc.size = 16;
+ bufferDesc.usage = wgpu::BufferUsage::Storage;
+ wgpu::Buffer storageBuffer = device.CreateBuffer(&bufferDesc);
+
+ bufferDesc.size = 4;
+ bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
+ wgpu::Buffer resultBuffer0 = device.CreateBuffer(&bufferDesc);
+ wgpu::Buffer resultBuffer1 = device.CreateBuffer(&bufferDesc);
+
+ wgpu::TextureDescriptor textureDesc;
+ textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
+ textureDesc.size = {2, 1};
+ textureDesc.mipLevelCount = 2;
+ textureDesc.usage = wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding |
+ wgpu::TextureUsage::CopySrc;
+ wgpu::Texture texture = device.CreateTexture(&textureDesc);
+
+ wgpu::TextureViewDescriptor textureViewDesc1;
+ textureViewDesc1.baseMipLevel = 0;
+ textureViewDesc1.mipLevelCount = 1;
+ wgpu::BindGroup loadBindGroup =
+ utils::MakeBindGroup(device, loadPipeline.GetBindGroupLayout(0),
+ {{1, texture.CreateView(&textureViewDesc1)}, {0, storageBuffer}});
+
+ wgpu::TextureViewDescriptor textureViewDesc2;
+ textureViewDesc2.baseMipLevel = 1;
+ textureViewDesc2.mipLevelCount = 1;
+ wgpu::BindGroup storeBindGroup = utils::MakeBindGroup(
+ device, storePipeline.GetBindGroupLayout(0), {{2, texture.CreateView(&textureViewDesc2)}});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::ComputePassEncoder computeEncoder = encoder.BeginComputePass();
+
+ computeEncoder.SetBindGroup(0, loadBindGroup);
+ computeEncoder.SetPipeline(loadPipeline);
+ computeEncoder.DispatchWorkgroups(1);
+
+ computeEncoder.SetBindGroup(0, storeBindGroup);
+ computeEncoder.SetPipeline(storePipeline);
+ computeEncoder.DispatchWorkgroups(1);
+
+ computeEncoder.End();
+
+ {
+ wgpu::ImageCopyTexture imageCopyTexture =
+ utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
+ wgpu::ImageCopyBuffer imageCopyBuffer =
+ utils::CreateImageCopyBuffer(resultBuffer0, 0, 256, 1);
+ wgpu::Extent3D size({1, 1, 1});
+ encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &size);
+ }
+
+ {
+ wgpu::ImageCopyTexture imageCopyTexture =
+ utils::CreateImageCopyTexture(texture, 1, {0, 0, 0});
+ wgpu::ImageCopyBuffer imageCopyBuffer =
+ utils::CreateImageCopyBuffer(resultBuffer1, 0, 256, 1);
+ wgpu::Extent3D size({1, 1, 1});
+ encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &size);
+ }
+
+ wgpu::CommandBuffer commandBuffer = encoder.Finish();
+ queue.Submit(1, &commandBuffer);
+
+ static uint8_t expectedData0[]{0, 0, 0, 0};
+ EXPECT_BUFFER_U8_RANGE_EQ(expectedData0, resultBuffer0, 0, 4);
+
+ static uint8_t expectedData1[]{64, 128, 192, 255};
+ EXPECT_BUFFER_U8_RANGE_EQ(expectedData1, resultBuffer1, 0, 4);
+}
+
+// Tests reading from both a TEXTURE_BINDING and a STORAGE_BINDING from the same
+// texture at the same time. This test is to double check on a workaround for
+// fixing the previous test above where in the GL backend we try to reset the
+// TEXTURE_BASE_LEVEL and TEXTURE_MAX_LEVEL. If we mistakenly apply the workaround
+// to read only textures then this test will fail.
+TEST_P(ReadWriteStorageTextureTests, ReadMipLevel2AsBothTextureBindingAndStorageBinding) {
+ // This asserts in TextureVK.cpp, see https://crbug.com/392121643
+ DAWN_SUPPRESS_TEST_IF(IsVulkan());
+ // https://crbug.com/392121648
+ DAWN_SUPPRESS_TEST_IF(IsANGLED3D11());
+
+ wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
+ @binding(0) @group(0) var<storage, read_write> buf : array<vec4u>;
+ @binding(1) @group(0) var t_in: texture_2d<f32>;
+ @binding(2) @group(0) var s_in: texture_storage_2d<rgba8unorm, read>;
+
+ @compute @workgroup_size(1) fn cs() {
+ buf[0] = vec4u(
+ u32(textureLoad(t_in, vec2u(0), 0).r * 255),
+ u32(textureLoad(s_in, vec2u(0)).r * 255),
+ 123,
+ 456,
+ );
+ }
+ )");
+
+ wgpu::ComputePipelineDescriptor pipelineDescriptor;
+ pipelineDescriptor.layout = nullptr;
+ pipelineDescriptor.compute.module = csModule;
+ wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDescriptor);
+
+ wgpu::BufferDescriptor bufferDesc;
+ bufferDesc.size = 16;
+ bufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
+ wgpu::Buffer storageBuffer = device.CreateBuffer(&bufferDesc);
+
+ // make a 3 mip level texture
+ wgpu::TextureDescriptor textureDesc;
+ textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
+ textureDesc.size = {4, 1};
+ textureDesc.mipLevelCount = 3;
+ textureDesc.usage = wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding |
+ wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
+ wgpu::Texture texture = device.CreateTexture(&textureDesc);
+
+ // put 1 in first mip, 2 in 2nd, 3 in 3rd.
+ for (uint32_t mipLevel = 0; mipLevel < 3; ++mipLevel) {
+ uint32_t width = 4 >> mipLevel;
+ uint32_t bytesPerRow = width * 4;
+ wgpu::Extent3D copySize({width, 1, 1});
+ wgpu::ImageCopyTexture imageCopyTexture =
+ utils::CreateImageCopyTexture(texture, mipLevel, {0, 0, 0});
+ wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout(0, bytesPerRow);
+ std::vector<uint8_t> data(bytesPerRow, mipLevel + 1);
+ queue.WriteTexture(&imageCopyTexture, data.data(), bytesPerRow, &textureDataLayout,
+ ©Size);
+ }
+
+ // View mip level 2
+ wgpu::TextureViewDescriptor textureViewDesc;
+ textureViewDesc.baseMipLevel = 2;
+ textureViewDesc.mipLevelCount = 1;
+ wgpu::TextureView view = texture.CreateView(&textureViewDesc);
+ wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
+ {{0, storageBuffer}, {1, view}, {2, view}});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::ComputePassEncoder computeEncoder = encoder.BeginComputePass();
+
+ computeEncoder.SetBindGroup(0, bindGroup);
+ computeEncoder.SetPipeline(pipeline);
+ computeEncoder.DispatchWorkgroups(1);
+
+ computeEncoder.End();
+
+ wgpu::CommandBuffer commandBuffer = encoder.Finish();
+ queue.Submit(1, &commandBuffer);
+
+ // expect 3 from reading through the texture binding and
+ // also 3 from reading through the storage binding.
+ static uint32_t expectedData[]{3, 3, 123, 456};
+ EXPECT_BUFFER_U32_RANGE_EQ(expectedData, storageBuffer, 0, 4);
+}
+
+// Tests reading from mip level 1 via TEXTURE_BINDING and write to mip level 2 via
+// STORAGE_BINDING at the same time.
+TEST_P(ReadWriteStorageTextureTests, ReadMipLevel1AndWriteLevel2AtTheSameTime) {
+ // Compat mode doesn't support different views of the same texture
+ DAWN_SUPPRESS_TEST_IF(IsCompatibilityMode());
+
+ wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
+ @binding(0) @group(0) var<storage, read_write> buf : array<vec4u>;
+ @binding(1) @group(0) var t_in: texture_2d<f32>;
+ @binding(2) @group(0) var s_out: texture_storage_2d<rgba8unorm, write>;
+
+ @compute @workgroup_size(1) fn cs() {
+ buf[0] = vec4u(textureLoad(t_in, vec2u(0), 0) * 255);
+ textureStore(s_out, vec2u(0), vec4f(64, 128, 192, 255) / 255);
+ }
+ )");
+
+ wgpu::ComputePipelineDescriptor pipelineDescriptor;
+ pipelineDescriptor.layout = nullptr;
+ pipelineDescriptor.compute.module = csModule;
+ wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDescriptor);
+
+ wgpu::BufferDescriptor bufferDesc;
+ bufferDesc.size = 16;
+ bufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
+ wgpu::Buffer storageBuffer = device.CreateBuffer(&bufferDesc);
+
+ bufferDesc.size = 16;
+ bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
+ wgpu::Buffer resultBuffer = device.CreateBuffer(&bufferDesc);
+
+ // make a 3 mip level texture
+ wgpu::TextureDescriptor textureDesc;
+ textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
+ textureDesc.size = {4, 1};
+ textureDesc.mipLevelCount = 3;
+ textureDesc.usage = wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding |
+ wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
+ wgpu::Texture texture = device.CreateTexture(&textureDesc);
+
+ // put 1 in first mip, 2 in 2nd, 3 in 3rd.
+ for (uint32_t mipLevel = 0; mipLevel < 3; ++mipLevel) {
+ uint32_t width = 4 >> mipLevel;
+ uint32_t bytesPerRow = width * 4;
+ wgpu::Extent3D copySize({width, 1, 1});
+ wgpu::ImageCopyTexture imageCopyTexture =
+ utils::CreateImageCopyTexture(texture, mipLevel, {0, 0, 0});
+ wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout(0, bytesPerRow);
+ std::vector<uint8_t> data(bytesPerRow, mipLevel + 1);
+ queue.WriteTexture(&imageCopyTexture, data.data(), bytesPerRow, &textureDataLayout,
+ ©Size);
+ }
+
+ // View mip level 1
+ wgpu::TextureViewDescriptor textureViewDesc;
+ textureViewDesc.baseMipLevel = 1;
+ textureViewDesc.mipLevelCount = 1;
+ wgpu::TextureView viewL1 = texture.CreateView(&textureViewDesc);
+
+ // View mip level 2
+ textureViewDesc.baseMipLevel = 2;
+ textureViewDesc.mipLevelCount = 1;
+ wgpu::TextureView viewL2 = texture.CreateView(&textureViewDesc);
+
+ wgpu::BindGroup bindGroup = utils::MakeBindGroup(
+ device, pipeline.GetBindGroupLayout(0), {{0, storageBuffer}, {1, viewL1}, {2, viewL2}});
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ wgpu::ComputePassEncoder computeEncoder = encoder.BeginComputePass();
+
+ computeEncoder.SetBindGroup(0, bindGroup);
+ computeEncoder.SetPipeline(pipeline);
+ computeEncoder.DispatchWorkgroups(1);
+
+ computeEncoder.End();
+
+ // copy a texel from mip level 2
+ {
+ wgpu::ImageCopyTexture imageCopyTexture =
+ utils::CreateImageCopyTexture(texture, 2, {0, 0, 0});
+ wgpu::ImageCopyBuffer imageCopyBuffer =
+ utils::CreateImageCopyBuffer(resultBuffer, 0, 256, 1);
+ wgpu::Extent3D size({1, 1, 1});
+ encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &size);
+ }
+
+ wgpu::CommandBuffer commandBuffer = encoder.Finish();
+ queue.Submit(1, &commandBuffer);
+
+ // expect 2 from reading mip level 1.
+ {
+ static uint32_t expectedData[]{2, 2, 2, 2};
+ EXPECT_BUFFER_U32_RANGE_EQ(expectedData, storageBuffer, 0, 4);
+ }
+
+ // expect 0.25, 0.5, 0.75, 1 in mip level 2
+ {
+ static uint8_t expectedData[]{64, 128, 192, 255};
+ EXPECT_BUFFER_U8_RANGE_EQ(expectedData, resultBuffer, 0, 4);
+ }
+}
+
DAWN_INSTANTIATE_TEST(ReadWriteStorageTextureTests,
D3D11Backend(),
D3D12Backend(),