Raise maxStorageTexturesPerShaderStage to 8
The higher tier for this limit is available on all D3D12, all Metal, and most Vulkan devices. Bug: dawn:685 Change-Id: Ic2a39ad7908ea178e7aac48b7bb54b262d7039cf Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/121543 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Shrek Shao <shrekshao@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
4d3af66bbd
commit
148e7fab1c
|
@ -31,11 +31,11 @@ static constexpr uint32_t kMaxInterStageShaderVariables = 16u;
|
|||
static constexpr uint64_t kAssumedMaxBufferSize =
|
||||
0x80000000u; // Use 2 GB when the limit is unavailable
|
||||
|
||||
// Per stage limits
|
||||
// Per stage maximum limits used to optimized Dawn internals.
|
||||
static constexpr uint32_t kMaxSampledTexturesPerShaderStage = 16;
|
||||
static constexpr uint32_t kMaxSamplersPerShaderStage = 16;
|
||||
static constexpr uint32_t kMaxStorageBuffersPerShaderStage = 8;
|
||||
static constexpr uint32_t kMaxStorageTexturesPerShaderStage = 4;
|
||||
static constexpr uint32_t kMaxStorageTexturesPerShaderStage = 8;
|
||||
static constexpr uint32_t kMaxUniformBuffersPerShaderStage = 12;
|
||||
|
||||
// Indirect command sizes
|
||||
|
|
|
@ -112,80 +112,85 @@ MaybeError ValidateBindingCounts(const CombinedLimits& limits, const BindingCoun
|
|||
limits.v1.maxDynamicStorageBuffersPerPipelineLayout);
|
||||
|
||||
for (SingleShaderStage stage : IterateStages(kAllStages)) {
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].sampledTextureCount > kMaxSampledTexturesPerShaderStage,
|
||||
"The number of sampled textures (%u) in the %s stage exceeds the maximum "
|
||||
"per-stage limit (%u).",
|
||||
bindingCounts.perStage[stage].sampledTextureCount, stage,
|
||||
kMaxSampledTexturesPerShaderStage);
|
||||
DAWN_INVALID_IF(bindingCounts.perStage[stage].sampledTextureCount >
|
||||
limits.v1.maxSampledTexturesPerShaderStage,
|
||||
"The number of sampled textures (%u) in the %s stage exceeds the maximum "
|
||||
"per-stage limit (%u).",
|
||||
bindingCounts.perStage[stage].sampledTextureCount, stage,
|
||||
limits.v1.maxSampledTexturesPerShaderStage);
|
||||
|
||||
// The per-stage number of external textures is bound by the maximum sampled textures
|
||||
// per stage.
|
||||
DAWN_INVALID_IF(bindingCounts.perStage[stage].externalTextureCount >
|
||||
kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
||||
"The number of external textures (%u) in the %s stage exceeds the maximum "
|
||||
"per-stage limit (%u).",
|
||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||
kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture);
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].externalTextureCount >
|
||||
limits.v1.maxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
||||
"The number of external textures (%u) in the %s stage exceeds the maximum "
|
||||
"per-stage limit (%u).",
|
||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||
limits.v1.maxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture);
|
||||
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].sampledTextureCount +
|
||||
(bindingCounts.perStage[stage].externalTextureCount *
|
||||
kSampledTexturesPerExternalTexture) >
|
||||
kMaxSampledTexturesPerShaderStage,
|
||||
limits.v1.maxSampledTexturesPerShaderStage,
|
||||
"The combination of sampled textures (%u) and external textures (%u) in the %s "
|
||||
"stage exceeds the maximum per-stage limit (%u).",
|
||||
bindingCounts.perStage[stage].sampledTextureCount,
|
||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||
kMaxSampledTexturesPerShaderStage);
|
||||
limits.v1.maxSampledTexturesPerShaderStage);
|
||||
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].samplerCount > kMaxSamplersPerShaderStage,
|
||||
bindingCounts.perStage[stage].samplerCount > limits.v1.maxSamplersPerShaderStage,
|
||||
"The number of samplers (%u) in the %s stage exceeds the maximum per-stage limit "
|
||||
"(%u).",
|
||||
bindingCounts.perStage[stage].samplerCount, stage, kMaxSamplersPerShaderStage);
|
||||
bindingCounts.perStage[stage].samplerCount, stage, limits.v1.maxSamplersPerShaderStage);
|
||||
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].samplerCount +
|
||||
(bindingCounts.perStage[stage].externalTextureCount *
|
||||
kSamplersPerExternalTexture) >
|
||||
kMaxSamplersPerShaderStage,
|
||||
limits.v1.maxSamplersPerShaderStage,
|
||||
"The combination of samplers (%u) and external textures (%u) in the %s stage "
|
||||
"exceeds the maximum per-stage limit (%u).",
|
||||
bindingCounts.perStage[stage].samplerCount,
|
||||
bindingCounts.perStage[stage].externalTextureCount, stage, kMaxSamplersPerShaderStage);
|
||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||
limits.v1.maxSamplersPerShaderStage);
|
||||
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].storageBufferCount > kMaxStorageBuffersPerShaderStage,
|
||||
bindingCounts.perStage[stage].storageBufferCount >
|
||||
limits.v1.maxStorageBuffersPerShaderStage,
|
||||
"The number of storage buffers (%u) in the %s stage exceeds the maximum per-stage "
|
||||
"limit (%u).",
|
||||
bindingCounts.perStage[stage].storageBufferCount, stage,
|
||||
kMaxStorageBuffersPerShaderStage);
|
||||
limits.v1.maxStorageBuffersPerShaderStage);
|
||||
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].storageTextureCount > kMaxStorageTexturesPerShaderStage,
|
||||
bindingCounts.perStage[stage].storageTextureCount >
|
||||
limits.v1.maxStorageTexturesPerShaderStage,
|
||||
"The number of storage textures (%u) in the %s stage exceeds the maximum per-stage "
|
||||
"limit (%u).",
|
||||
bindingCounts.perStage[stage].storageTextureCount, stage,
|
||||
kMaxStorageTexturesPerShaderStage);
|
||||
limits.v1.maxStorageTexturesPerShaderStage);
|
||||
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].uniformBufferCount > kMaxUniformBuffersPerShaderStage,
|
||||
bindingCounts.perStage[stage].uniformBufferCount >
|
||||
limits.v1.maxUniformBuffersPerShaderStage,
|
||||
"The number of uniform buffers (%u) in the %s stage exceeds the maximum per-stage "
|
||||
"limit (%u).",
|
||||
bindingCounts.perStage[stage].uniformBufferCount, stage,
|
||||
kMaxUniformBuffersPerShaderStage);
|
||||
limits.v1.maxUniformBuffersPerShaderStage);
|
||||
|
||||
DAWN_INVALID_IF(
|
||||
bindingCounts.perStage[stage].uniformBufferCount +
|
||||
(bindingCounts.perStage[stage].externalTextureCount *
|
||||
kUniformsPerExternalTexture) >
|
||||
kMaxUniformBuffersPerShaderStage,
|
||||
limits.v1.maxUniformBuffersPerShaderStage,
|
||||
"The combination of uniform buffers (%u) and external textures (%u) in the %s "
|
||||
"stage exceeds the maximum per-stage limit (%u).",
|
||||
bindingCounts.perStage[stage].uniformBufferCount,
|
||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||
kMaxUniformBuffersPerShaderStage);
|
||||
limits.v1.maxUniformBuffersPerShaderStage);
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
@ -38,6 +38,11 @@
|
|||
#define LIMITS_RESOURCE_BINDINGS(X) \
|
||||
X(Maximum, maxDynamicUniformBuffersPerPipelineLayout, 8, 10) \
|
||||
X(Maximum, maxDynamicStorageBuffersPerPipelineLayout, 4, 8) \
|
||||
X(Maximum, maxSampledTexturesPerShaderStage, 16, 16) \
|
||||
X(Maximum, maxSamplersPerShaderStage, 16, 16) \
|
||||
X(Maximum, maxStorageBuffersPerShaderStage, 8, 8) \
|
||||
X(Maximum, maxStorageTexturesPerShaderStage, 4, 8) \
|
||||
X(Maximum, maxUniformBuffersPerShaderStage, 12, 12)
|
||||
|
||||
// TODO(crbug.com/dawn/685):
|
||||
// These limits don't have tiers yet. Define two tiers with the same values since the macros
|
||||
|
@ -49,11 +54,6 @@
|
|||
X(Maximum, maxTextureArrayLayers, 256, 256) \
|
||||
X(Maximum, maxBindGroups, 4, 4) \
|
||||
X(Maximum, maxBindingsPerBindGroup, 640, 640) \
|
||||
X(Maximum, maxSampledTexturesPerShaderStage, 16, 16) \
|
||||
X(Maximum, maxSamplersPerShaderStage, 16, 16) \
|
||||
X(Maximum, maxStorageBuffersPerShaderStage, 8, 8) \
|
||||
X(Maximum, maxStorageTexturesPerShaderStage, 4, 4) \
|
||||
X(Maximum, maxUniformBuffersPerShaderStage, 12, 12) \
|
||||
X(Maximum, maxUniformBufferBindingSize, 65536, 65536) \
|
||||
X(Alignment, minUniformBufferOffsetAlignment, 256, 256) \
|
||||
X(Alignment, minStorageBufferOffsetAlignment, 256, 256) \
|
||||
|
|
|
@ -1427,150 +1427,6 @@ TEST_P(BindGroupTests, ReadonlyStorage) {
|
|||
EXPECT_PIXEL_RGBA8_EQ(utils::RGBA8::kGreen, renderPass.color, 0, 0);
|
||||
}
|
||||
|
||||
// Test that creating a large bind group, with each binding type at the max count, works and can be
|
||||
// used correctly. The test loads a different value from each binding, and writes 1 to a storage
|
||||
// buffer if all values are correct.
|
||||
TEST_P(BindGroupTests, ReallyLargeBindGroup) {
|
||||
DAWN_SUPPRESS_TEST_IF(IsOpenGLES());
|
||||
std::ostringstream interface;
|
||||
std::ostringstream body;
|
||||
uint32_t binding = 0;
|
||||
uint32_t expectedValue = 42;
|
||||
|
||||
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||
|
||||
auto CreateTextureWithRedData = [&](wgpu::TextureFormat format, uint32_t value,
|
||||
wgpu::TextureUsage usage) {
|
||||
wgpu::TextureDescriptor textureDesc = {};
|
||||
textureDesc.usage = wgpu::TextureUsage::CopyDst | usage;
|
||||
textureDesc.size = {1, 1, 1};
|
||||
textureDesc.format = format;
|
||||
wgpu::Texture texture = device.CreateTexture(&textureDesc);
|
||||
|
||||
if (format == wgpu::TextureFormat::R8Unorm) {
|
||||
ASSERT(expectedValue < 255u);
|
||||
}
|
||||
wgpu::Buffer textureData =
|
||||
utils::CreateBufferFromData(device, wgpu::BufferUsage::CopySrc, {value});
|
||||
|
||||
wgpu::ImageCopyBuffer imageCopyBuffer = {};
|
||||
imageCopyBuffer.buffer = textureData;
|
||||
imageCopyBuffer.layout.bytesPerRow = 256;
|
||||
|
||||
wgpu::ImageCopyTexture imageCopyTexture = {};
|
||||
imageCopyTexture.texture = texture;
|
||||
|
||||
wgpu::Extent3D copySize = {1, 1, 1};
|
||||
|
||||
commandEncoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
|
||||
return texture;
|
||||
};
|
||||
|
||||
std::vector<wgpu::BindGroupEntry> bgEntries;
|
||||
static_assert(kMaxSampledTexturesPerShaderStage == kMaxSamplersPerShaderStage,
|
||||
"Please update this test");
|
||||
for (uint32_t i = 0; i < kMaxSampledTexturesPerShaderStage; ++i) {
|
||||
wgpu::Texture texture = CreateTextureWithRedData(
|
||||
wgpu::TextureFormat::R8Unorm, expectedValue, wgpu::TextureUsage::TextureBinding);
|
||||
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
|
||||
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var tex" << i << " : texture_2d<f32>;\n";
|
||||
|
||||
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, device.CreateSampler(), nullptr});
|
||||
|
||||
interface << "@group(0) @binding(" << binding++ << ")"
|
||||
<< "var samp" << i << " : sampler;\n";
|
||||
|
||||
body << "if (abs(textureSampleLevel(tex" << i << ", samp" << i
|
||||
<< ", vec2f(0.5, 0.5), 0.0).r - " << expectedValue++ << ".0 / 255.0) > 0.0001) {\n";
|
||||
body << " return;\n";
|
||||
body << "}\n";
|
||||
}
|
||||
for (uint32_t i = 0; i < kMaxStorageTexturesPerShaderStage; ++i) {
|
||||
wgpu::Texture texture = CreateTextureWithRedData(
|
||||
wgpu::TextureFormat::R32Uint, expectedValue, wgpu::TextureUsage::StorageBinding);
|
||||
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
|
||||
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var image" << i << " : texture_storage_2d<r32uint, write>;\n";
|
||||
|
||||
body << "_ = image" << i << ";";
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < kMaxUniformBuffersPerShaderStage; ++i) {
|
||||
wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
|
||||
device, wgpu::BufferUsage::Uniform, {expectedValue, 0, 0, 0});
|
||||
bgEntries.push_back({nullptr, binding, buffer, 0, 4 * sizeof(uint32_t), nullptr, nullptr});
|
||||
|
||||
interface << "struct UniformBuffer" << i << R"({
|
||||
value : u32
|
||||
}
|
||||
)";
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var<uniform> ubuf" << i << " : UniformBuffer" << i << ";\n";
|
||||
|
||||
body << "if (ubuf" << i << ".value != " << expectedValue++ << "u) {\n";
|
||||
body << " return;\n";
|
||||
body << "}\n";
|
||||
}
|
||||
// Save one storage buffer for writing the result
|
||||
for (uint32_t i = 0; i < kMaxStorageBuffersPerShaderStage - 1; ++i) {
|
||||
wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
|
||||
device, wgpu::BufferUsage::Storage, {expectedValue});
|
||||
bgEntries.push_back({nullptr, binding, buffer, 0, sizeof(uint32_t), nullptr, nullptr});
|
||||
|
||||
interface << "struct ReadOnlyStorageBuffer" << i << R"({
|
||||
value : u32
|
||||
}
|
||||
)";
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var<storage, read> sbuf" << i << " : ReadOnlyStorageBuffer" << i << ";\n";
|
||||
|
||||
body << "if (sbuf" << i << ".value != " << expectedValue++ << "u) {\n";
|
||||
body << " return;\n";
|
||||
body << "}\n";
|
||||
}
|
||||
|
||||
wgpu::Buffer result = utils::CreateBufferFromData<uint32_t>(
|
||||
device, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc, {0});
|
||||
bgEntries.push_back({nullptr, binding, result, 0, sizeof(uint32_t), nullptr, nullptr});
|
||||
|
||||
interface << R"(struct ReadWriteStorageBuffer{
|
||||
value : u32
|
||||
}
|
||||
)";
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var<storage, read_write> result : ReadWriteStorageBuffer;\n";
|
||||
|
||||
body << "result.value = 1u;\n";
|
||||
|
||||
std::string shader =
|
||||
interface.str() + "@compute @workgroup_size(1) fn main() {\n" + body.str() + "}\n";
|
||||
wgpu::ComputePipelineDescriptor cpDesc;
|
||||
cpDesc.compute.module = utils::CreateShaderModule(device, shader.c_str());
|
||||
cpDesc.compute.entryPoint = "main";
|
||||
wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc);
|
||||
|
||||
wgpu::BindGroupDescriptor bgDesc = {};
|
||||
bgDesc.layout = cp.GetBindGroupLayout(0);
|
||||
bgDesc.entryCount = static_cast<uint32_t>(bgEntries.size());
|
||||
bgDesc.entries = bgEntries.data();
|
||||
|
||||
wgpu::BindGroup bg = device.CreateBindGroup(&bgDesc);
|
||||
|
||||
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
|
||||
pass.SetPipeline(cp);
|
||||
pass.SetBindGroup(0, bg);
|
||||
pass.DispatchWorkgroups(1, 1, 1);
|
||||
pass.End();
|
||||
|
||||
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
EXPECT_BUFFER_U32_EQ(1, result, 0);
|
||||
}
|
||||
|
||||
// This is a regression test for crbug.com/dawn/319 where creating a bind group with a
|
||||
// destroyed resource would crash the backend.
|
||||
TEST_P(BindGroupTests, CreateWithDestroyedResource) {
|
||||
|
|
|
@ -392,6 +392,152 @@ TEST_P(MaxLimitTests, MaxDynamicBuffers) {
|
|||
EXPECT_TEXTURE_EQ(&expected, renderTarget, {0, 0}, {1, 1});
|
||||
}
|
||||
|
||||
// Test that creating a large bind group, with each binding type at the max count, works and can be
|
||||
// used correctly. The test loads a different value from each binding, and writes 1 to a storage
|
||||
// buffer if all values are correct.
|
||||
TEST_P(MaxLimitTests, ReallyLargeBindGroup) {
|
||||
DAWN_SUPPRESS_TEST_IF(IsOpenGLES());
|
||||
wgpu::Limits limits = GetSupportedLimits().limits;
|
||||
|
||||
std::ostringstream interface;
|
||||
std::ostringstream body;
|
||||
uint32_t binding = 0;
|
||||
uint32_t expectedValue = 42;
|
||||
|
||||
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
|
||||
|
||||
auto CreateTextureWithRedData = [&](wgpu::TextureFormat format, uint32_t value,
|
||||
wgpu::TextureUsage usage) {
|
||||
wgpu::TextureDescriptor textureDesc = {};
|
||||
textureDesc.usage = wgpu::TextureUsage::CopyDst | usage;
|
||||
textureDesc.size = {1, 1, 1};
|
||||
textureDesc.format = format;
|
||||
wgpu::Texture texture = device.CreateTexture(&textureDesc);
|
||||
|
||||
if (format == wgpu::TextureFormat::R8Unorm) {
|
||||
ASSERT(expectedValue < 255u);
|
||||
}
|
||||
wgpu::Buffer textureData =
|
||||
utils::CreateBufferFromData(device, wgpu::BufferUsage::CopySrc, {value});
|
||||
|
||||
wgpu::ImageCopyBuffer imageCopyBuffer = {};
|
||||
imageCopyBuffer.buffer = textureData;
|
||||
imageCopyBuffer.layout.bytesPerRow = 256;
|
||||
|
||||
wgpu::ImageCopyTexture imageCopyTexture = {};
|
||||
imageCopyTexture.texture = texture;
|
||||
|
||||
wgpu::Extent3D copySize = {1, 1, 1};
|
||||
|
||||
commandEncoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
|
||||
return texture;
|
||||
};
|
||||
|
||||
std::vector<wgpu::BindGroupEntry> bgEntries;
|
||||
for (uint32_t i = 0;
|
||||
i < std::min(limits.maxSampledTexturesPerShaderStage, limits.maxSamplersPerShaderStage);
|
||||
++i) {
|
||||
wgpu::Texture texture = CreateTextureWithRedData(
|
||||
wgpu::TextureFormat::R8Unorm, expectedValue, wgpu::TextureUsage::TextureBinding);
|
||||
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
|
||||
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var tex" << i << " : texture_2d<f32>;\n";
|
||||
|
||||
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, device.CreateSampler(), nullptr});
|
||||
|
||||
interface << "@group(0) @binding(" << binding++ << ")"
|
||||
<< "var samp" << i << " : sampler;\n";
|
||||
|
||||
body << "if (abs(textureSampleLevel(tex" << i << ", samp" << i
|
||||
<< ", vec2f(0.5, 0.5), 0.0).r - " << expectedValue++ << ".0 / 255.0) > 0.0001) {\n";
|
||||
body << " return;\n";
|
||||
body << "}\n";
|
||||
}
|
||||
for (uint32_t i = 0; i < limits.maxStorageTexturesPerShaderStage; ++i) {
|
||||
wgpu::Texture texture = CreateTextureWithRedData(
|
||||
wgpu::TextureFormat::R32Uint, expectedValue, wgpu::TextureUsage::StorageBinding);
|
||||
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
|
||||
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var image" << i << " : texture_storage_2d<r32uint, write>;\n";
|
||||
|
||||
body << "_ = image" << i << ";";
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < limits.maxUniformBuffersPerShaderStage; ++i) {
|
||||
wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
|
||||
device, wgpu::BufferUsage::Uniform, {expectedValue, 0, 0, 0});
|
||||
bgEntries.push_back({nullptr, binding, buffer, 0, 4 * sizeof(uint32_t), nullptr, nullptr});
|
||||
|
||||
interface << "struct UniformBuffer" << i << R"({
|
||||
value : u32
|
||||
}
|
||||
)";
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var<uniform> ubuf" << i << " : UniformBuffer" << i << ";\n";
|
||||
|
||||
body << "if (ubuf" << i << ".value != " << expectedValue++ << "u) {\n";
|
||||
body << " return;\n";
|
||||
body << "}\n";
|
||||
}
|
||||
// Save one storage buffer for writing the result
|
||||
for (uint32_t i = 0; i < limits.maxStorageBuffersPerShaderStage - 1; ++i) {
|
||||
wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
|
||||
device, wgpu::BufferUsage::Storage, {expectedValue});
|
||||
bgEntries.push_back({nullptr, binding, buffer, 0, sizeof(uint32_t), nullptr, nullptr});
|
||||
|
||||
interface << "struct ReadOnlyStorageBuffer" << i << R"({
|
||||
value : u32
|
||||
}
|
||||
)";
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var<storage, read> sbuf" << i << " : ReadOnlyStorageBuffer" << i << ";\n";
|
||||
|
||||
body << "if (sbuf" << i << ".value != " << expectedValue++ << "u) {\n";
|
||||
body << " return;\n";
|
||||
body << "}\n";
|
||||
}
|
||||
|
||||
wgpu::Buffer result = utils::CreateBufferFromData<uint32_t>(
|
||||
device, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc, {0});
|
||||
bgEntries.push_back({nullptr, binding, result, 0, sizeof(uint32_t), nullptr, nullptr});
|
||||
|
||||
interface << R"(struct ReadWriteStorageBuffer{
|
||||
value : u32
|
||||
}
|
||||
)";
|
||||
interface << "@group(0) @binding(" << binding++ << ") "
|
||||
<< "var<storage, read_write> result : ReadWriteStorageBuffer;\n";
|
||||
|
||||
body << "result.value = 1u;\n";
|
||||
|
||||
std::string shader =
|
||||
interface.str() + "@compute @workgroup_size(1) fn main() {\n" + body.str() + "}\n";
|
||||
wgpu::ComputePipelineDescriptor cpDesc;
|
||||
cpDesc.compute.module = utils::CreateShaderModule(device, shader.c_str());
|
||||
cpDesc.compute.entryPoint = "main";
|
||||
wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc);
|
||||
|
||||
wgpu::BindGroupDescriptor bgDesc = {};
|
||||
bgDesc.layout = cp.GetBindGroupLayout(0);
|
||||
bgDesc.entryCount = static_cast<uint32_t>(bgEntries.size());
|
||||
bgDesc.entries = bgEntries.data();
|
||||
|
||||
wgpu::BindGroup bg = device.CreateBindGroup(&bgDesc);
|
||||
|
||||
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
|
||||
pass.SetPipeline(cp);
|
||||
pass.SetBindGroup(0, bg);
|
||||
pass.DispatchWorkgroups(1, 1, 1);
|
||||
pass.End();
|
||||
|
||||
wgpu::CommandBuffer commands = commandEncoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
EXPECT_BUFFER_U32_EQ(1, result, 0);
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(MaxLimitTests,
|
||||
D3D12Backend(),
|
||||
MetalBackend(),
|
||||
|
|
|
@ -1293,25 +1293,32 @@ TEST_F(BindGroupLayoutValidationTest, PerStageLimits) {
|
|||
wgpu::BindGroupLayoutEntry otherEntry;
|
||||
};
|
||||
|
||||
wgpu::Limits limits = GetSupportedLimits().limits;
|
||||
|
||||
std::array<TestInfo, 7> kTestInfos = {
|
||||
TestInfo{kMaxSampledTexturesPerShaderStage, BGLEntryType(wgpu::TextureSampleType::Float),
|
||||
TestInfo{limits.maxSampledTexturesPerShaderStage,
|
||||
BGLEntryType(wgpu::TextureSampleType::Float),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||
TestInfo{kMaxSamplersPerShaderStage, BGLEntryType(wgpu::SamplerBindingType::Filtering),
|
||||
TestInfo{limits.maxSamplersPerShaderStage,
|
||||
BGLEntryType(wgpu::SamplerBindingType::Filtering),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||
TestInfo{kMaxSamplersPerShaderStage, BGLEntryType(wgpu::SamplerBindingType::Comparison),
|
||||
TestInfo{limits.maxSamplersPerShaderStage,
|
||||
BGLEntryType(wgpu::SamplerBindingType::Comparison),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||
TestInfo{kMaxStorageBuffersPerShaderStage, BGLEntryType(wgpu::BufferBindingType::Storage),
|
||||
TestInfo{limits.maxStorageBuffersPerShaderStage,
|
||||
BGLEntryType(wgpu::BufferBindingType::Storage),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||
TestInfo{
|
||||
kMaxStorageTexturesPerShaderStage,
|
||||
limits.maxStorageTexturesPerShaderStage,
|
||||
BGLEntryType(wgpu::StorageTextureAccess::WriteOnly, wgpu::TextureFormat::RGBA8Unorm),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||
TestInfo{kMaxUniformBuffersPerShaderStage, BGLEntryType(wgpu::BufferBindingType::Uniform),
|
||||
TestInfo{limits.maxUniformBuffersPerShaderStage,
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform),
|
||||
BGLEntryType(wgpu::TextureSampleType::Float)},
|
||||
// External textures use multiple bindings (3 sampled textures, 1 sampler, 1 uniform buffer)
|
||||
// that count towards the per stage binding limits. The number of external textures are
|
||||
// currently restricted by the maximum number of sampled textures.
|
||||
TestInfo{kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
||||
TestInfo{limits.maxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
||||
BGLEntryType(&utils::kExternalTextureBindingLayout),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)}};
|
||||
|
||||
|
@ -1388,14 +1395,16 @@ TEST_F(BindGroupLayoutValidationTest, PerStageLimitsWithExternalTexture) {
|
|||
wgpu::BindGroupLayoutEntry otherEntry;
|
||||
};
|
||||
|
||||
wgpu::Limits limits = GetSupportedLimits().limits;
|
||||
|
||||
std::array<TestInfo, 3> kTestInfos = {
|
||||
TestInfo{kMaxSampledTexturesPerShaderStage, kSampledTexturesPerExternalTexture,
|
||||
TestInfo{limits.maxSampledTexturesPerShaderStage, kSampledTexturesPerExternalTexture,
|
||||
BGLEntryType(wgpu::TextureSampleType::Float),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||
TestInfo{kMaxSamplersPerShaderStage, kSamplersPerExternalTexture,
|
||||
TestInfo{limits.maxSamplersPerShaderStage, kSamplersPerExternalTexture,
|
||||
BGLEntryType(wgpu::SamplerBindingType::Filtering),
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||
TestInfo{kMaxUniformBuffersPerShaderStage, kUniformsPerExternalTexture,
|
||||
TestInfo{limits.maxUniformBuffersPerShaderStage, kUniformsPerExternalTexture,
|
||||
BGLEntryType(wgpu::BufferBindingType::Uniform),
|
||||
BGLEntryType(wgpu::TextureSampleType::Float)},
|
||||
};
|
||||
|
@ -1480,8 +1489,10 @@ TEST_F(BindGroupLayoutValidationTest, DynamicBufferNumberLimit) {
|
|||
|
||||
// In this test, we use all the same shader stage. Ensure that this does not exceed the
|
||||
// per-stage limit.
|
||||
ASSERT(limits.maxDynamicUniformBuffersPerPipelineLayout <= kMaxUniformBuffersPerShaderStage);
|
||||
ASSERT(limits.maxDynamicStorageBuffersPerPipelineLayout <= kMaxStorageBuffersPerShaderStage);
|
||||
ASSERT(limits.maxDynamicUniformBuffersPerPipelineLayout <=
|
||||
limits.maxUniformBuffersPerShaderStage);
|
||||
ASSERT(limits.maxDynamicStorageBuffersPerPipelineLayout <=
|
||||
limits.maxStorageBuffersPerShaderStage);
|
||||
|
||||
for (uint32_t i = 0; i < limits.maxDynamicUniformBuffersPerPipelineLayout; ++i) {
|
||||
maxUniformDB.push_back(utils::BindingLayoutEntryInitializationHelper(
|
||||
|
|
Loading…
Reference in New Issue