mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-13 10:03:41 +00:00
This case verifies that reading from one read-only storage texture then writing into another write-only storage texture in one dispatch are supported in compute shader. Bug: dawn:458 Change-Id: If1b4c13da067fa39b45a378b54c22a4162695c8d Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23040 Commit-Queue: Jiawei Shao <jiawei.shao@intel.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
1163 lines
52 KiB
C++
1163 lines
52 KiB
C++
// Copyright 2020 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "tests/DawnTest.h"
|
|
|
|
#include "common/Assert.h"
|
|
#include "common/Constants.h"
|
|
#include "common/Math.h"
|
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/TextureFormatUtils.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
class StorageTextureTests : public DawnTest {
|
|
public:
|
|
static void FillExpectedData(void* pixelValuePtr,
|
|
wgpu::TextureFormat format,
|
|
uint32_t x,
|
|
uint32_t y,
|
|
uint32_t arrayLayer) {
|
|
const uint32_t pixelValue = 1 + x + kWidth * (y + kHeight * arrayLayer);
|
|
ASSERT(pixelValue <= 255u / 4);
|
|
|
|
switch (format) {
|
|
// 32-bit unsigned integer formats
|
|
case wgpu::TextureFormat::R32Uint: {
|
|
uint32_t* valuePtr = static_cast<uint32_t*>(pixelValuePtr);
|
|
*valuePtr = pixelValue;
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RG32Uint: {
|
|
uint32_t* valuePtr = static_cast<uint32_t*>(pixelValuePtr);
|
|
valuePtr[0] = pixelValue;
|
|
valuePtr[1] = pixelValue * 2;
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RGBA32Uint: {
|
|
uint32_t* valuePtr = static_cast<uint32_t*>(pixelValuePtr);
|
|
valuePtr[0] = pixelValue;
|
|
valuePtr[1] = pixelValue * 2;
|
|
valuePtr[2] = pixelValue * 3;
|
|
valuePtr[3] = pixelValue * 4;
|
|
break;
|
|
}
|
|
|
|
// 32-bit signed integer formats
|
|
case wgpu::TextureFormat::R32Sint: {
|
|
int32_t* valuePtr = static_cast<int32_t*>(pixelValuePtr);
|
|
*valuePtr = static_cast<int32_t>(pixelValue);
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RG32Sint: {
|
|
int32_t* valuePtr = static_cast<int32_t*>(pixelValuePtr);
|
|
valuePtr[0] = static_cast<int32_t>(pixelValue);
|
|
valuePtr[1] = -static_cast<int32_t>(pixelValue);
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RGBA32Sint: {
|
|
int32_t* valuePtr = static_cast<int32_t*>(pixelValuePtr);
|
|
valuePtr[0] = static_cast<int32_t>(pixelValue);
|
|
valuePtr[1] = -static_cast<int32_t>(pixelValue);
|
|
valuePtr[2] = static_cast<int32_t>(pixelValue * 2);
|
|
valuePtr[3] = -static_cast<int32_t>(pixelValue * 2);
|
|
break;
|
|
}
|
|
|
|
// 32-bit float formats
|
|
case wgpu::TextureFormat::R32Float: {
|
|
float_t* valuePtr = static_cast<float_t*>(pixelValuePtr);
|
|
*valuePtr = static_cast<float_t>(pixelValue * 1.1f);
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RG32Float: {
|
|
float_t* valuePtr = static_cast<float_t*>(pixelValuePtr);
|
|
valuePtr[0] = static_cast<float_t>(pixelValue * 1.1f);
|
|
valuePtr[1] = -static_cast<float_t>(pixelValue * 2.2f);
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RGBA32Float: {
|
|
float_t* valuePtr = static_cast<float_t*>(pixelValuePtr);
|
|
valuePtr[0] = static_cast<float_t>(pixelValue * 1.1f);
|
|
valuePtr[1] = -static_cast<float_t>(pixelValue * 1.1f);
|
|
valuePtr[2] = static_cast<float_t>(pixelValue * 2.2f);
|
|
valuePtr[3] = -static_cast<float_t>(pixelValue * 2.2f);
|
|
break;
|
|
}
|
|
|
|
// 16-bit (unsigned integer, signed integer and float) 4-component formats
|
|
case wgpu::TextureFormat::RGBA16Uint: {
|
|
uint16_t* valuePtr = static_cast<uint16_t*>(pixelValuePtr);
|
|
valuePtr[0] = static_cast<uint16_t>(pixelValue);
|
|
valuePtr[1] = static_cast<uint16_t>(pixelValue * 2);
|
|
valuePtr[2] = static_cast<uint16_t>(pixelValue * 3);
|
|
valuePtr[3] = static_cast<uint16_t>(pixelValue * 4);
|
|
break;
|
|
}
|
|
case wgpu::TextureFormat::RGBA16Sint: {
|
|
int16_t* valuePtr = static_cast<int16_t*>(pixelValuePtr);
|
|
valuePtr[0] = static_cast<int16_t>(pixelValue);
|
|
valuePtr[1] = -static_cast<int16_t>(pixelValue);
|
|
valuePtr[2] = static_cast<int16_t>(pixelValue * 2);
|
|
valuePtr[3] = -static_cast<int16_t>(pixelValue * 2);
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RGBA16Float: {
|
|
uint16_t* valuePtr = static_cast<uint16_t*>(pixelValuePtr);
|
|
valuePtr[0] = Float32ToFloat16(static_cast<float_t>(pixelValue));
|
|
valuePtr[1] = Float32ToFloat16(-static_cast<float_t>(pixelValue));
|
|
valuePtr[2] = Float32ToFloat16(static_cast<float_t>(pixelValue * 2));
|
|
valuePtr[3] = Float32ToFloat16(-static_cast<float_t>(pixelValue * 2));
|
|
break;
|
|
}
|
|
|
|
// 8-bit (normalized/non-normalized signed/unsigned integer) 4-component formats
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
case wgpu::TextureFormat::RGBA8Uint: {
|
|
RGBA8* valuePtr = static_cast<RGBA8*>(pixelValuePtr);
|
|
*valuePtr = RGBA8(pixelValue, pixelValue * 2, pixelValue * 3, pixelValue * 4);
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureFormat::RGBA8Snorm:
|
|
case wgpu::TextureFormat::RGBA8Sint: {
|
|
int8_t* valuePtr = static_cast<int8_t*>(pixelValuePtr);
|
|
valuePtr[0] = static_cast<int8_t>(pixelValue);
|
|
valuePtr[1] = -static_cast<int8_t>(pixelValue);
|
|
valuePtr[2] = static_cast<int8_t>(pixelValue) * 2;
|
|
valuePtr[3] = -static_cast<int8_t>(pixelValue) * 2;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::string GetGLSLImageDeclaration(wgpu::TextureFormat format,
|
|
std::string accessQualifier,
|
|
bool is2DArray,
|
|
uint32_t binding) {
|
|
std::ostringstream ostream;
|
|
ostream << "layout(set = 0, binding = " << binding << ", "
|
|
<< utils::GetGLSLImageFormatQualifier(format) << ") uniform " << accessQualifier
|
|
<< " " << utils::GetColorTextureComponentTypePrefix(format) << "image2D";
|
|
if (is2DArray) {
|
|
ostream << "Array";
|
|
}
|
|
ostream << " storageImage" << binding << ";";
|
|
return ostream.str();
|
|
}
|
|
|
|
const char* GetExpectedPixelValue(wgpu::TextureFormat format) {
|
|
switch (format) {
|
|
// non-normalized unsigned integer formats
|
|
case wgpu::TextureFormat::R32Uint:
|
|
return "uvec4(value, 0, 0, 1u)";
|
|
|
|
case wgpu::TextureFormat::RG32Uint:
|
|
return "uvec4(value, value * 2, 0, 1);";
|
|
|
|
case wgpu::TextureFormat::RGBA8Uint:
|
|
case wgpu::TextureFormat::RGBA16Uint:
|
|
case wgpu::TextureFormat::RGBA32Uint:
|
|
return "uvec4(value, value * 2, value * 3, value * 4);";
|
|
|
|
// non-normalized signed integer formats
|
|
case wgpu::TextureFormat::R32Sint:
|
|
return "ivec4(value, 0, 0, 1)";
|
|
|
|
case wgpu::TextureFormat::RG32Sint:
|
|
return "ivec4(value, -value, 0, 1);";
|
|
|
|
case wgpu::TextureFormat::RGBA8Sint:
|
|
case wgpu::TextureFormat::RGBA16Sint:
|
|
case wgpu::TextureFormat::RGBA32Sint:
|
|
return "ivec4(value, -value, value * 2, -value * 2);";
|
|
|
|
// float formats
|
|
case wgpu::TextureFormat::R32Float:
|
|
return "vec4(value * 1.1f, 0, 0, 1);";
|
|
|
|
case wgpu::TextureFormat::RG32Float:
|
|
return "vec4(value * 1.1f, -(value * 2.2f), 0, 1);";
|
|
|
|
case wgpu::TextureFormat::RGBA16Float:
|
|
return "vec4(value, -float(value), float(value * 2), -float(value * 2));";
|
|
|
|
case wgpu::TextureFormat::RGBA32Float:
|
|
return "vec4(value * 1.1f, -(value * 1.1f), value * 2.2f, -(value * 2.2f));";
|
|
|
|
// normalized signed/unsigned integer formats
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
return "vec4(value / 255.0, value / 255.0 * 2, value / 255.0 * 3, value / 255.0 * "
|
|
"4);";
|
|
|
|
case wgpu::TextureFormat::RGBA8Snorm:
|
|
return "vec4(value / 127.0, -(value / 127.0), (value * 2 / 127.0), -(value * 2 / "
|
|
"127.0));";
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char* GetGLSLComparisonFunction(wgpu::TextureFormat format) {
|
|
switch (format) {
|
|
// non-normalized unsigned integer formats
|
|
case wgpu::TextureFormat::R32Uint:
|
|
case wgpu::TextureFormat::RG32Uint:
|
|
case wgpu::TextureFormat::RGBA8Uint:
|
|
case wgpu::TextureFormat::RGBA16Uint:
|
|
case wgpu::TextureFormat::RGBA32Uint:
|
|
return R"(bool IsEqualTo(uvec4 pixel, uvec4 expected) {
|
|
return pixel == expected;
|
|
})";
|
|
|
|
// non-normalized signed integer formats
|
|
case wgpu::TextureFormat::R32Sint:
|
|
case wgpu::TextureFormat::RG32Sint:
|
|
case wgpu::TextureFormat::RGBA8Sint:
|
|
case wgpu::TextureFormat::RGBA16Sint:
|
|
case wgpu::TextureFormat::RGBA32Sint:
|
|
return R"(bool IsEqualTo(ivec4 pixel, ivec4 expected) {
|
|
return pixel == expected;
|
|
})";
|
|
|
|
// float formats
|
|
case wgpu::TextureFormat::R32Float:
|
|
case wgpu::TextureFormat::RG32Float:
|
|
case wgpu::TextureFormat::RGBA16Float:
|
|
case wgpu::TextureFormat::RGBA32Float:
|
|
return R"(bool IsEqualTo(vec4 pixel, vec4 expected) {
|
|
return pixel == expected;
|
|
})";
|
|
|
|
// normalized signed/unsigned integer formats
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
case wgpu::TextureFormat::RGBA8Snorm:
|
|
// On Windows Intel drivers the tests will fail if tolerance <= 0.00000001f.
|
|
return R"(bool IsEqualTo(vec4 pixel, vec4 expected) {
|
|
const float tolerance = 0.0000001f;
|
|
return all(lessThan(abs(pixel - expected), vec4(tolerance)));
|
|
})";
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
std::string CommonReadOnlyTestCode(wgpu::TextureFormat format, bool is2DArray = false) {
|
|
std::ostringstream ostream;
|
|
|
|
const char* prefix = utils::GetColorTextureComponentTypePrefix(format);
|
|
|
|
ostream << GetGLSLImageDeclaration(format, "readonly", is2DArray, 0) << "\n"
|
|
<< GetGLSLComparisonFunction(format) << "bool doTest() {\n";
|
|
if (is2DArray) {
|
|
ostream << R"(ivec3 size = imageSize(storageImage0);
|
|
const uint layerCount = size.z;)";
|
|
} else {
|
|
ostream << R"(ivec2 size = imageSize(storageImage0);
|
|
const uint layerCount = 1;)";
|
|
}
|
|
ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) {
|
|
for (uint y = 0; y < size.y; ++y) {
|
|
for (uint x = 0; x < size.x; ++x) {
|
|
uint value = )"
|
|
<< kComputeExpectedValueGLSL << ";\n"
|
|
<< prefix << "vec4 expected = " << GetExpectedPixelValue(format) << ";\n"
|
|
<< prefix << R"(vec4 pixel = imageLoad(storageImage0, )";
|
|
if (is2DArray) {
|
|
ostream << "ivec3(x, y, layer));";
|
|
} else {
|
|
ostream << "ivec2(x, y));";
|
|
}
|
|
ostream << R"(
|
|
if (!IsEqualTo(pixel, expected)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
})";
|
|
|
|
return ostream.str();
|
|
}
|
|
|
|
std::string CommonWriteOnlyTestCode(wgpu::TextureFormat format, bool is2DArray = false) {
|
|
std::ostringstream ostream;
|
|
|
|
const char* prefix = utils::GetColorTextureComponentTypePrefix(format);
|
|
|
|
ostream << R"(
|
|
#version 450
|
|
)" << GetGLSLImageDeclaration(format, "writeonly", is2DArray, 0)
|
|
<< R"(
|
|
void main() {
|
|
)";
|
|
if (is2DArray) {
|
|
ostream << R"(ivec3 size = imageSize(storageImage0);
|
|
const uint layerCount = size.z;
|
|
)";
|
|
} else {
|
|
ostream << R"(ivec2 size = imageSize(storageImage0);
|
|
const uint layerCount = 1;
|
|
)";
|
|
}
|
|
|
|
ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) {
|
|
for (uint y = 0; y < size.y; ++y) {
|
|
for (uint x = 0; x < size.x; ++x) {
|
|
uint value = )"
|
|
<< kComputeExpectedValueGLSL << ";\n"
|
|
<< prefix << "vec4 expected = " << GetExpectedPixelValue(format) << ";\n";
|
|
if (is2DArray) {
|
|
ostream << "ivec3 texcoord = ivec3(x, y, layer);\n";
|
|
} else {
|
|
ostream << "ivec2 texcoord = ivec2(x, y);\n";
|
|
}
|
|
|
|
ostream << R"( imageStore(storageImage0, texcoord, expected);
|
|
}
|
|
}
|
|
}
|
|
})";
|
|
|
|
return ostream.str();
|
|
}
|
|
|
|
std::string CommonReadWriteTestCode(wgpu::TextureFormat format, bool is2DArray = false) {
|
|
std::ostringstream ostream;
|
|
|
|
ostream << R"(
|
|
#version 450
|
|
)" << GetGLSLImageDeclaration(format, "writeonly", is2DArray, 0)
|
|
<< GetGLSLImageDeclaration(format, "readonly", is2DArray, 1) << R"(
|
|
void main() {
|
|
)";
|
|
if (is2DArray) {
|
|
ostream << R"(ivec3 size = imageSize(storageImage0);
|
|
const uint layerCount = size.z;
|
|
)";
|
|
} else {
|
|
ostream << R"(ivec2 size = imageSize(storageImage0);
|
|
const uint layerCount = 1;
|
|
)";
|
|
}
|
|
|
|
ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) {
|
|
for (uint y = 0; y < size.y; ++y) {
|
|
for (uint x = 0; x < size.x; ++x) {)"
|
|
"\n";
|
|
if (is2DArray) {
|
|
ostream << "ivec3 texcoord = ivec3(x, y, layer);\n";
|
|
} else {
|
|
ostream << "ivec2 texcoord = ivec2(x, y);\n";
|
|
}
|
|
|
|
ostream
|
|
<< R"( imageStore(storageImage0, texcoord, imageLoad(storageImage1, texcoord));
|
|
}
|
|
}
|
|
}
|
|
})";
|
|
return ostream.str();
|
|
}
|
|
|
|
static std::vector<uint8_t> GetExpectedData(wgpu::TextureFormat format,
|
|
uint32_t arrayLayerCount = 1) {
|
|
const uint32_t texelSizeInBytes = utils::GetTexelBlockSizeInBytes(format);
|
|
|
|
std::vector<uint8_t> outputData(texelSizeInBytes * kWidth * kHeight * arrayLayerCount);
|
|
|
|
for (uint32_t i = 0; i < outputData.size() / texelSizeInBytes; ++i) {
|
|
uint8_t* pixelValuePtr = &outputData[i * texelSizeInBytes];
|
|
const uint32_t x = i % kWidth;
|
|
const uint32_t y = (i % (kWidth * kHeight)) / kWidth;
|
|
const uint32_t arrayLayer = i / (kWidth * kHeight);
|
|
FillExpectedData(pixelValuePtr, format, x, y, arrayLayer);
|
|
}
|
|
|
|
return outputData;
|
|
}
|
|
|
|
wgpu::Texture CreateTexture(wgpu::TextureFormat format,
|
|
wgpu::TextureUsage usage,
|
|
uint32_t width = kWidth,
|
|
uint32_t height = kHeight,
|
|
uint32_t arrayLayerCount = 1) {
|
|
wgpu::TextureDescriptor descriptor;
|
|
descriptor.size = {width, height, 1};
|
|
descriptor.format = format;
|
|
descriptor.usage = usage;
|
|
descriptor.arrayLayerCount = arrayLayerCount;
|
|
return device.CreateTexture(&descriptor);
|
|
}
|
|
|
|
wgpu::Buffer CreateEmptyBufferForTextureCopy(uint32_t texelSize, uint32_t arrayLayerCount = 1) {
|
|
ASSERT(kWidth * texelSize <= kTextureBytesPerRowAlignment);
|
|
const size_t uploadBufferSize =
|
|
kTextureBytesPerRowAlignment * (kHeight * arrayLayerCount - 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) {
|
|
uint32_t texelSize = utils::GetTexelBlockSizeInBytes(format);
|
|
ASSERT(kWidth * texelSize <= kTextureBytesPerRowAlignment);
|
|
|
|
const uint32_t bytesPerTextureRow = texelSize * kWidth;
|
|
const uint32_t arrayLayerCount =
|
|
static_cast<uint32_t>(initialTextureData.size() / texelSize / (kWidth * kHeight));
|
|
const size_t uploadBufferSize =
|
|
kTextureBytesPerRowAlignment * (kHeight * arrayLayerCount - 1) +
|
|
kWidth * bytesPerTextureRow;
|
|
|
|
std::vector<uint8_t> uploadBufferData(uploadBufferSize);
|
|
for (uint32_t layer = 0; layer < arrayLayerCount; ++layer) {
|
|
const size_t initialDataOffset = bytesPerTextureRow * kHeight * layer;
|
|
for (size_t y = 0; y < kHeight; ++y) {
|
|
for (size_t x = 0; x < bytesPerTextureRow; ++x) {
|
|
uint8_t data =
|
|
initialTextureData[initialDataOffset + bytesPerTextureRow * y + x];
|
|
size_t indexInUploadBuffer =
|
|
(kHeight * layer + y) * kTextureBytesPerRowAlignment + x;
|
|
uploadBufferData[indexInUploadBuffer] = data;
|
|
}
|
|
}
|
|
}
|
|
wgpu::Buffer uploadBuffer =
|
|
utils::CreateBufferFromData(device, uploadBufferData.data(), uploadBufferSize,
|
|
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
wgpu::Texture outputTexture =
|
|
CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopyDst, kWidth,
|
|
kHeight, arrayLayerCount);
|
|
|
|
const wgpu::Extent3D copyExtent = {kWidth, kHeight, 1};
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
// TODO(jiawei.shao@intel.com): copy multiple array layers in one CopyBufferToTexture() when
|
|
// it is supported.
|
|
for (uint32_t layer = 0; layer < arrayLayerCount; ++layer) {
|
|
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
|
|
uploadBuffer, kTextureBytesPerRowAlignment * kHeight * layer,
|
|
kTextureBytesPerRowAlignment, 0);
|
|
|
|
wgpu::TextureCopyView textureCopyView;
|
|
textureCopyView.texture = outputTexture;
|
|
textureCopyView.arrayLayer = layer;
|
|
|
|
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Extent);
|
|
}
|
|
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
|
|
return outputTexture;
|
|
}
|
|
|
|
wgpu::ComputePipeline CreateComputePipeline(const char* computeShader) {
|
|
wgpu::ShaderModule csModule =
|
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
|
|
wgpu::ComputePipelineDescriptor computeDescriptor;
|
|
computeDescriptor.layout = nullptr;
|
|
computeDescriptor.computeStage.module = csModule;
|
|
computeDescriptor.computeStage.entryPoint = "main";
|
|
return device.CreateComputePipeline(&computeDescriptor);
|
|
}
|
|
|
|
wgpu::RenderPipeline CreateRenderPipeline(const char* vertexShader,
|
|
const char* fragmentShader) {
|
|
wgpu::ShaderModule vsModule =
|
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader);
|
|
wgpu::ShaderModule fsModule =
|
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader);
|
|
|
|
utils::ComboRenderPipelineDescriptor desc(device);
|
|
desc.vertexStage.module = vsModule;
|
|
desc.cFragmentStage.module = fsModule;
|
|
desc.cColorStates[0].format = kOutputAttachmentFormat;
|
|
desc.primitiveTopology = wgpu::PrimitiveTopology::PointList;
|
|
return device.CreateRenderPipeline(&desc);
|
|
}
|
|
|
|
void CheckDrawsGreen(const char* vertexShader,
|
|
const char* fragmentShader,
|
|
wgpu::Texture readonlyStorageTexture) {
|
|
wgpu::RenderPipeline pipeline = CreateRenderPipeline(vertexShader, fragmentShader);
|
|
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
|
|
device, pipeline.GetBindGroupLayout(0), {{0, readonlyStorageTexture.CreateView()}});
|
|
|
|
// Clear the output attachment to red at the beginning of the render pass.
|
|
wgpu::Texture outputTexture =
|
|
CreateTexture(kOutputAttachmentFormat,
|
|
wgpu::TextureUsage::OutputAttachment | 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};
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
renderPassEncoder.SetBindGroup(0, bindGroup);
|
|
renderPassEncoder.SetPipeline(pipeline);
|
|
renderPassEncoder.Draw(1);
|
|
renderPassEncoder.EndPass();
|
|
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
|
|
// Check if the contents in the output texture are all as expected (green).
|
|
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, outputTexture, 0, 0);
|
|
}
|
|
|
|
void CheckResultInStorageBuffer(wgpu::Texture readonlyStorageTexture,
|
|
const std::string& computeShader) {
|
|
wgpu::ComputePipeline pipeline = CreateComputePipeline(computeShader.c_str());
|
|
|
|
// Clear the content of the result buffer into 0.
|
|
constexpr uint32_t kInitialValue = 0;
|
|
wgpu::Buffer resultBuffer =
|
|
utils::CreateBufferFromData(device, &kInitialValue, sizeof(kInitialValue),
|
|
wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc);
|
|
wgpu::BindGroup bindGroup =
|
|
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
|
|
{{0, readonlyStorageTexture.CreateView()}, {1, resultBuffer}});
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder computeEncoder = encoder.BeginComputePass();
|
|
computeEncoder.SetBindGroup(0, bindGroup);
|
|
computeEncoder.SetPipeline(pipeline);
|
|
computeEncoder.Dispatch(1);
|
|
computeEncoder.EndPass();
|
|
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
|
|
// Check if the contents in the result buffer are what we expect.
|
|
constexpr uint32_t kExpectedValue = 1u;
|
|
EXPECT_BUFFER_U32_RANGE_EQ(&kExpectedValue, resultBuffer, 0, 1u);
|
|
}
|
|
|
|
void WriteIntoStorageTextureInRenderPass(wgpu::Texture writeonlyStorageTexture,
|
|
const char* kVertexShader,
|
|
const char* kFragmentShader) {
|
|
// Create a render pipeline that writes the expected pixel values into the storage texture
|
|
// without fragment shader outputs.
|
|
wgpu::RenderPipeline pipeline = CreateRenderPipeline(kVertexShader, kFragmentShader);
|
|
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
|
|
device, pipeline.GetBindGroupLayout(0), {{0, writeonlyStorageTexture.CreateView()}});
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
// TODO(jiawei.shao@intel.com): remove the output attachment when Dawn supports beginning a
|
|
// render pass with no attachments.
|
|
wgpu::Texture dummyOutputTexture =
|
|
CreateTexture(kOutputAttachmentFormat,
|
|
wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, 1, 1);
|
|
utils::ComboRenderPassDescriptor renderPassDescriptor({dummyOutputTexture.CreateView()});
|
|
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
renderPassEncoder.SetBindGroup(0, bindGroup);
|
|
renderPassEncoder.SetPipeline(pipeline);
|
|
renderPassEncoder.Draw(1);
|
|
renderPassEncoder.EndPass();
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
}
|
|
|
|
void WriteIntoStorageTextureInComputePass(wgpu::Texture writeonlyStorageTexture,
|
|
const char* computeShader) {
|
|
// Create a compute pipeline that writes the expected pixel values into the storage texture.
|
|
wgpu::ComputePipeline pipeline = CreateComputePipeline(computeShader);
|
|
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
|
|
device, pipeline.GetBindGroupLayout(0), {{0, writeonlyStorageTexture.CreateView()}});
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass();
|
|
computePassEncoder.SetBindGroup(0, bindGroup);
|
|
computePassEncoder.SetPipeline(pipeline);
|
|
computePassEncoder.Dispatch(1);
|
|
computePassEncoder.EndPass();
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
}
|
|
|
|
void ReadWriteIntoStorageTextureInComputePass(wgpu::Texture readonlyStorageTexture,
|
|
wgpu::Texture writeonlyStorageTexture,
|
|
const char* computeShader) {
|
|
// Create a compute pipeline that writes the expected pixel values into the storage texture.
|
|
wgpu::ComputePipeline pipeline = CreateComputePipeline(computeShader);
|
|
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
|
|
device, pipeline.GetBindGroupLayout(0),
|
|
{{0, writeonlyStorageTexture.CreateView()}, {1, readonlyStorageTexture.CreateView()}});
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass();
|
|
computePassEncoder.SetBindGroup(0, bindGroup);
|
|
computePassEncoder.SetPipeline(pipeline);
|
|
computePassEncoder.Dispatch(1);
|
|
computePassEncoder.EndPass();
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
}
|
|
|
|
void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture,
|
|
wgpu::TextureFormat format,
|
|
uint32_t arrayLayerCount = 1) {
|
|
const uint32_t texelSize = utils::GetTexelBlockSizeInBytes(format);
|
|
const std::vector<uint8_t>& expectedData = GetExpectedData(format, arrayLayerCount);
|
|
CheckOutputStorageTexture(writeonlyStorageTexture, texelSize, expectedData);
|
|
}
|
|
|
|
void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture,
|
|
uint32_t texelSize,
|
|
const std::vector<uint8_t>& expectedData) {
|
|
// Copy the content from the write-only storage texture to the result buffer.
|
|
const uint32_t arrayLayerCount =
|
|
static_cast<uint32_t>(expectedData.size() / texelSize / (kWidth * kHeight));
|
|
wgpu::Buffer resultBuffer = CreateEmptyBufferForTextureCopy(texelSize, arrayLayerCount);
|
|
|
|
const wgpu::Extent3D copyExtent = {kWidth, kHeight, 1};
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
// TODO(jiawei.shao@intel.com): copy multiple array layers in one CopyTextureToBuffer() when
|
|
// it is supported.
|
|
for (uint32_t layer = 0; layer < arrayLayerCount; ++layer) {
|
|
wgpu::TextureCopyView textureCopyView;
|
|
textureCopyView.texture = writeonlyStorageTexture;
|
|
textureCopyView.arrayLayer = layer;
|
|
|
|
const uint64_t bufferOffset = kTextureBytesPerRowAlignment * kHeight * layer;
|
|
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(
|
|
resultBuffer, bufferOffset, kTextureBytesPerRowAlignment, 0);
|
|
|
|
encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Extent);
|
|
}
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
|
|
// Check if the contents in the result buffer are what we expect.
|
|
for (size_t layer = 0; layer < arrayLayerCount; ++layer) {
|
|
for (size_t y = 0; y < kHeight; ++y) {
|
|
const size_t resultBufferOffset =
|
|
kTextureBytesPerRowAlignment * (kHeight * layer + y);
|
|
const size_t expectedDataOffset = texelSize * kWidth * (kHeight * layer + y);
|
|
EXPECT_BUFFER_U32_RANGE_EQ(
|
|
reinterpret_cast<const uint32_t*>(expectedData.data() + expectedDataOffset),
|
|
resultBuffer, resultBufferOffset, kWidth);
|
|
}
|
|
}
|
|
}
|
|
|
|
static constexpr size_t kWidth = 4u;
|
|
static constexpr size_t kHeight = 4u;
|
|
static constexpr wgpu::TextureFormat kOutputAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
const char* kSimpleVertexShader = R"(
|
|
#version 450
|
|
void main() {
|
|
gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
|
|
gl_PointSize = 1.0f;
|
|
})";
|
|
|
|
const char* kComputeExpectedValueGLSL = "1 + x + size.x * (y + size.y * layer)";
|
|
};
|
|
|
|
// Test that using read-only storage texture and write-only storage texture in BindGroupLayout is
|
|
// valid on all backends. This test is a regression test for chromium:1061156 and passes by not
|
|
// asserting or crashing.
|
|
TEST_P(StorageTextureTests, BindGroupLayoutWithStorageTextureBindingType) {
|
|
// wgpu::BindingType::ReadonlyStorageTexture is a valid binding type to create a bind group
|
|
// layout.
|
|
{
|
|
wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::Compute,
|
|
wgpu::BindingType::ReadonlyStorageTexture};
|
|
entry.storageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
wgpu::BindGroupLayoutDescriptor descriptor;
|
|
descriptor.entryCount = 1;
|
|
descriptor.entries = &entry;
|
|
device.CreateBindGroupLayout(&descriptor);
|
|
}
|
|
|
|
// wgpu::BindingType::WriteonlyStorageTexture is a valid binding type to create a bind group
|
|
// layout.
|
|
{
|
|
wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::Compute,
|
|
wgpu::BindingType::WriteonlyStorageTexture};
|
|
entry.storageTextureFormat = wgpu::TextureFormat::R32Float;
|
|
wgpu::BindGroupLayoutDescriptor descriptor;
|
|
descriptor.entryCount = 1;
|
|
descriptor.entries = &entry;
|
|
device.CreateBindGroupLayout(&descriptor);
|
|
}
|
|
}
|
|
|
|
// Test that read-only storage textures are supported in compute shader.
|
|
TEST_P(StorageTextureTests, ReadonlyStorageTextureInComputeShader) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
|
|
|
|
for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(format)) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare the read-only storage texture and fill it with the expected data.
|
|
const std::vector<uint8_t> kInitialTextureData = GetExpectedData(format);
|
|
wgpu::Texture readonlyStorageTexture =
|
|
CreateTextureWithTestData(kInitialTextureData, format);
|
|
|
|
// Create a compute shader that reads the pixels from the read-only storage texture and
|
|
// writes 1 to DstBuffer if they all have to expected value.
|
|
std::ostringstream csStream;
|
|
csStream << R"(
|
|
#version 450
|
|
layout(set = 0, binding = 1, std430) buffer DstBuffer {
|
|
uint result;
|
|
} dstBuffer;
|
|
)" << CommonReadOnlyTestCode(format)
|
|
<< R"(
|
|
void main() {
|
|
if (doTest()) {
|
|
dstBuffer.result = 1;
|
|
} else {
|
|
dstBuffer.result = 0;
|
|
}
|
|
})";
|
|
|
|
CheckResultInStorageBuffer(readonlyStorageTexture, csStream.str());
|
|
}
|
|
}
|
|
|
|
// Test that read-only storage textures are supported in vertex shader.
|
|
TEST_P(StorageTextureTests, ReadonlyStorageTextureInVertexShader) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed());
|
|
|
|
for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(format)) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare the read-only storage texture and fill it with the expected data.
|
|
const std::vector<uint8_t> kInitialTextureData = GetExpectedData(format);
|
|
wgpu::Texture readonlyStorageTexture =
|
|
CreateTextureWithTestData(kInitialTextureData, format);
|
|
|
|
// Create a rendering pipeline that reads the pixels from the read-only storage texture and
|
|
// uses green as the output color, otherwise uses red instead.
|
|
std::ostringstream vsStream;
|
|
vsStream << R"(
|
|
#version 450
|
|
layout(location = 0) out vec4 o_color;
|
|
)" << CommonReadOnlyTestCode(format)
|
|
<< R"(
|
|
void main() {
|
|
gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
|
|
if (doTest()) {
|
|
o_color = vec4(0.f, 1.f, 0.f, 1.f);
|
|
} else {
|
|
o_color = vec4(1.f, 0.f, 0.f, 1.f);
|
|
}
|
|
gl_PointSize = 1.0f;
|
|
})";
|
|
const char* kFragmentShader = R"(
|
|
#version 450
|
|
layout(location = 0) in vec4 o_color;
|
|
layout(location = 0) out vec4 fragColor;
|
|
void main() {
|
|
fragColor = o_color;
|
|
})";
|
|
CheckDrawsGreen(vsStream.str().c_str(), kFragmentShader, readonlyStorageTexture);
|
|
}
|
|
}
|
|
|
|
// Test that read-only storage textures are supported in fragment shader.
|
|
TEST_P(StorageTextureTests, ReadonlyStorageTextureInFragmentShader) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed());
|
|
|
|
for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(format)) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare the read-only storage texture and fill it with the expected data.
|
|
const std::vector<uint8_t> kInitialTextureData = GetExpectedData(format);
|
|
wgpu::Texture readonlyStorageTexture =
|
|
CreateTextureWithTestData(kInitialTextureData, format);
|
|
|
|
// Create a rendering pipeline that reads the pixels from the read-only storage texture and
|
|
// uses green as the output color if the pixel value is expected, otherwise uses red
|
|
// instead.
|
|
std::ostringstream fsStream;
|
|
fsStream << R"(
|
|
#version 450
|
|
layout(location = 0) out vec4 o_color;
|
|
)" << CommonReadOnlyTestCode(format)
|
|
<< R"(
|
|
void main() {
|
|
if (doTest()) {
|
|
o_color = vec4(0.f, 1.f, 0.f, 1.f);
|
|
} else {
|
|
o_color = vec4(1.f, 0.f, 0.f, 1.f);
|
|
}
|
|
})";
|
|
CheckDrawsGreen(kSimpleVertexShader, fsStream.str().c_str(), readonlyStorageTexture);
|
|
}
|
|
}
|
|
|
|
// Test that write-only storage textures are supported in compute shader.
|
|
TEST_P(StorageTextureTests, WriteonlyStorageTextureInComputeShader) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
|
|
|
|
for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(format)) {
|
|
continue;
|
|
}
|
|
|
|
// TODO(jiawei.shao@intel.com): investigate why this test fails with RGBA8Snorm on Linux
|
|
// Intel OpenGL driver.
|
|
if (format == wgpu::TextureFormat::RGBA8Snorm && IsIntel() && IsOpenGL() && IsLinux()) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare the write-only storage texture.
|
|
wgpu::Texture writeonlyStorageTexture =
|
|
CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc);
|
|
|
|
// Write the expected pixel values into the write-only storage texture.
|
|
const std::string computeShader = CommonWriteOnlyTestCode(format);
|
|
WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, computeShader.c_str());
|
|
|
|
// Verify the pixel data in the write-only storage texture is expected.
|
|
CheckOutputStorageTexture(writeonlyStorageTexture, format);
|
|
}
|
|
}
|
|
|
|
// Test that reading from one read-only storage texture then writing into another write-only storage
|
|
// texture in one dispatch are supported in compute shader.
|
|
TEST_P(StorageTextureTests, ReadWriteDifferentStorageTextureInOneDispatchInComputeShader) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
|
|
|
|
for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(format)) {
|
|
continue;
|
|
}
|
|
|
|
// TODO(jiawei.shao@intel.com): investigate why this test fails with RGBA8Snorm on Linux
|
|
// Intel OpenGL driver.
|
|
if (format == wgpu::TextureFormat::RGBA8Snorm && IsIntel() && IsOpenGL() && IsLinux()) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare the read-only storage texture.
|
|
const std::vector<uint8_t> kInitialTextureData = GetExpectedData(format);
|
|
wgpu::Texture readonlyStorageTexture =
|
|
CreateTextureWithTestData(kInitialTextureData, format);
|
|
|
|
// Prepare the write-only storage texture.
|
|
wgpu::Texture writeonlyStorageTexture =
|
|
CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc);
|
|
|
|
// Write the expected pixel values into the write-only storage texture.
|
|
const std::string computeShader = CommonReadWriteTestCode(format);
|
|
ReadWriteIntoStorageTextureInComputePass(readonlyStorageTexture, writeonlyStorageTexture,
|
|
computeShader.c_str());
|
|
|
|
// Verify the pixel data in the write-only storage texture is expected.
|
|
CheckOutputStorageTexture(writeonlyStorageTexture, format);
|
|
}
|
|
}
|
|
|
|
// Test that write-only storage textures are supported in fragment shader.
|
|
TEST_P(StorageTextureTests, WriteonlyStorageTextureInFragmentShader) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
|
|
|
|
for (wgpu::TextureFormat format : utils::kAllTextureFormats) {
|
|
if (!utils::TextureFormatSupportsStorageTexture(format)) {
|
|
continue;
|
|
}
|
|
|
|
// TODO(jiawei.shao@intel.com): investigate why this test fails with RGBA8Snorm on Linux
|
|
// Intel OpenGL driver.
|
|
if (format == wgpu::TextureFormat::RGBA8Snorm && IsIntel() && IsOpenGL() && IsLinux()) {
|
|
continue;
|
|
}
|
|
|
|
// Prepare the write-only storage texture.
|
|
wgpu::Texture writeonlyStorageTexture =
|
|
CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc);
|
|
|
|
// Write the expected pixel values into the write-only storage texture.
|
|
const std::string fragmentShader = CommonWriteOnlyTestCode(format);
|
|
WriteIntoStorageTextureInRenderPass(writeonlyStorageTexture, kSimpleVertexShader,
|
|
fragmentShader.c_str());
|
|
|
|
// Verify the pixel data in the write-only storage texture is expected.
|
|
CheckOutputStorageTexture(writeonlyStorageTexture, format);
|
|
}
|
|
}
|
|
|
|
// Verify 2D array read-only storage texture works correctly.
|
|
TEST_P(StorageTextureTests, Readonly2DArrayStorageTexture) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed());
|
|
|
|
constexpr uint32_t kArrayLayerCount = 3u;
|
|
|
|
constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint;
|
|
|
|
const std::vector<uint8_t> initialTextureData =
|
|
GetExpectedData(kTextureFormat, kArrayLayerCount);
|
|
wgpu::Texture readonlyStorageTexture =
|
|
CreateTextureWithTestData(initialTextureData, kTextureFormat);
|
|
|
|
// Create a compute shader that reads the pixels from the read-only storage texture and writes 1
|
|
// to DstBuffer if they all have to expected value.
|
|
std::ostringstream csStream;
|
|
csStream << R"(
|
|
#version 450
|
|
layout (set = 0, binding = 1, std430) buffer DstBuffer {
|
|
uint result;
|
|
} dstBuffer;
|
|
)" << CommonReadOnlyTestCode(kTextureFormat, true)
|
|
<< R"(
|
|
void main() {
|
|
if (doTest()) {
|
|
dstBuffer.result = 1;
|
|
} else {
|
|
dstBuffer.result = 0;
|
|
}
|
|
})";
|
|
|
|
CheckResultInStorageBuffer(readonlyStorageTexture, csStream.str());
|
|
}
|
|
|
|
// Verify 2D array write-only storage texture works correctly.
|
|
TEST_P(StorageTextureTests, Writeonly2DArrayStorageTexture) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
|
|
|
|
constexpr uint32_t kArrayLayerCount = 3u;
|
|
|
|
constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint;
|
|
|
|
// Prepare the write-only storage texture.
|
|
wgpu::Texture writeonlyStorageTexture =
|
|
CreateTexture(kTextureFormat, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc,
|
|
kWidth, kHeight, kArrayLayerCount);
|
|
|
|
// Write the expected pixel values into the write-only storage texture.
|
|
const std::string computeShader = CommonWriteOnlyTestCode(kTextureFormat, true);
|
|
WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, computeShader.c_str());
|
|
|
|
// Verify the pixel data in the write-only storage texture is expected.
|
|
CheckOutputStorageTexture(writeonlyStorageTexture, kTextureFormat, kArrayLayerCount);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(StorageTextureTests,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
VulkanBackend());
|
|
|
|
class StorageTextureZeroInitTests : public StorageTextureTests {
|
|
public:
|
|
static std::vector<uint8_t> GetExpectedData() {
|
|
constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint;
|
|
|
|
const uint32_t texelSizeInBytes = utils::GetTexelBlockSizeInBytes(kTextureFormat);
|
|
const size_t kDataCount = texelSizeInBytes * kWidth * kHeight;
|
|
std::vector<uint8_t> outputData(kDataCount, 0);
|
|
|
|
uint32_t* outputDataPtr = reinterpret_cast<uint32_t*>(&outputData[0]);
|
|
*outputDataPtr = 1u;
|
|
|
|
return outputData;
|
|
}
|
|
|
|
const char* kCommonReadOnlyZeroInitTestCode = R"(
|
|
bool doTest() {
|
|
for (uint y = 0; y < 4; ++y) {
|
|
for (uint x = 0; x < 4; ++x) {
|
|
uvec4 pixel = imageLoad(srcImage, ivec2(x, y));
|
|
if (pixel != uvec4(0, 0, 0, 1u)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
})";
|
|
|
|
const char* kCommonWriteOnlyZeroInitTestCode = R"(
|
|
#version 450
|
|
layout(set = 0, binding = 0, r32ui) uniform writeonly uimage2D dstImage;
|
|
void main() {
|
|
imageStore(dstImage, ivec2(0, 0), uvec4(1u, 0, 0, 1u));
|
|
})";
|
|
};
|
|
|
|
// Verify that the texture is correctly cleared to 0 before its first usage as a read-only storage
|
|
// texture in a render pass.
|
|
TEST_P(StorageTextureZeroInitTests, ReadonlyStorageTextureClearsToZeroInRenderPass) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed());
|
|
|
|
wgpu::Texture readonlyStorageTexture =
|
|
CreateTexture(wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage);
|
|
|
|
// Create a rendering pipeline that reads the pixels from the read-only storage texture and uses
|
|
// green as the output color, otherwise uses red instead.
|
|
const char* kVertexShader = kSimpleVertexShader;
|
|
const std::string kFragmentShader = std::string(R"(
|
|
#version 450
|
|
layout(set = 0, binding = 0, r32ui) uniform readonly uimage2D srcImage;
|
|
layout(location = 0) out vec4 o_color;)") +
|
|
kCommonReadOnlyZeroInitTestCode +
|
|
R"(
|
|
|
|
void main() {
|
|
if (doTest()) {
|
|
o_color = vec4(0.f, 1.f, 0.f, 1.f);
|
|
} else {
|
|
o_color = vec4(1.f, 0.f, 0.f, 1.f);
|
|
}
|
|
})";
|
|
CheckDrawsGreen(kVertexShader, kFragmentShader.c_str(), readonlyStorageTexture);
|
|
}
|
|
|
|
// Verify that the texture is correctly cleared to 0 before its first usage as a read-only storage
|
|
// texture in a compute pass.
|
|
TEST_P(StorageTextureZeroInitTests, ReadonlyStorageTextureClearsToZeroInComputePass) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed());
|
|
|
|
wgpu::Texture readonlyStorageTexture =
|
|
CreateTexture(wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage);
|
|
|
|
// Create a compute shader that reads the pixels from the read-only storage texture and writes 1
|
|
// to DstBuffer if they all have to expected value.
|
|
const std::string kComputeShader = std::string(R"(
|
|
#version 450
|
|
layout (set = 0, binding = 0, r32ui) uniform readonly uimage2D srcImage;
|
|
layout (set = 0, binding = 1, std430) buffer DstBuffer {
|
|
uint result;
|
|
} dstBuffer;)") + kCommonReadOnlyZeroInitTestCode +
|
|
R"(
|
|
|
|
void main() {
|
|
if (doTest()) {
|
|
dstBuffer.result = 1;
|
|
} else {
|
|
dstBuffer.result = 0;
|
|
}
|
|
})";
|
|
|
|
CheckResultInStorageBuffer(readonlyStorageTexture, kComputeShader);
|
|
}
|
|
|
|
// Verify that the texture is correctly cleared to 0 before its first usage as a write-only storage
|
|
// storage texture in a render pass.
|
|
TEST_P(StorageTextureZeroInitTests, WriteonlyStorageTextureClearsToZeroInRenderPass) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
|
|
|
|
// Prepare the write-only storage texture.
|
|
constexpr uint32_t kTexelSizeR32Uint = 4u;
|
|
wgpu::Texture writeonlyStorageTexture = CreateTexture(
|
|
wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc);
|
|
|
|
WriteIntoStorageTextureInRenderPass(writeonlyStorageTexture, kSimpleVertexShader,
|
|
kCommonWriteOnlyZeroInitTestCode);
|
|
CheckOutputStorageTexture(writeonlyStorageTexture, kTexelSizeR32Uint, 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) {
|
|
// When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a
|
|
// read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture.
|
|
// TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the
|
|
// bug in spvc parser is fixed.
|
|
DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed());
|
|
|
|
// Prepare the write-only storage texture.
|
|
constexpr uint32_t kTexelSizeR32Uint = 4u;
|
|
wgpu::Texture writeonlyStorageTexture = CreateTexture(
|
|
wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc);
|
|
|
|
WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, kCommonWriteOnlyZeroInitTestCode);
|
|
CheckOutputStorageTexture(writeonlyStorageTexture, kTexelSizeR32Uint, GetExpectedData());
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(StorageTextureZeroInitTests,
|
|
D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}),
|
|
OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}),
|
|
MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}),
|
|
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"}));
|