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 =
|
static constexpr uint64_t kAssumedMaxBufferSize =
|
||||||
0x80000000u; // Use 2 GB when the limit is unavailable
|
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 kMaxSampledTexturesPerShaderStage = 16;
|
||||||
static constexpr uint32_t kMaxSamplersPerShaderStage = 16;
|
static constexpr uint32_t kMaxSamplersPerShaderStage = 16;
|
||||||
static constexpr uint32_t kMaxStorageBuffersPerShaderStage = 8;
|
static constexpr uint32_t kMaxStorageBuffersPerShaderStage = 8;
|
||||||
static constexpr uint32_t kMaxStorageTexturesPerShaderStage = 4;
|
static constexpr uint32_t kMaxStorageTexturesPerShaderStage = 8;
|
||||||
static constexpr uint32_t kMaxUniformBuffersPerShaderStage = 12;
|
static constexpr uint32_t kMaxUniformBuffersPerShaderStage = 12;
|
||||||
|
|
||||||
// Indirect command sizes
|
// Indirect command sizes
|
||||||
|
|
|
@ -112,80 +112,85 @@ MaybeError ValidateBindingCounts(const CombinedLimits& limits, const BindingCoun
|
||||||
limits.v1.maxDynamicStorageBuffersPerPipelineLayout);
|
limits.v1.maxDynamicStorageBuffersPerPipelineLayout);
|
||||||
|
|
||||||
for (SingleShaderStage stage : IterateStages(kAllStages)) {
|
for (SingleShaderStage stage : IterateStages(kAllStages)) {
|
||||||
DAWN_INVALID_IF(
|
DAWN_INVALID_IF(bindingCounts.perStage[stage].sampledTextureCount >
|
||||||
bindingCounts.perStage[stage].sampledTextureCount > kMaxSampledTexturesPerShaderStage,
|
limits.v1.maxSampledTexturesPerShaderStage,
|
||||||
"The number of sampled textures (%u) in the %s stage exceeds the maximum "
|
"The number of sampled textures (%u) in the %s stage exceeds the maximum "
|
||||||
"per-stage limit (%u).",
|
"per-stage limit (%u).",
|
||||||
bindingCounts.perStage[stage].sampledTextureCount, stage,
|
bindingCounts.perStage[stage].sampledTextureCount, stage,
|
||||||
kMaxSampledTexturesPerShaderStage);
|
limits.v1.maxSampledTexturesPerShaderStage);
|
||||||
|
|
||||||
// The per-stage number of external textures is bound by the maximum sampled textures
|
// The per-stage number of external textures is bound by the maximum sampled textures
|
||||||
// per stage.
|
// per stage.
|
||||||
DAWN_INVALID_IF(bindingCounts.perStage[stage].externalTextureCount >
|
DAWN_INVALID_IF(
|
||||||
kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
bindingCounts.perStage[stage].externalTextureCount >
|
||||||
"The number of external textures (%u) in the %s stage exceeds the maximum "
|
limits.v1.maxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
||||||
"per-stage limit (%u).",
|
"The number of external textures (%u) in the %s stage exceeds the maximum "
|
||||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
"per-stage limit (%u).",
|
||||||
kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture);
|
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||||
|
limits.v1.maxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture);
|
||||||
|
|
||||||
DAWN_INVALID_IF(
|
DAWN_INVALID_IF(
|
||||||
bindingCounts.perStage[stage].sampledTextureCount +
|
bindingCounts.perStage[stage].sampledTextureCount +
|
||||||
(bindingCounts.perStage[stage].externalTextureCount *
|
(bindingCounts.perStage[stage].externalTextureCount *
|
||||||
kSampledTexturesPerExternalTexture) >
|
kSampledTexturesPerExternalTexture) >
|
||||||
kMaxSampledTexturesPerShaderStage,
|
limits.v1.maxSampledTexturesPerShaderStage,
|
||||||
"The combination of sampled textures (%u) and external textures (%u) in the %s "
|
"The combination of sampled textures (%u) and external textures (%u) in the %s "
|
||||||
"stage exceeds the maximum per-stage limit (%u).",
|
"stage exceeds the maximum per-stage limit (%u).",
|
||||||
bindingCounts.perStage[stage].sampledTextureCount,
|
bindingCounts.perStage[stage].sampledTextureCount,
|
||||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||||
kMaxSampledTexturesPerShaderStage);
|
limits.v1.maxSampledTexturesPerShaderStage);
|
||||||
|
|
||||||
DAWN_INVALID_IF(
|
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 "
|
"The number of samplers (%u) in the %s stage exceeds the maximum per-stage limit "
|
||||||
"(%u).",
|
"(%u).",
|
||||||
bindingCounts.perStage[stage].samplerCount, stage, kMaxSamplersPerShaderStage);
|
bindingCounts.perStage[stage].samplerCount, stage, limits.v1.maxSamplersPerShaderStage);
|
||||||
|
|
||||||
DAWN_INVALID_IF(
|
DAWN_INVALID_IF(
|
||||||
bindingCounts.perStage[stage].samplerCount +
|
bindingCounts.perStage[stage].samplerCount +
|
||||||
(bindingCounts.perStage[stage].externalTextureCount *
|
(bindingCounts.perStage[stage].externalTextureCount *
|
||||||
kSamplersPerExternalTexture) >
|
kSamplersPerExternalTexture) >
|
||||||
kMaxSamplersPerShaderStage,
|
limits.v1.maxSamplersPerShaderStage,
|
||||||
"The combination of samplers (%u) and external textures (%u) in the %s stage "
|
"The combination of samplers (%u) and external textures (%u) in the %s stage "
|
||||||
"exceeds the maximum per-stage limit (%u).",
|
"exceeds the maximum per-stage limit (%u).",
|
||||||
bindingCounts.perStage[stage].samplerCount,
|
bindingCounts.perStage[stage].samplerCount,
|
||||||
bindingCounts.perStage[stage].externalTextureCount, stage, kMaxSamplersPerShaderStage);
|
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||||
|
limits.v1.maxSamplersPerShaderStage);
|
||||||
|
|
||||||
DAWN_INVALID_IF(
|
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 "
|
"The number of storage buffers (%u) in the %s stage exceeds the maximum per-stage "
|
||||||
"limit (%u).",
|
"limit (%u).",
|
||||||
bindingCounts.perStage[stage].storageBufferCount, stage,
|
bindingCounts.perStage[stage].storageBufferCount, stage,
|
||||||
kMaxStorageBuffersPerShaderStage);
|
limits.v1.maxStorageBuffersPerShaderStage);
|
||||||
|
|
||||||
DAWN_INVALID_IF(
|
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 "
|
"The number of storage textures (%u) in the %s stage exceeds the maximum per-stage "
|
||||||
"limit (%u).",
|
"limit (%u).",
|
||||||
bindingCounts.perStage[stage].storageTextureCount, stage,
|
bindingCounts.perStage[stage].storageTextureCount, stage,
|
||||||
kMaxStorageTexturesPerShaderStage);
|
limits.v1.maxStorageTexturesPerShaderStage);
|
||||||
|
|
||||||
DAWN_INVALID_IF(
|
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 "
|
"The number of uniform buffers (%u) in the %s stage exceeds the maximum per-stage "
|
||||||
"limit (%u).",
|
"limit (%u).",
|
||||||
bindingCounts.perStage[stage].uniformBufferCount, stage,
|
bindingCounts.perStage[stage].uniformBufferCount, stage,
|
||||||
kMaxUniformBuffersPerShaderStage);
|
limits.v1.maxUniformBuffersPerShaderStage);
|
||||||
|
|
||||||
DAWN_INVALID_IF(
|
DAWN_INVALID_IF(
|
||||||
bindingCounts.perStage[stage].uniformBufferCount +
|
bindingCounts.perStage[stage].uniformBufferCount +
|
||||||
(bindingCounts.perStage[stage].externalTextureCount *
|
(bindingCounts.perStage[stage].externalTextureCount *
|
||||||
kUniformsPerExternalTexture) >
|
kUniformsPerExternalTexture) >
|
||||||
kMaxUniformBuffersPerShaderStage,
|
limits.v1.maxUniformBuffersPerShaderStage,
|
||||||
"The combination of uniform buffers (%u) and external textures (%u) in the %s "
|
"The combination of uniform buffers (%u) and external textures (%u) in the %s "
|
||||||
"stage exceeds the maximum per-stage limit (%u).",
|
"stage exceeds the maximum per-stage limit (%u).",
|
||||||
bindingCounts.perStage[stage].uniformBufferCount,
|
bindingCounts.perStage[stage].uniformBufferCount,
|
||||||
bindingCounts.perStage[stage].externalTextureCount, stage,
|
bindingCounts.perStage[stage].externalTextureCount, stage,
|
||||||
kMaxUniformBuffersPerShaderStage);
|
limits.v1.maxUniformBuffersPerShaderStage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -38,6 +38,11 @@
|
||||||
#define LIMITS_RESOURCE_BINDINGS(X) \
|
#define LIMITS_RESOURCE_BINDINGS(X) \
|
||||||
X(Maximum, maxDynamicUniformBuffersPerPipelineLayout, 8, 10) \
|
X(Maximum, maxDynamicUniformBuffersPerPipelineLayout, 8, 10) \
|
||||||
X(Maximum, maxDynamicStorageBuffersPerPipelineLayout, 4, 8) \
|
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):
|
// TODO(crbug.com/dawn/685):
|
||||||
// These limits don't have tiers yet. Define two tiers with the same values since the macros
|
// 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, maxTextureArrayLayers, 256, 256) \
|
||||||
X(Maximum, maxBindGroups, 4, 4) \
|
X(Maximum, maxBindGroups, 4, 4) \
|
||||||
X(Maximum, maxBindingsPerBindGroup, 640, 640) \
|
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(Maximum, maxUniformBufferBindingSize, 65536, 65536) \
|
||||||
X(Alignment, minUniformBufferOffsetAlignment, 256, 256) \
|
X(Alignment, minUniformBufferOffsetAlignment, 256, 256) \
|
||||||
X(Alignment, minStorageBufferOffsetAlignment, 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);
|
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
|
// This is a regression test for crbug.com/dawn/319 where creating a bind group with a
|
||||||
// destroyed resource would crash the backend.
|
// destroyed resource would crash the backend.
|
||||||
TEST_P(BindGroupTests, CreateWithDestroyedResource) {
|
TEST_P(BindGroupTests, CreateWithDestroyedResource) {
|
||||||
|
|
|
@ -392,6 +392,152 @@ TEST_P(MaxLimitTests, MaxDynamicBuffers) {
|
||||||
EXPECT_TEXTURE_EQ(&expected, renderTarget, {0, 0}, {1, 1});
|
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,
|
DAWN_INSTANTIATE_TEST(MaxLimitTests,
|
||||||
D3D12Backend(),
|
D3D12Backend(),
|
||||||
MetalBackend(),
|
MetalBackend(),
|
||||||
|
|
|
@ -1293,25 +1293,32 @@ TEST_F(BindGroupLayoutValidationTest, PerStageLimits) {
|
||||||
wgpu::BindGroupLayoutEntry otherEntry;
|
wgpu::BindGroupLayoutEntry otherEntry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wgpu::Limits limits = GetSupportedLimits().limits;
|
||||||
|
|
||||||
std::array<TestInfo, 7> kTestInfos = {
|
std::array<TestInfo, 7> kTestInfos = {
|
||||||
TestInfo{kMaxSampledTexturesPerShaderStage, BGLEntryType(wgpu::TextureSampleType::Float),
|
TestInfo{limits.maxSampledTexturesPerShaderStage,
|
||||||
|
BGLEntryType(wgpu::TextureSampleType::Float),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||||
TestInfo{kMaxSamplersPerShaderStage, BGLEntryType(wgpu::SamplerBindingType::Filtering),
|
TestInfo{limits.maxSamplersPerShaderStage,
|
||||||
|
BGLEntryType(wgpu::SamplerBindingType::Filtering),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||||
TestInfo{kMaxSamplersPerShaderStage, BGLEntryType(wgpu::SamplerBindingType::Comparison),
|
TestInfo{limits.maxSamplersPerShaderStage,
|
||||||
|
BGLEntryType(wgpu::SamplerBindingType::Comparison),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||||
TestInfo{kMaxStorageBuffersPerShaderStage, BGLEntryType(wgpu::BufferBindingType::Storage),
|
TestInfo{limits.maxStorageBuffersPerShaderStage,
|
||||||
|
BGLEntryType(wgpu::BufferBindingType::Storage),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||||
TestInfo{
|
TestInfo{
|
||||||
kMaxStorageTexturesPerShaderStage,
|
limits.maxStorageTexturesPerShaderStage,
|
||||||
BGLEntryType(wgpu::StorageTextureAccess::WriteOnly, wgpu::TextureFormat::RGBA8Unorm),
|
BGLEntryType(wgpu::StorageTextureAccess::WriteOnly, wgpu::TextureFormat::RGBA8Unorm),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||||
TestInfo{kMaxUniformBuffersPerShaderStage, BGLEntryType(wgpu::BufferBindingType::Uniform),
|
TestInfo{limits.maxUniformBuffersPerShaderStage,
|
||||||
|
BGLEntryType(wgpu::BufferBindingType::Uniform),
|
||||||
BGLEntryType(wgpu::TextureSampleType::Float)},
|
BGLEntryType(wgpu::TextureSampleType::Float)},
|
||||||
// External textures use multiple bindings (3 sampled textures, 1 sampler, 1 uniform buffer)
|
// 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
|
// that count towards the per stage binding limits. The number of external textures are
|
||||||
// currently restricted by the maximum number of sampled textures.
|
// currently restricted by the maximum number of sampled textures.
|
||||||
TestInfo{kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
TestInfo{limits.maxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
|
||||||
BGLEntryType(&utils::kExternalTextureBindingLayout),
|
BGLEntryType(&utils::kExternalTextureBindingLayout),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)}};
|
BGLEntryType(wgpu::BufferBindingType::Uniform)}};
|
||||||
|
|
||||||
|
@ -1388,14 +1395,16 @@ TEST_F(BindGroupLayoutValidationTest, PerStageLimitsWithExternalTexture) {
|
||||||
wgpu::BindGroupLayoutEntry otherEntry;
|
wgpu::BindGroupLayoutEntry otherEntry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wgpu::Limits limits = GetSupportedLimits().limits;
|
||||||
|
|
||||||
std::array<TestInfo, 3> kTestInfos = {
|
std::array<TestInfo, 3> kTestInfos = {
|
||||||
TestInfo{kMaxSampledTexturesPerShaderStage, kSampledTexturesPerExternalTexture,
|
TestInfo{limits.maxSampledTexturesPerShaderStage, kSampledTexturesPerExternalTexture,
|
||||||
BGLEntryType(wgpu::TextureSampleType::Float),
|
BGLEntryType(wgpu::TextureSampleType::Float),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||||
TestInfo{kMaxSamplersPerShaderStage, kSamplersPerExternalTexture,
|
TestInfo{limits.maxSamplersPerShaderStage, kSamplersPerExternalTexture,
|
||||||
BGLEntryType(wgpu::SamplerBindingType::Filtering),
|
BGLEntryType(wgpu::SamplerBindingType::Filtering),
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
BGLEntryType(wgpu::BufferBindingType::Uniform)},
|
||||||
TestInfo{kMaxUniformBuffersPerShaderStage, kUniformsPerExternalTexture,
|
TestInfo{limits.maxUniformBuffersPerShaderStage, kUniformsPerExternalTexture,
|
||||||
BGLEntryType(wgpu::BufferBindingType::Uniform),
|
BGLEntryType(wgpu::BufferBindingType::Uniform),
|
||||||
BGLEntryType(wgpu::TextureSampleType::Float)},
|
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
|
// In this test, we use all the same shader stage. Ensure that this does not exceed the
|
||||||
// per-stage limit.
|
// per-stage limit.
|
||||||
ASSERT(limits.maxDynamicUniformBuffersPerPipelineLayout <= kMaxUniformBuffersPerShaderStage);
|
ASSERT(limits.maxDynamicUniformBuffersPerPipelineLayout <=
|
||||||
ASSERT(limits.maxDynamicStorageBuffersPerPipelineLayout <= kMaxStorageBuffersPerShaderStage);
|
limits.maxUniformBuffersPerShaderStage);
|
||||||
|
ASSERT(limits.maxDynamicStorageBuffersPerPipelineLayout <=
|
||||||
|
limits.maxStorageBuffersPerShaderStage);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < limits.maxDynamicUniformBuffersPerPipelineLayout; ++i) {
|
for (uint32_t i = 0; i < limits.maxDynamicUniformBuffersPerPipelineLayout; ++i) {
|
||||||
maxUniformDB.push_back(utils::BindingLayoutEntryInitializationHelper(
|
maxUniformDB.push_back(utils::BindingLayoutEntryInitializationHelper(
|
||||||
|
|
Loading…
Reference in New Issue