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:
parent
310686b795
commit
42e648c1cf
|
@ -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();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// TODO(crbug.com/dawn/814): Implement for 1D texture.
|
||||
bool IsArrayLayerValidForTextureViewDimension(
|
||||
wgpu::TextureViewDimension textureViewDimension,
|
||||
uint32_t textureViewArrayLayer) {
|
||||
|
@ -82,12 +81,12 @@ 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();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
MaybeError ValidateSampleCount(const TextureDescriptor* descriptor,
|
||||
|
@ -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).",
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,11 +51,9 @@ namespace dawn::native::vulkan {
|
|||
case wgpu::TextureViewDimension::e3D:
|
||||
return VK_IMAGE_VIEW_TYPE_3D;
|
||||
|
||||
case wgpu::TextureViewDimension::e1D:
|
||||
case wgpu::TextureViewDimension::Undefined:
|
||||
break;
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Computes which vulkan access type could be required for the given Dawn usage.
|
||||
|
|
|
@ -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, ©Extent);
|
||||
{
|
||||
wgpu::ImageCopyTexture imageCopyTexture =
|
||||
utils::CreateImageCopyTexture(writeonlyStorageTexture, 0, {0, 0, 0});
|
||||
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,
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
descriptor.mipLevelCount = kDefaultMipLevels;
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue