Implement creating and using 1D texture views.

- Adds a test to sample a 1D texture.
 - Adds a test writing to a 1D texture as a storage texture.
 - Reworks some of the StorageTextureTests helper code to allow passing
   custom sizes (since 1D textures must have height=1).
 - Deletes some dead code leftover from readonly storage textures.
 - Adds validation tests for 1D texture view creation.

Bug: dawn:814

Change-Id: I279856569f4fc6c9a7a5023a42bfa50d444158ea
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/79106
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2022-02-04 08:34:54 +00:00 committed by Dawn LUCI CQ
parent 310686b795
commit 42e648c1cf
7 changed files with 250 additions and 185 deletions

View File

@ -47,7 +47,6 @@ namespace dawn::native {
return {};
}
// TODO(crbug.com/dawn/814): Implement for 1D texture.
bool IsTextureViewDimensionCompatibleWithTextureDimension(
wgpu::TextureViewDimension textureViewDimension,
wgpu::TextureDimension textureDimension) {
@ -62,13 +61,13 @@ namespace dawn::native {
return textureDimension == wgpu::TextureDimension::e3D;
case wgpu::TextureViewDimension::e1D:
return textureDimension == wgpu::TextureDimension::e1D;
case wgpu::TextureViewDimension::Undefined:
break;
}
UNREACHABLE();
}
}
// TODO(crbug.com/dawn/814): Implement for 1D texture.
bool IsArrayLayerValidForTextureViewDimension(
wgpu::TextureViewDimension textureViewDimension,
uint32_t textureViewArrayLayer) {
@ -82,13 +81,13 @@ namespace dawn::native {
return textureViewArrayLayer == 6u;
case wgpu::TextureViewDimension::CubeArray:
return textureViewArrayLayer % 6 == 0;
case wgpu::TextureViewDimension::e1D:
return textureViewArrayLayer == 1u;
case wgpu::TextureViewDimension::Undefined:
break;
}
UNREACHABLE();
}
}
MaybeError ValidateSampleCount(const TextureDescriptor* descriptor,
wgpu::TextureUsage usage,
@ -159,15 +158,14 @@ namespace dawn::native {
texture->GetSize().height);
break;
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::e2D:
case wgpu::TextureViewDimension::e2DArray:
case wgpu::TextureViewDimension::e3D:
break;
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::Undefined:
UNREACHABLE();
break;
}
return {};
@ -362,12 +360,9 @@ namespace dawn::native {
ASSERT(!texture->IsError());
DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension));
DAWN_INVALID_IF(descriptor->dimension == wgpu::TextureViewDimension::e1D,
"1D texture views aren't supported (yet).");
DAWN_TRY(ValidateTextureFormat(descriptor->format));
DAWN_TRY(ValidateTextureAspect(descriptor->aspect));
DAWN_INVALID_IF(
SelectFormatAspects(texture->GetFormat(), descriptor->aspect) == Aspect::None,
"Texture format (%s) does not have the texture view's selected aspect (%s).",

View File

@ -1264,7 +1264,6 @@ namespace dawn::native::d3d12 {
// D3D12_SRV_DIMENSION_TEXTURE2DMS.
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv
// TODO(crbug.com/dawn/814): support 1D textures.
if (GetTexture()->IsMultisampledTexture()) {
switch (descriptor->dimension) {
case wgpu::TextureViewDimension::e2DArray:
@ -1280,6 +1279,13 @@ namespace dawn::native::d3d12 {
}
} else {
switch (descriptor->dimension) {
case wgpu::TextureViewDimension::e1D:
mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
mSrvDesc.Texture1D.MipLevels = descriptor->mipLevelCount;
mSrvDesc.Texture1D.MostDetailedMip = descriptor->baseMipLevel;
mSrvDesc.Texture1D.ResourceMinLODClamp = 0;
break;
case wgpu::TextureViewDimension::e2D:
case wgpu::TextureViewDimension::e2DArray:
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
@ -1310,7 +1316,6 @@ namespace dawn::native::d3d12 {
mSrvDesc.Texture3D.ResourceMinLODClamp = 0;
break;
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::Undefined:
UNREACHABLE();
}
@ -1345,6 +1350,10 @@ namespace dawn::native::d3d12 {
ASSERT(!GetTexture()->IsMultisampledTexture());
switch (GetDimension()) {
case wgpu::TextureViewDimension::e1D:
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D;
uavDesc.Texture1D.MipSlice = GetBaseMipLevel();
break;
case wgpu::TextureViewDimension::e2D:
case wgpu::TextureViewDimension::e2DArray:
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
@ -1359,8 +1368,6 @@ namespace dawn::native::d3d12 {
uavDesc.Texture3D.WSize = GetTexture()->GetDepth() >> GetBaseMipLevel();
uavDesc.Texture3D.MipSlice = GetBaseMipLevel();
break;
// TODO(crbug.com/dawn/814): support 1D textures.
case wgpu::TextureViewDimension::e1D:
// Cube and Cubemap can't be used as storage texture. So there is no need to create UAV
// descriptor for them.
case wgpu::TextureViewDimension::Cube:

View File

@ -66,6 +66,8 @@ namespace dawn::native::metal {
MTLTextureType MetalTextureViewType(wgpu::TextureViewDimension dimension,
unsigned int sampleCount) {
switch (dimension) {
case wgpu::TextureViewDimension::e1D:
return MTLTextureType1D;
case wgpu::TextureViewDimension::e2D:
return (sampleCount > 1) ? MTLTextureType2DMultisample : MTLTextureType2D;
case wgpu::TextureViewDimension::e2DArray:
@ -77,7 +79,6 @@ namespace dawn::native::metal {
case wgpu::TextureViewDimension::e3D:
return MTLTextureType3D;
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::Undefined:
UNREACHABLE();
}

View File

@ -38,6 +38,8 @@ namespace dawn::native::vulkan {
// Contrary to image types, image view types include arrayness and cubemapness
VkImageViewType VulkanImageViewType(wgpu::TextureViewDimension dimension) {
switch (dimension) {
case wgpu::TextureViewDimension::e1D:
return VK_IMAGE_VIEW_TYPE_1D;
case wgpu::TextureViewDimension::e2D:
return VK_IMAGE_VIEW_TYPE_2D;
case wgpu::TextureViewDimension::e2DArray:
@ -49,12 +51,10 @@ namespace dawn::native::vulkan {
case wgpu::TextureViewDimension::e3D:
return VK_IMAGE_VIEW_TYPE_3D;
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::Undefined:
break;
}
UNREACHABLE();
}
}
// Computes which vulkan access type could be required for the given Dawn usage.
// TODO(crbug.com/dawn/269): We shouldn't need any access usages for srcAccessMask when

View File

@ -18,6 +18,7 @@
#include "common/Constants.h"
#include "common/Math.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/TestUtils.h"
#include "utils/TextureUtils.h"
#include "utils/WGPUHelpers.h"
@ -168,6 +169,9 @@ class StorageTextureTests : public DawnTest {
ostream << "@group(0) @binding(" << binding << ") "
<< "var storageImage" << binding << " : ";
switch (dimension) {
case wgpu::TextureViewDimension::e1D:
ostream << "texture_storage_1d";
break;
case wgpu::TextureViewDimension::e2D:
ostream << "texture_storage_2d";
break;
@ -295,56 +299,6 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
return "";
}
std::string CommonReadOnlyTestCode(
wgpu::TextureFormat format,
wgpu::TextureViewDimension dimension = wgpu::TextureViewDimension::e2D) {
std::string componentFmt = utils::GetWGSLColorTextureComponentType(format);
auto texelType = "vec4<" + componentFmt + ">";
std::string sliceCount;
std::string textureLoad;
switch (dimension) {
case wgpu::TextureViewDimension::e2D:
sliceCount = "1";
textureLoad = "textureLoad(storageImage0, vec2<i32>(x, y))";
break;
case wgpu::TextureViewDimension::e2DArray:
sliceCount = "textureNumLayers(storageImage0)";
textureLoad = "textureLoad(storageImage0, vec2<i32>(x, y), i32(slice))";
break;
case wgpu::TextureViewDimension::e3D:
sliceCount = "textureDimensions(storageImage0).z";
textureLoad = "textureLoad(storageImage0, vec3<i32>(x, y, slice))";
break;
default:
UNREACHABLE();
break;
}
std::ostringstream ostream;
ostream << GetImageDeclaration(format, "read", dimension, 0) << "\n"
<< GetComparisonFunction(format) << "\n";
ostream << "fn doTest() -> bool {\n";
ostream << " var size : vec2<i32> = textureDimensions(storageImage0).xy;\n";
ostream << " let sliceCount : i32 = " << sliceCount << ";\n";
ostream << " for (var slice : i32 = 0; slice < sliceCount; slice = slice + 1) {\n";
ostream << " for (var y : i32 = 0; y < size.y; y = y + 1) {\n";
ostream << " for (var x : i32 = 0; x < size.x; x = x + 1) {\n";
ostream << " var value : i32 = " << kComputeExpectedValue << ";\n";
ostream << " var expected : " << texelType << " = " << GetExpectedPixelValue(format)
<< ";\n";
ostream << " var pixel : " << texelType << " = " << textureLoad << ";\n";
ostream << " if (!IsEqualTo(pixel, expected)) {\n";
ostream << " return false;\n";
ostream << " }\n";
ostream << " }\n";
ostream << " }\n";
ostream << " }\n";
ostream << " return true;\n";
ostream << "}\n";
return ostream.str();
}
std::string CommonWriteOnlyTestCode(
const char* stage,
wgpu::TextureFormat format,
@ -353,7 +307,13 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
auto texelType = "vec4<" + componentFmt + ">";
std::string sliceCount;
std::string textureStore;
std::string textureSize = "textureDimensions(storageImage0).xy";
switch (dimension) {
case wgpu::TextureViewDimension::e1D:
sliceCount = "1";
textureStore = "textureStore(storageImage0, x, expected)";
textureSize = "vec2<i32>(textureDimensions(storageImage0), 1)";
break;
case wgpu::TextureViewDimension::e2D:
sliceCount = "1";
textureStore = "textureStore(storageImage0, vec2<i32>(x, y), expected)";
@ -381,7 +341,7 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
ostream << "-> @location(0) vec4<f32> ";
}
ostream << "{\n";
ostream << " let size : vec2<i32> = textureDimensions(storageImage0).xy;\n";
ostream << " let size : vec2<i32> = " << textureSize << ";\n";
ostream << " let sliceCount : i32 = " << sliceCount << ";\n";
ostream << " for (var slice : i32 = 0; slice < sliceCount; slice = slice + 1) {\n";
ostream << " for (var y : i32 = 0; y < size.y; y = y + 1) {\n";
@ -401,53 +361,6 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
return ostream.str();
}
std::string CommonReadWriteTestCode(
wgpu::TextureFormat format,
wgpu::TextureViewDimension dimension = wgpu::TextureViewDimension::e2D) {
std::string sliceCount;
std::string textureStore;
switch (dimension) {
case wgpu::TextureViewDimension::e2D:
sliceCount = "1";
textureStore =
"textureStore(storageImage0, texcoord, "
"textureLoad(storageImage1, texcoord))";
break;
case wgpu::TextureViewDimension::e2DArray:
sliceCount = "textureNumLayers(storageImage0)";
textureStore =
"textureStore(storageImage0, texcoord, slice, "
"textureLoad(storageImage1, texcoord, slice))";
break;
case wgpu::TextureViewDimension::e3D:
sliceCount = "textureDimensions(storageImage0).z";
textureStore =
"textureStore(storageImage0, vec3<i32>(texcoord, slice), "
"textureLoad(storageImage1, vec3<i32>(texcoord, slice)))";
break;
default:
UNREACHABLE();
break;
}
std::ostringstream ostream;
ostream << GetImageDeclaration(format, "write", dimension, 0) << "\n";
ostream << GetImageDeclaration(format, "read", dimension, 1) << "\n";
ostream << "@stage(compute) @workgroup_size(1) fn main() {\n";
ostream << " let size : vec2<i32> = textureDimensions(storageImage0).xy;\n";
ostream << " let sliceCount : i32 = " << sliceCount << ";\n";
ostream << " for (var slice : i32 = 0; slice < sliceCount; slice = slice + 1) {\n";
ostream << " for (var y : i32 = 0; y < size.y; y = y + 1) {\n";
ostream << " for (var x : i32 = 0; x < size.x; x = x + 1) {\n";
ostream << " var texcoord : vec2<i32> = vec2<i32>(x, y);\n";
ostream << " " << textureStore << ";\n";
ostream << " }\n";
ostream << " }\n";
ostream << " }\n";
ostream << "}\n";
return ostream.str();
}
static std::vector<uint8_t> GetExpectedData(wgpu::TextureFormat format,
uint32_t sliceCount = 1) {
const uint32_t texelSizeInBytes = utils::GetTexelBlockSizeInBytes(format);
@ -467,28 +380,16 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
wgpu::Texture CreateTexture(wgpu::TextureFormat format,
wgpu::TextureUsage usage,
uint32_t width = kWidth,
uint32_t height = kHeight,
uint32_t sliceCount = 1,
const wgpu::Extent3D& size,
wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
wgpu::TextureDescriptor descriptor;
descriptor.size = {width, height, sliceCount};
descriptor.size = size;
descriptor.dimension = dimension;
descriptor.format = format;
descriptor.usage = usage;
return device.CreateTexture(&descriptor);
}
wgpu::Buffer CreateEmptyBufferForTextureCopy(uint32_t texelSize, uint32_t sliceCount = 1) {
ASSERT(kWidth * texelSize <= kTextureBytesPerRowAlignment);
const size_t uploadBufferSize =
kTextureBytesPerRowAlignment * (kHeight * sliceCount - 1) + kWidth * texelSize;
wgpu::BufferDescriptor descriptor;
descriptor.size = uploadBufferSize;
descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
return device.CreateBuffer(&descriptor);
}
wgpu::Texture CreateTextureWithTestData(
const std::vector<uint8_t>& initialTextureData,
wgpu::TextureFormat format,
@ -520,8 +421,8 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
wgpu::Texture outputTexture = CreateTexture(
format, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopyDst, kWidth,
kHeight, sliceCount, utils::ViewDimensionToTextureDimension(dimension));
format, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopyDst,
{kWidth, kHeight, sliceCount}, utils::ViewDimensionToTextureDimension(dimension));
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@ -568,9 +469,9 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
device, pipeline.GetBindGroupLayout(0), {{0, readonlyStorageTexture.CreateView()}});
// Clear the render attachment to red at the beginning of the render pass.
wgpu::Texture outputTexture =
CreateTexture(kRenderAttachmentFormat,
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 1, 1);
wgpu::Texture outputTexture = CreateTexture(
kRenderAttachmentFormat,
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, {1, 1});
utils::ComboRenderPassDescriptor renderPassDescriptor({outputTexture.CreateView()});
renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
renderPassDescriptor.cColorAttachments[0].clearColor = {1.f, 0.f, 0.f, 1.f};
@ -634,9 +535,9 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::Texture dummyOutputTexture =
CreateTexture(kRenderAttachmentFormat,
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, 1, 1);
wgpu::Texture dummyOutputTexture = CreateTexture(
kRenderAttachmentFormat,
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, {1, 1});
utils::ComboRenderPassDescriptor renderPassDescriptor({dummyOutputTexture.CreateView()});
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
renderPassEncoder.SetBindGroup(0, bindGroup);
@ -695,40 +596,45 @@ fn IsEqualTo(pixel : vec4<f32>, expected : vec4<f32>) -> bool {
void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture,
wgpu::TextureFormat format,
uint32_t sliceCount = 1) {
const uint32_t texelSize = utils::GetTexelBlockSizeInBytes(format);
const std::vector<uint8_t>& expectedData = GetExpectedData(format, sliceCount);
CheckOutputStorageTexture(writeonlyStorageTexture, texelSize, expectedData);
const wgpu::Extent3D& size) {
const std::vector<uint8_t>& expectedData = GetExpectedData(format, size.depthOrArrayLayers);
CheckOutputStorageTexture(writeonlyStorageTexture, format, size, expectedData);
}
void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture,
uint32_t texelSize,
wgpu::TextureFormat format,
const wgpu::Extent3D& size,
const std::vector<uint8_t>& expectedData) {
// Copy the content from the write-only storage texture to the result buffer.
const uint32_t sliceCount =
static_cast<uint32_t>(expectedData.size() / texelSize / (kWidth * kHeight));
wgpu::Buffer resultBuffer = CreateEmptyBufferForTextureCopy(texelSize, sliceCount);
wgpu::BufferDescriptor descriptor;
descriptor.size =
utils::RequiredBytesInCopy(kTextureBytesPerRowAlignment, size.height, size, format);
descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
wgpu::Buffer resultBuffer = device.CreateBuffer(&descriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
const wgpu::Extent3D copyExtent = {kWidth, kHeight, sliceCount};
{
wgpu::ImageCopyTexture imageCopyTexture =
utils::CreateImageCopyTexture(writeonlyStorageTexture, 0, {0, 0, 0});
wgpu::ImageCopyBuffer imageCopyBuffer =
utils::CreateImageCopyBuffer(resultBuffer, 0, kTextureBytesPerRowAlignment, kHeight);
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &copyExtent);
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(
resultBuffer, 0, kTextureBytesPerRowAlignment, size.height);
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &size);
}
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
// Check if the contents in the result buffer are what we expect.
for (size_t slice = 0; slice < sliceCount; ++slice) {
for (size_t y = 0; y < kHeight; ++y) {
uint32_t texelSize = utils::GetTexelBlockSizeInBytes(format);
ASSERT(size.width * texelSize <= kTextureBytesPerRowAlignment);
for (size_t z = 0; z < size.depthOrArrayLayers; ++z) {
for (size_t y = 0; y < size.height; ++y) {
const size_t resultBufferOffset =
kTextureBytesPerRowAlignment * (kHeight * slice + y);
const size_t expectedDataOffset = texelSize * kWidth * (kHeight * slice + y);
kTextureBytesPerRowAlignment * (size.height * z + y);
const size_t expectedDataOffset = texelSize * size.width * (size.height * z + y);
EXPECT_BUFFER_U32_RANGE_EQ(
reinterpret_cast<const uint32_t*>(expectedData.data() + expectedDataOffset),
resultBuffer, resultBufferOffset, kWidth);
resultBuffer, resultBufferOffset, texelSize);
}
}
}
@ -769,14 +675,15 @@ TEST_P(StorageTextureTests, WriteonlyStorageTextureInComputeShader) {
// Prepare the write-only storage texture.
wgpu::Texture writeonlyStorageTexture =
CreateTexture(format, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc);
CreateTexture(format, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc,
{kWidth, kHeight});
// Write the expected pixel values into the write-only storage texture.
const std::string computeShader = CommonWriteOnlyTestCode("compute", format);
WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, computeShader.c_str());
// Verify the pixel data in the write-only storage texture is expected.
CheckOutputStorageTexture(writeonlyStorageTexture, format);
CheckOutputStorageTexture(writeonlyStorageTexture, format, {kWidth, kHeight});
}
}
@ -807,7 +714,8 @@ TEST_P(StorageTextureTests, WriteonlyStorageTextureInFragmentShader) {
// Prepare the write-only storage texture.
wgpu::Texture writeonlyStorageTexture =
CreateTexture(format, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc);
CreateTexture(format, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc,
{kWidth, kHeight});
// Write the expected pixel values into the write-only storage texture.
const std::string fragmentShader = CommonWriteOnlyTestCode("fragment", format);
@ -815,7 +723,7 @@ TEST_P(StorageTextureTests, WriteonlyStorageTextureInFragmentShader) {
fragmentShader.c_str());
// Verify the pixel data in the write-only storage texture is expected.
CheckOutputStorageTexture(writeonlyStorageTexture, format);
CheckOutputStorageTexture(writeonlyStorageTexture, format, {kWidth, kHeight});
}
}
@ -837,7 +745,7 @@ TEST_P(StorageTextureTests, Writeonly2DArrayOr3DStorageTexture) {
for (wgpu::TextureViewDimension dimension : dimensions) {
wgpu::Texture writeonlyStorageTexture = CreateTexture(
kTextureFormat, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc,
kWidth, kHeight, kSliceCount, utils::ViewDimensionToTextureDimension(dimension));
{kWidth, kHeight, kSliceCount}, utils::ViewDimensionToTextureDimension(dimension));
// Write the expected pixel values into the write-only storage texture.
const std::string computeShader =
@ -846,10 +754,33 @@ TEST_P(StorageTextureTests, Writeonly2DArrayOr3DStorageTexture) {
dimension);
// Verify the pixel data in the write-only storage texture is expected.
CheckOutputStorageTexture(writeonlyStorageTexture, kTextureFormat, kSliceCount);
CheckOutputStorageTexture(writeonlyStorageTexture, kTextureFormat,
{kWidth, kHeight, kSliceCount});
}
}
// Verify 1D write-only storage textures work correctly.
TEST_P(StorageTextureTests, Writeonly1DStorageTexture) {
// TODO(crbug.com/dawn/547): implement 1D storage texture on OpenGL and OpenGLES.
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL() || IsOpenGLES());
constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint;
// Prepare the write-only storage texture.
wgpu::Texture writeonlyStorageTexture = CreateTexture(
kTextureFormat, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc,
{kWidth, 1, 1}, wgpu::TextureDimension::e1D);
// Write the expected pixel values into the write-only storage texture.
const std::string computeShader =
CommonWriteOnlyTestCode("compute", kTextureFormat, wgpu::TextureViewDimension::e1D);
WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, computeShader.c_str(),
wgpu::TextureViewDimension::e1D);
// Verify the pixel data in the write-only storage texture is expected.
CheckOutputStorageTexture(writeonlyStorageTexture, kTextureFormat, {kWidth, 1, 1});
}
// Test that multiple dispatches to increment values by ping-ponging between a sampled texture and
// a write-only storage texture are synchronized in one pass.
TEST_P(StorageTextureTests, SampledAndWriteonlyStorageTexturePingPong) {
@ -858,10 +789,10 @@ TEST_P(StorageTextureTests, SampledAndWriteonlyStorageTexturePingPong) {
CreateTexture(kTextureFormat,
wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding |
wgpu::TextureUsage::CopySrc,
1u, 1u);
{1u, 1u});
wgpu::Texture storageTexture2 = CreateTexture(
kTextureFormat, wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding, 1u,
1u);
kTextureFormat, wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::StorageBinding,
{1u, 1u});
wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
@group(0) @binding(0) var Src : texture_2d<u32>;
@group(0) @binding(1) var Dst : texture_storage_2d<r32uint, write>;
@ -980,28 +911,28 @@ fn doTest() -> bool {
// storage texture in a render pass.
TEST_P(StorageTextureZeroInitTests, WriteonlyStorageTextureClearsToZeroInRenderPass) {
// Prepare the write-only storage texture.
constexpr uint32_t kTexelSizeR32Uint = 4u;
wgpu::Texture writeonlyStorageTexture =
CreateTexture(wgpu::TextureFormat::R32Uint,
wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc);
wgpu::Texture writeonlyStorageTexture = CreateTexture(
wgpu::TextureFormat::R32Uint,
wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc, {kWidth, kHeight});
WriteIntoStorageTextureInRenderPass(writeonlyStorageTexture, kSimpleVertexShader,
kCommonWriteOnlyZeroInitTestCodeFragment);
CheckOutputStorageTexture(writeonlyStorageTexture, kTexelSizeR32Uint, GetExpectedData());
CheckOutputStorageTexture(writeonlyStorageTexture, wgpu::TextureFormat::R32Uint,
{kWidth, kHeight}, GetExpectedData());
}
// Verify that the texture is correctly cleared to 0 before its first usage as a write-only storage
// texture in a compute pass.
TEST_P(StorageTextureZeroInitTests, WriteonlyStorageTextureClearsToZeroInComputePass) {
// Prepare the write-only storage texture.
constexpr uint32_t kTexelSizeR32Uint = 4u;
wgpu::Texture writeonlyStorageTexture =
CreateTexture(wgpu::TextureFormat::R32Uint,
wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc);
wgpu::Texture writeonlyStorageTexture = CreateTexture(
wgpu::TextureFormat::R32Uint,
wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc, {kWidth, kHeight});
WriteIntoStorageTextureInComputePass(writeonlyStorageTexture,
kCommonWriteOnlyZeroInitTestCodeCompute);
CheckOutputStorageTexture(writeonlyStorageTexture, kTexelSizeR32Uint, GetExpectedData());
CheckOutputStorageTexture(writeonlyStorageTexture, wgpu::TextureFormat::R32Uint,
{kWidth, kHeight}, GetExpectedData());
}
DAWN_INSTANTIATE_TEST(StorageTextureZeroInitTests,

View File

@ -718,3 +718,71 @@ DAWN_INSTANTIATE_TEST(TextureView3DTest,
OpenGLBackend(),
OpenGLESBackend(),
VulkanBackend());
class TextureView1DTest : public DawnTest {};
// Test that it is possible to create a 1D texture view and sample from it.
TEST_P(TextureView1DTest, Sampling) {
// Create a 1D texture and fill it with some data.
wgpu::TextureDescriptor texDesc;
texDesc.dimension = wgpu::TextureDimension::e1D;
texDesc.format = wgpu::TextureFormat::RGBA8Unorm;
texDesc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst;
texDesc.size = {4, 1, 1};
wgpu::Texture tex = device.CreateTexture(&texDesc);
std::array<RGBA8, 4> data = {RGBA8::kGreen, RGBA8::kRed, RGBA8::kBlue, RGBA8::kWhite};
wgpu::ImageCopyTexture target = utils::CreateImageCopyTexture(tex, 0, {});
wgpu::TextureDataLayout layout = utils::CreateTextureDataLayout(0, wgpu::kCopyStrideUndefined);
queue.WriteTexture(&target, &data, sizeof(data), &layout, &texDesc.size);
// Create a pipeline that will sample from the 1D texture and output to an attachment.
wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
@stage(vertex)
fn vs(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
var pos = array<vec4<f32>, 3>(
vec4<f32>( 0., 2., 0., 1.),
vec4<f32>(-3., -1., 0., 1.),
vec4<f32>( 3., -1., 0., 1.));
return pos[VertexIndex];
}
@group(0) @binding(0) var tex : texture_1d<f32>;
@group(0) @binding(1) var samp : sampler;
@stage(fragment)
fn fs(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {
return textureSample(tex, samp, pos.x / 4.0);
}
)");
utils::ComboRenderPipelineDescriptor pDesc;
pDesc.vertex.module = module;
pDesc.vertex.entryPoint = "vs";
pDesc.cFragment.module = module;
pDesc.cFragment.entryPoint = "fs";
pDesc.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pDesc);
// Do the sample + rendering.
wgpu::BindGroup bg = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, tex.CreateView()}, {1, device.CreateSampler()}});
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
utils::BasicRenderPass rp = utils::CreateBasicRenderPass(device, 4, 1);
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&rp.renderPassInfo);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bg);
pass.Draw(3);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
// Check texels got sampled correctly.
EXPECT_PIXEL_RGBA8_EQ(data[0], rp.color, 0, 0);
EXPECT_PIXEL_RGBA8_EQ(data[1], rp.color, 1, 0);
EXPECT_PIXEL_RGBA8_EQ(data[2], rp.color, 2, 0);
EXPECT_PIXEL_RGBA8_EQ(data[3], rp.color, 3, 0);
}
DAWN_INSTANTIATE_TEST(TextureView1DTest, D3D12Backend(), MetalBackend(), VulkanBackend());

View File

@ -54,6 +54,15 @@ namespace {
return device.CreateTexture(&descriptor);
}
wgpu::Texture Create1DTexture(wgpu::Device& device) {
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e1D;
descriptor.size = {kWidth, 1, 1};
descriptor.format = kDefaultTextureFormat;
descriptor.usage = wgpu::TextureUsage::TextureBinding;
return device.CreateTexture(&descriptor);
}
wgpu::Texture CreateDepthStencilTexture(wgpu::Device& device, wgpu::TextureFormat format) {
wgpu::TextureDescriptor descriptor = {};
descriptor.size = {kWidth, kHeight, kDepth};
@ -69,7 +78,9 @@ namespace {
descriptor.format = kDefaultTextureFormat;
descriptor.dimension = dimension;
descriptor.baseMipLevel = 0;
if (dimension != wgpu::TextureViewDimension::e1D) {
descriptor.mipLevelCount = kDefaultMipLevels;
}
descriptor.baseArrayLayer = 0;
descriptor.arrayLayerCount = 1;
return descriptor;
@ -208,6 +219,14 @@ namespace {
ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
}
// It is an error to create a 1D texture view on a 2D array texture.
{
wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor;
descriptor.dimension = wgpu::TextureViewDimension::e1D;
descriptor.arrayLayerCount = 1;
ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
}
// baseArrayLayer == k && arrayLayerCount == wgpu::kArrayLayerCountUndefined means to use
// layers k..end.
{
@ -269,12 +288,11 @@ namespace {
texture.CreateView(&descriptor);
}
// It is an error to create a 2D/2DArray/Cube/CubeArray texture view on a 3D texture.
// It is an error to create a 1D/2D/2DArray/Cube/CubeArray texture view on a 3D texture.
{
wgpu::TextureViewDimension invalidDimensions[] = {
wgpu::TextureViewDimension::e2D,
wgpu::TextureViewDimension::e2DArray,
wgpu::TextureViewDimension::Cube,
wgpu::TextureViewDimension::e1D, wgpu::TextureViewDimension::e2D,
wgpu::TextureViewDimension::e2DArray, wgpu::TextureViewDimension::Cube,
wgpu::TextureViewDimension::CubeArray,
};
for (wgpu::TextureViewDimension dimension : invalidDimensions) {
@ -342,6 +360,51 @@ namespace {
}
}
// Test creating texture view on a 1D texture
TEST_F(TextureViewValidationTest, CreateTextureViewOnTexture1D) {
wgpu::Texture texture = Create1DTexture(device);
wgpu::TextureViewDescriptor base1DTextureViewDescriptor =
CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e1D);
// It is an error to create a view with zero 'arrayLayerCount'.
{
wgpu::TextureViewDescriptor descriptor = base1DTextureViewDescriptor;
descriptor.arrayLayerCount = 0;
ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
}
// It is an error to create a view with zero 'mipLevelCount'.
{
wgpu::TextureViewDescriptor descriptor = base1DTextureViewDescriptor;
descriptor.mipLevelCount = 0;
ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
}
// It is OK to create a 1D texture view on a 1D texture.
{
wgpu::TextureViewDescriptor descriptor = base1DTextureViewDescriptor;
texture.CreateView(&descriptor);
}
// It is an error to create a 2D/2DArray/Cube/CubeArray/3D texture view on a 1D texture.
{
wgpu::TextureViewDimension invalidDimensions[] = {
wgpu::TextureViewDimension::e2D, wgpu::TextureViewDimension::e2DArray,
wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray,
wgpu::TextureViewDimension::e3D,
};
for (wgpu::TextureViewDimension dimension : invalidDimensions) {
wgpu::TextureViewDescriptor descriptor = base1DTextureViewDescriptor;
descriptor.dimension = dimension;
ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor));
}
}
// No tests for setting mip levels / array layer ranges because 1D textures can only have
// a single mip and layer.
}
// Using the "none" ("default") values validates the same as explicitly
// specifying the values they're supposed to default to.
// Variant for a 2D texture with more than 1 array layer.