Remove several compile-time constants in favor of limits

Bug: dawn:685
Change-Id: Ifac25116c741fdab7b6a8093b4230065beca4773
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/65483
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2021-10-13 18:57:18 +00:00 committed by Dawn LUCI CQ
parent dcc520dc23
commit 91851e23a8
29 changed files with 408 additions and 302 deletions

View File

@ -37,7 +37,8 @@ float RandomFloat(float min, float max) {
constexpr size_t kNumTriangles = 10000; constexpr size_t kNumTriangles = 10000;
struct alignas(kMinUniformBufferOffsetAlignment) ShaderData { // Aligned as minUniformBufferOffsetAlignment
struct alignas(256) ShaderData {
float scale; float scale;
float time; float time;
float offsetX; float offsetX;

View File

@ -27,14 +27,6 @@ static constexpr uint32_t kTextureBytesPerRowAlignment = 256u;
static constexpr uint32_t kMaxInterStageShaderComponents = 60u; static constexpr uint32_t kMaxInterStageShaderComponents = 60u;
static constexpr uint32_t kMaxInterStageShaderVariables = kMaxInterStageShaderComponents / 4; static constexpr uint32_t kMaxInterStageShaderVariables = kMaxInterStageShaderComponents / 4;
// Compute constants
static constexpr uint32_t kMaxComputeWorkgroupStorageSize = 16352u;
static constexpr uint32_t kMaxComputeWorkgroupInvocations = 256u;
static constexpr uint32_t kMaxComputePerDimensionDispatchSize = 65535u;
static constexpr uint32_t kMaxComputeWorkgroupSizeX = 256;
static constexpr uint32_t kMaxComputeWorkgroupSizeY = 256;
static constexpr uint32_t kMaxComputeWorkgroupSizeZ = 64;
// Per stage limits // Per stage limits
static constexpr uint32_t kMaxSampledTexturesPerShaderStage = 16; static constexpr uint32_t kMaxSampledTexturesPerShaderStage = 16;
static constexpr uint32_t kMaxSamplersPerShaderStage = 16; static constexpr uint32_t kMaxSamplersPerShaderStage = 16;
@ -46,12 +38,6 @@ static constexpr uint32_t kMaxUniformBuffersPerShaderStage = 12;
static constexpr uint32_t kMaxDynamicUniformBuffersPerPipelineLayout = 8u; static constexpr uint32_t kMaxDynamicUniformBuffersPerPipelineLayout = 8u;
static constexpr uint32_t kMaxDynamicStorageBuffersPerPipelineLayout = 4u; static constexpr uint32_t kMaxDynamicStorageBuffersPerPipelineLayout = 4u;
// Buffer binding constraints
static constexpr uint64_t kMaxUniformBufferBindingSize = 16384u;
static constexpr uint64_t kMaxStorageBufferBindingSize = 134217728u;
static constexpr uint64_t kMinUniformBufferOffsetAlignment = 256u;
static constexpr uint64_t kMinStorageBufferOffsetAlignment = 256u;
// Indirect command sizes // Indirect command sizes
static constexpr uint64_t kDispatchIndirectSize = 3 * sizeof(uint32_t); static constexpr uint64_t kDispatchIndirectSize = 3 * sizeof(uint32_t);
static constexpr uint64_t kDrawIndirectSize = 4 * sizeof(uint32_t); static constexpr uint64_t kDrawIndirectSize = 4 * sizeof(uint32_t);
@ -61,15 +47,6 @@ static constexpr uint64_t kDrawIndexedIndirectSize = 5 * sizeof(uint32_t);
static constexpr float kLodMin = 0.0; static constexpr float kLodMin = 0.0;
static constexpr float kLodMax = 1000.0; static constexpr float kLodMax = 1000.0;
// Max texture size constants
static constexpr uint32_t kMaxTextureDimension1D = 8192u;
static constexpr uint32_t kMaxTextureDimension2D = 8192u;
static constexpr uint32_t kMaxTextureDimension3D = 2048u;
static constexpr uint32_t kMaxTextureArrayLayers = 256u;
static constexpr uint32_t kMaxTexture2DMipLevels = 14u;
static_assert(1 << (kMaxTexture2DMipLevels - 1) == kMaxTextureDimension2D,
"kMaxTexture2DMipLevels and kMaxTextureDimension2D size mismatch");
// Offset alignment for CopyB2B. Strictly speaking this alignment is required only // Offset alignment for CopyB2B. Strictly speaking this alignment is required only
// on macOS, but we decide to do it on all platforms. // on macOS, but we decide to do it on all platforms.
static constexpr uint64_t kCopyBufferToBufferOffsetAlignment = 4u; static constexpr uint64_t kCopyBufferToBufferOffsetAlignment = 4u;

View File

@ -50,19 +50,22 @@ namespace dawn_native {
switch (bindingInfo.buffer.type) { switch (bindingInfo.buffer.type) {
case wgpu::BufferBindingType::Uniform: case wgpu::BufferBindingType::Uniform:
requiredUsage = wgpu::BufferUsage::Uniform; requiredUsage = wgpu::BufferUsage::Uniform;
maxBindingSize = kMaxUniformBufferBindingSize; maxBindingSize = device->GetLimits().v1.maxUniformBufferBindingSize;
requiredBindingAlignment = kMinUniformBufferOffsetAlignment; requiredBindingAlignment =
device->GetLimits().v1.minUniformBufferOffsetAlignment;
break; break;
case wgpu::BufferBindingType::Storage: case wgpu::BufferBindingType::Storage:
case wgpu::BufferBindingType::ReadOnlyStorage: case wgpu::BufferBindingType::ReadOnlyStorage:
requiredUsage = wgpu::BufferUsage::Storage; requiredUsage = wgpu::BufferUsage::Storage;
maxBindingSize = kMaxStorageBufferBindingSize; maxBindingSize = device->GetLimits().v1.maxStorageBufferBindingSize;
requiredBindingAlignment = kMinStorageBufferOffsetAlignment; requiredBindingAlignment =
device->GetLimits().v1.minStorageBufferOffsetAlignment;
break; break;
case kInternalStorageBufferBinding: case kInternalStorageBufferBinding:
requiredUsage = kInternalStorageBuffer; requiredUsage = kInternalStorageBuffer;
maxBindingSize = kMaxStorageBufferBindingSize; maxBindingSize = device->GetLimits().v1.maxStorageBufferBindingSize;
requiredBindingAlignment = kMinStorageBufferOffsetAlignment; requiredBindingAlignment =
device->GetLimits().v1.minStorageBufferOffsetAlignment;
break; break;
case wgpu::BufferBindingType::Undefined: case wgpu::BufferBindingType::Undefined:
UNREACHABLE(); UNREACHABLE();

View File

@ -28,8 +28,8 @@ namespace dawn_native {
namespace { namespace {
MaybeError ValidatePerDimensionDispatchSizeLimit(uint32_t size) { MaybeError ValidatePerDimensionDispatchSizeLimit(const DeviceBase* device, uint32_t size) {
if (size > kMaxComputePerDimensionDispatchSize) { if (size > device->GetLimits().v1.maxComputeWorkgroupsPerDimension) {
return DAWN_VALIDATION_ERROR("Dispatch size exceeds defined limits"); return DAWN_VALIDATION_ERROR("Dispatch size exceeds defined limits");
} }
@ -85,9 +85,9 @@ namespace dawn_native {
[&](CommandAllocator* allocator) -> MaybeError { [&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) { if (IsValidationEnabled()) {
DAWN_TRY(mCommandBufferState.ValidateCanDispatch()); DAWN_TRY(mCommandBufferState.ValidateCanDispatch());
DAWN_TRY(ValidatePerDimensionDispatchSizeLimit(x)); DAWN_TRY(ValidatePerDimensionDispatchSizeLimit(GetDevice(), x));
DAWN_TRY(ValidatePerDimensionDispatchSizeLimit(y)); DAWN_TRY(ValidatePerDimensionDispatchSizeLimit(GetDevice(), y));
DAWN_TRY(ValidatePerDimensionDispatchSizeLimit(z)); DAWN_TRY(ValidatePerDimensionDispatchSizeLimit(GetDevice(), z));
} }
// Record the synchronization scope for Dispatch, which is just the current // Record the synchronization scope for Dispatch, which is just the current

View File

@ -1560,6 +1560,10 @@ namespace dawn_native {
} }
} }
const CombinedLimits& DeviceBase::GetLimits() const {
return mLimits;
}
AsyncTaskManager* DeviceBase::GetAsyncTaskManager() const { AsyncTaskManager* DeviceBase::GetAsyncTaskManager() const {
return mAsyncTaskManager.get(); return mAsyncTaskManager.get();
} }

View File

@ -336,6 +336,8 @@ namespace dawn_native {
virtual float GetTimestampPeriodInNS() const = 0; virtual float GetTimestampPeriodInNS() const = 0;
const CombinedLimits& GetLimits() const;
AsyncTaskManager* GetAsyncTaskManager() const; AsyncTaskManager* GetAsyncTaskManager() const;
CallbackTaskManager* GetCallbackTaskManager() const; CallbackTaskManager* GetCallbackTaskManager() const;
dawn_platform::WorkerTaskPool* GetWorkerTaskPool() const; dawn_platform::WorkerTaskPool* GetWorkerTaskPool() const;

View File

@ -17,6 +17,7 @@
#include "common/Constants.h" #include "common/Constants.h"
#include "common/RefCounted.h" #include "common/RefCounted.h"
#include "dawn_native/IndirectDrawValidationEncoder.h" #include "dawn_native/IndirectDrawValidationEncoder.h"
#include "dawn_native/Limits.h"
#include "dawn_native/RenderBundle.h" #include "dawn_native/RenderBundle.h"
#include <algorithm> #include <algorithm>
@ -24,15 +25,10 @@
namespace dawn_native { namespace dawn_native {
namespace { uint32_t ComputeMaxIndirectValidationBatchOffsetRange(const CombinedLimits& limits) {
return limits.v1.maxStorageBufferBindingSize - limits.v1.minStorageBufferOffsetAlignment -
// In the unlikely scenario that indirect offsets used over a single buffer span more than
// this length of the buffer, we split the validation work into multiple batches.
constexpr uint64_t kMaxBatchOffsetRange = kMaxStorageBufferBindingSize -
kMinStorageBufferOffsetAlignment -
kDrawIndexedIndirectSize; kDrawIndexedIndirectSize;
}
} // namespace
IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::IndexedIndirectBufferValidationInfo( IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::IndexedIndirectBufferValidationInfo(
BufferBase* indirectBuffer) BufferBase* indirectBuffer)
@ -40,12 +36,14 @@ namespace dawn_native {
} }
void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddIndexedIndirectDraw( void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddIndexedIndirectDraw(
uint32_t maxDrawCallsPerIndirectValidationBatch,
uint32_t maxBatchOffsetRange,
IndexedIndirectDraw draw) { IndexedIndirectDraw draw) {
const uint64_t newOffset = draw.clientBufferOffset; const uint64_t newOffset = draw.clientBufferOffset;
auto it = mBatches.begin(); auto it = mBatches.begin();
while (it != mBatches.end()) { while (it != mBatches.end()) {
IndexedIndirectValidationBatch& batch = *it; IndexedIndirectValidationBatch& batch = *it;
if (batch.draws.size() >= kMaxDrawCallsPerIndirectValidationBatch) { if (batch.draws.size() >= maxDrawCallsPerIndirectValidationBatch) {
// This batch is full. If its minOffset is to the right of the new offset, we can // This batch is full. If its minOffset is to the right of the new offset, we can
// just insert a new batch here. // just insert a new batch here.
if (newOffset < batch.minOffset) { if (newOffset < batch.minOffset) {
@ -62,16 +60,14 @@ namespace dawn_native {
return; return;
} }
if (newOffset < batch.minOffset && if (newOffset < batch.minOffset && batch.maxOffset - newOffset <= maxBatchOffsetRange) {
batch.maxOffset - newOffset <= kMaxBatchOffsetRange) {
// We can extend this batch to the left in order to fit the new offset. // We can extend this batch to the left in order to fit the new offset.
batch.minOffset = newOffset; batch.minOffset = newOffset;
batch.draws.push_back(std::move(draw)); batch.draws.push_back(std::move(draw));
return; return;
} }
if (newOffset > batch.maxOffset && if (newOffset > batch.maxOffset && newOffset - batch.minOffset <= maxBatchOffsetRange) {
newOffset - batch.minOffset <= kMaxBatchOffsetRange) {
// We can extend this batch to the right in order to fit the new offset. // We can extend this batch to the right in order to fit the new offset.
batch.maxOffset = newOffset; batch.maxOffset = newOffset;
batch.draws.push_back(std::move(draw)); batch.draws.push_back(std::move(draw));
@ -95,14 +91,16 @@ namespace dawn_native {
} }
void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddBatch( void IndirectDrawMetadata::IndexedIndirectBufferValidationInfo::AddBatch(
uint32_t maxDrawCallsPerIndirectValidationBatch,
uint32_t maxBatchOffsetRange,
const IndexedIndirectValidationBatch& newBatch) { const IndexedIndirectValidationBatch& newBatch) {
auto it = mBatches.begin(); auto it = mBatches.begin();
while (it != mBatches.end()) { while (it != mBatches.end()) {
IndexedIndirectValidationBatch& batch = *it; IndexedIndirectValidationBatch& batch = *it;
uint64_t min = std::min(newBatch.minOffset, batch.minOffset); uint64_t min = std::min(newBatch.minOffset, batch.minOffset);
uint64_t max = std::max(newBatch.maxOffset, batch.maxOffset); uint64_t max = std::max(newBatch.maxOffset, batch.maxOffset);
if (max - min <= kMaxBatchOffsetRange && batch.draws.size() + newBatch.draws.size() <= if (max - min <= maxBatchOffsetRange && batch.draws.size() + newBatch.draws.size() <=
kMaxDrawCallsPerIndirectValidationBatch) { maxDrawCallsPerIndirectValidationBatch) {
// This batch fits within the limits of an existing batch. Merge it. // This batch fits within the limits of an existing batch. Merge it.
batch.minOffset = min; batch.minOffset = min;
batch.maxOffset = max; batch.maxOffset = max;
@ -124,7 +122,10 @@ namespace dawn_native {
return mBatches; return mBatches;
} }
IndirectDrawMetadata::IndirectDrawMetadata() = default; IndirectDrawMetadata::IndirectDrawMetadata(const CombinedLimits& limits)
: mMaxDrawCallsPerBatch(ComputeMaxDrawCallsPerIndirectValidationBatch(limits)),
mMaxBatchOffsetRange(ComputeMaxIndirectValidationBatchOffsetRange(limits)) {
}
IndirectDrawMetadata::~IndirectDrawMetadata() = default; IndirectDrawMetadata::~IndirectDrawMetadata() = default;
@ -150,7 +151,7 @@ namespace dawn_native {
if (it != mIndexedIndirectBufferValidationInfo.end() && it->first == config) { if (it != mIndexedIndirectBufferValidationInfo.end() && it->first == config) {
// We already have batches for the same config. Merge the new ones in. // We already have batches for the same config. Merge the new ones in.
for (const IndexedIndirectValidationBatch& batch : entry.second.GetBatches()) { for (const IndexedIndirectValidationBatch& batch : entry.second.GetBatches()) {
it->second.AddBatch(batch); it->second.AddBatch(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange, batch);
} }
} else { } else {
mIndexedIndirectBufferValidationInfo.emplace_hint(it, config, entry.second); mIndexedIndirectBufferValidationInfo.emplace_hint(it, config, entry.second);
@ -187,7 +188,8 @@ namespace dawn_native {
IndexedIndirectDraw draw; IndexedIndirectDraw draw;
draw.clientBufferOffset = indirectOffset; draw.clientBufferOffset = indirectOffset;
draw.bufferLocation = drawCmdIndirectBufferLocation; draw.bufferLocation = drawCmdIndirectBufferLocation;
it->second.AddIndexedIndirectDraw(std::move(draw)); it->second.AddIndexedIndirectDraw(mMaxDrawCallsPerBatch, mMaxBatchOffsetRange,
std::move(draw));
} }
} // namespace dawn_native } // namespace dawn_native

View File

@ -31,6 +31,11 @@
namespace dawn_native { namespace dawn_native {
class RenderBundleBase; class RenderBundleBase;
struct CombinedLimits;
// In the unlikely scenario that indirect offsets used over a single buffer span more than
// this length of the buffer, we split the validation work into multiple batches.
uint32_t ComputeMaxIndirectValidationBatchOffsetRange(const CombinedLimits& limits);
// Metadata corresponding to the validation requirements of a single render pass. This metadata // Metadata corresponding to the validation requirements of a single render pass. This metadata
// is accumulated while its corresponding render pass is encoded, and is later used to encode // is accumulated while its corresponding render pass is encoded, and is later used to encode
@ -58,12 +63,16 @@ namespace dawn_native {
// Logs a new drawIndexedIndirect call for the render pass. `cmd` is updated with an // Logs a new drawIndexedIndirect call for the render pass. `cmd` is updated with an
// assigned (and deferred) buffer ref and relative offset before returning. // assigned (and deferred) buffer ref and relative offset before returning.
void AddIndexedIndirectDraw(IndexedIndirectDraw draw); void AddIndexedIndirectDraw(uint32_t maxDrawCallsPerIndirectValidationBatch,
uint32_t maxBatchOffsetRange,
IndexedIndirectDraw draw);
// Adds draw calls from an already-computed batch, e.g. from a previously encoded // Adds draw calls from an already-computed batch, e.g. from a previously encoded
// RenderBundle. The added batch is merged into an existing batch if possible, otherwise // RenderBundle. The added batch is merged into an existing batch if possible, otherwise
// it's added to mBatch. // it's added to mBatch.
void AddBatch(const IndexedIndirectValidationBatch& batch); void AddBatch(uint32_t maxDrawCallsPerIndirectValidationBatch,
uint32_t maxBatchOffsetRange,
const IndexedIndirectValidationBatch& batch);
const std::vector<IndexedIndirectValidationBatch>& GetBatches() const; const std::vector<IndexedIndirectValidationBatch>& GetBatches() const;
@ -87,7 +96,7 @@ namespace dawn_native {
using IndexedIndirectBufferValidationInfoMap = using IndexedIndirectBufferValidationInfoMap =
std::map<IndexedIndirectConfig, IndexedIndirectBufferValidationInfo>; std::map<IndexedIndirectConfig, IndexedIndirectBufferValidationInfo>;
IndirectDrawMetadata(); explicit IndirectDrawMetadata(const CombinedLimits& limits);
~IndirectDrawMetadata(); ~IndirectDrawMetadata();
IndirectDrawMetadata(IndirectDrawMetadata&&); IndirectDrawMetadata(IndirectDrawMetadata&&);
@ -105,6 +114,9 @@ namespace dawn_native {
private: private:
IndexedIndirectBufferValidationInfoMap mIndexedIndirectBufferValidationInfo; IndexedIndirectBufferValidationInfoMap mIndexedIndirectBufferValidationInfo;
std::set<RenderBundleBase*> mAddedBundles; std::set<RenderBundleBase*> mAddedBundles;
uint32_t mMaxDrawCallsPerBatch;
uint32_t mMaxBatchOffsetRange;
}; };
} // namespace dawn_native } // namespace dawn_native

View File

@ -188,12 +188,15 @@ namespace dawn_native {
} // namespace } // namespace
const uint32_t kBatchDrawCallLimitByDispatchSize = uint32_t ComputeMaxDrawCallsPerIndirectValidationBatch(const CombinedLimits& limits) {
kMaxComputePerDimensionDispatchSize * kWorkgroupSize; const uint64_t batchDrawCallLimitByDispatchSize =
const uint32_t kBatchDrawCallLimitByStorageBindingSize = static_cast<uint64_t>(limits.v1.maxComputeWorkgroupsPerDimension) * kWorkgroupSize;
(kMaxStorageBufferBindingSize - sizeof(BatchInfo)) / sizeof(uint32_t); const uint64_t batchDrawCallLimitByStorageBindingSize =
const uint32_t kMaxDrawCallsPerIndirectValidationBatch = (limits.v1.maxStorageBufferBindingSize - sizeof(BatchInfo)) / sizeof(uint32_t);
std::min(kBatchDrawCallLimitByDispatchSize, kBatchDrawCallLimitByStorageBindingSize); return static_cast<uint32_t>(
std::min({batchDrawCallLimitByDispatchSize, batchDrawCallLimitByStorageBindingSize,
uint64_t(std::numeric_limits<uint32_t>::max())}));
}
MaybeError EncodeIndirectDrawValidationCommands(DeviceBase* device, MaybeError EncodeIndirectDrawValidationCommands(DeviceBase* device,
CommandEncoder* commandEncoder, CommandEncoder* commandEncoder,
@ -232,13 +235,18 @@ namespace dawn_native {
return {}; return {};
} }
const uint32_t maxStorageBufferBindingSize =
device->GetLimits().v1.maxStorageBufferBindingSize;
const uint32_t minStorageBufferOffsetAlignment =
device->GetLimits().v1.minStorageBufferOffsetAlignment;
for (auto& entry : bufferInfoMap) { for (auto& entry : bufferInfoMap) {
const IndirectDrawMetadata::IndexedIndirectConfig& config = entry.first; const IndirectDrawMetadata::IndexedIndirectConfig& config = entry.first;
BufferBase* clientIndirectBuffer = config.first; BufferBase* clientIndirectBuffer = config.first;
for (const IndirectDrawMetadata::IndexedIndirectValidationBatch& batch : for (const IndirectDrawMetadata::IndexedIndirectValidationBatch& batch :
entry.second.GetBatches()) { entry.second.GetBatches()) {
const uint64_t minOffsetFromAlignedBoundary = const uint64_t minOffsetFromAlignedBoundary =
batch.minOffset % kMinStorageBufferOffsetAlignment; batch.minOffset % minStorageBufferOffsetAlignment;
const uint64_t minOffsetAlignedDown = const uint64_t minOffsetAlignedDown =
batch.minOffset - minOffsetFromAlignedBoundary; batch.minOffset - minOffsetFromAlignedBoundary;
@ -253,18 +261,18 @@ namespace dawn_native {
newBatch.validatedParamsSize = batch.draws.size() * kDrawIndexedIndirectSize; newBatch.validatedParamsSize = batch.draws.size() * kDrawIndexedIndirectSize;
newBatch.validatedParamsOffset = newBatch.validatedParamsOffset =
Align(validatedParamsSize, kMinStorageBufferOffsetAlignment); Align(validatedParamsSize, minStorageBufferOffsetAlignment);
validatedParamsSize = newBatch.validatedParamsOffset + newBatch.validatedParamsSize; validatedParamsSize = newBatch.validatedParamsOffset + newBatch.validatedParamsSize;
if (validatedParamsSize > kMaxStorageBufferBindingSize) { if (validatedParamsSize > maxStorageBufferBindingSize) {
return DAWN_INTERNAL_ERROR("Too many drawIndexedIndirect calls to validate"); return DAWN_INTERNAL_ERROR("Too many drawIndexedIndirect calls to validate");
} }
Pass* currentPass = passes.empty() ? nullptr : &passes.back(); Pass* currentPass = passes.empty() ? nullptr : &passes.back();
if (currentPass && currentPass->clientIndirectBuffer == clientIndirectBuffer) { if (currentPass && currentPass->clientIndirectBuffer == clientIndirectBuffer) {
uint64_t nextBatchDataOffset = uint64_t nextBatchDataOffset =
Align(currentPass->batchDataSize, kMinStorageBufferOffsetAlignment); Align(currentPass->batchDataSize, minStorageBufferOffsetAlignment);
uint64_t newPassBatchDataSize = nextBatchDataOffset + newBatch.dataSize; uint64_t newPassBatchDataSize = nextBatchDataOffset + newBatch.dataSize;
if (newPassBatchDataSize <= kMaxStorageBufferBindingSize) { if (newPassBatchDataSize <= maxStorageBufferBindingSize) {
// We can fit this batch in the current pass. // We can fit this batch in the current pass.
newBatch.dataBufferOffset = nextBatchDataOffset; newBatch.dataBufferOffset = nextBatchDataOffset;
currentPass->batchDataSize = newPassBatchDataSize; currentPass->batchDataSize = newPassBatchDataSize;

View File

@ -21,13 +21,14 @@
namespace dawn_native { namespace dawn_native {
class CommandEncoder; class CommandEncoder;
struct CombinedLimits;
class DeviceBase; class DeviceBase;
class RenderPassResourceUsageTracker; class RenderPassResourceUsageTracker;
// The maximum number of draws call we can fit into a single validation batch. This is // The maximum number of draws call we can fit into a single validation batch. This is
// essentially limited by the number of indirect parameter blocks that can fit into the maximum // essentially limited by the number of indirect parameter blocks that can fit into the maximum
// allowed storage binding size (about 6.7M). // allowed storage binding size (with the base limits, it is about 6.7M).
extern const uint32_t kMaxDrawCallsPerIndirectValidationBatch; uint32_t ComputeMaxDrawCallsPerIndirectValidationBatch(const CombinedLimits& limits);
MaybeError EncodeIndirectDrawValidationCommands(DeviceBase* device, MaybeError EncodeIndirectDrawValidationCommands(DeviceBase* device,
CommandEncoder* commandEncoder, CommandEncoder* commandEncoder,

View File

@ -139,13 +139,12 @@ namespace dawn_native {
uint64_t requiredAlignment; uint64_t requiredAlignment;
switch (bindingInfo.buffer.type) { switch (bindingInfo.buffer.type) {
case wgpu::BufferBindingType::Uniform: case wgpu::BufferBindingType::Uniform:
requiredAlignment = kMinUniformBufferOffsetAlignment; requiredAlignment = GetDevice()->GetLimits().v1.minUniformBufferOffsetAlignment;
break; break;
case wgpu::BufferBindingType::Storage: case wgpu::BufferBindingType::Storage:
case wgpu::BufferBindingType::ReadOnlyStorage: case wgpu::BufferBindingType::ReadOnlyStorage:
case kInternalStorageBufferBinding: case kInternalStorageBufferBinding:
requiredAlignment = kMinStorageBufferOffsetAlignment; requiredAlignment = GetDevice()->GetLimits().v1.minStorageBufferOffsetAlignment;
requiredAlignment = kMinStorageBufferOffsetAlignment;
break; break;
case wgpu::BufferBindingType::Undefined: case wgpu::BufferBindingType::Undefined:
UNREACHABLE(); UNREACHABLE();

View File

@ -44,7 +44,7 @@ namespace dawn_native {
} }
RenderBundleBase::RenderBundleBase(DeviceBase* device, ErrorTag errorTag) RenderBundleBase::RenderBundleBase(DeviceBase* device, ErrorTag errorTag)
: ApiObjectBase(device, errorTag) { : ApiObjectBase(device, errorTag), mIndirectDrawMetadata(device->GetLimits()) {
} }
ObjectType RenderBundleBase::GetType() const { ObjectType RenderBundleBase::GetType() const {

View File

@ -34,6 +34,7 @@ namespace dawn_native {
EncodingContext* encodingContext, EncodingContext* encodingContext,
Ref<AttachmentState> attachmentState) Ref<AttachmentState> attachmentState)
: ProgrammablePassEncoder(device, encodingContext), : ProgrammablePassEncoder(device, encodingContext),
mIndirectDrawMetadata(device->GetLimits()),
mAttachmentState(std::move(attachmentState)), mAttachmentState(std::move(attachmentState)),
mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)), mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)),
mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) { mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) {
@ -43,6 +44,7 @@ namespace dawn_native {
EncodingContext* encodingContext, EncodingContext* encodingContext,
ErrorTag errorTag) ErrorTag errorTag)
: ProgrammablePassEncoder(device, encodingContext, errorTag), : ProgrammablePassEncoder(device, encodingContext, errorTag),
mIndirectDrawMetadata(device->GetLimits()),
mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)), mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)),
mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) { mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) {
} }

View File

@ -609,10 +609,12 @@ namespace dawn_native {
} }
ResultOrError<EntryPointMetadataTable> ReflectShaderUsingTint( ResultOrError<EntryPointMetadataTable> ReflectShaderUsingTint(
DeviceBase*, const DeviceBase* device,
const tint::Program* program) { const tint::Program* program) {
ASSERT(program->IsValid()); ASSERT(program->IsValid());
const CombinedLimits& limits = device->GetLimits();
EntryPointMetadataTable result; EntryPointMetadataTable result;
tint::inspector::Inspector inspector(program); tint::inspector::Inspector inspector(program);
@ -645,36 +647,32 @@ namespace dawn_native {
DAWN_TRY_ASSIGN(metadata->stage, TintPipelineStageToShaderStage(entryPoint.stage)); DAWN_TRY_ASSIGN(metadata->stage, TintPipelineStageToShaderStage(entryPoint.stage));
if (metadata->stage == SingleShaderStage::Compute) { if (metadata->stage == SingleShaderStage::Compute) {
DAWN_INVALID_IF(entryPoint.workgroup_size_x > kMaxComputeWorkgroupSizeX || DAWN_INVALID_IF(
entryPoint.workgroup_size_y > kMaxComputeWorkgroupSizeY || entryPoint.workgroup_size_x > limits.v1.maxComputeWorkgroupSizeX ||
entryPoint.workgroup_size_z > kMaxComputeWorkgroupSizeZ, entryPoint.workgroup_size_y > limits.v1.maxComputeWorkgroupSizeY ||
entryPoint.workgroup_size_z > limits.v1.maxComputeWorkgroupSizeZ,
"Entry-point uses workgroup_size(%u, %u, %u) that exceeds the " "Entry-point uses workgroup_size(%u, %u, %u) that exceeds the "
"maximum allowed (%u, %u, %u).", "maximum allowed (%u, %u, %u).",
entryPoint.workgroup_size_x, entryPoint.workgroup_size_y, entryPoint.workgroup_size_x, entryPoint.workgroup_size_y,
entryPoint.workgroup_size_z, kMaxComputeWorkgroupSizeX, entryPoint.workgroup_size_z, limits.v1.maxComputeWorkgroupSizeX,
kMaxComputeWorkgroupSizeY, kMaxComputeWorkgroupSizeZ); limits.v1.maxComputeWorkgroupSizeY, limits.v1.maxComputeWorkgroupSizeZ);
// Dimensions have already been validated against their individual limits above. // Dimensions have already been validated against their individual limits above.
// This assertion ensures that the product of such limited dimensions cannot // Cast to uint64_t to avoid overflow in this multiplication.
// possibly overflow a uint32_t. uint64_t numInvocations = static_cast<uint64_t>(entryPoint.workgroup_size_x) *
static_assert(static_cast<uint64_t>(kMaxComputeWorkgroupSizeX) *
kMaxComputeWorkgroupSizeY * kMaxComputeWorkgroupSizeZ <=
std::numeric_limits<uint32_t>::max(),
"Per-dimension workgroup size limits are too high");
uint32_t numInvocations = entryPoint.workgroup_size_x *
entryPoint.workgroup_size_y * entryPoint.workgroup_size_y *
entryPoint.workgroup_size_z; entryPoint.workgroup_size_z;
DAWN_INVALID_IF(numInvocations > kMaxComputeWorkgroupInvocations, DAWN_INVALID_IF(numInvocations > limits.v1.maxComputeInvocationsPerWorkgroup,
"The total number of workgroup invocations (%u) exceeds the " "The total number of workgroup invocations (%u) exceeds the "
"maximum allowed (%u).", "maximum allowed (%u).",
numInvocations, kMaxComputeWorkgroupInvocations); numInvocations, limits.v1.maxComputeInvocationsPerWorkgroup);
const size_t workgroupStorageSize = const size_t workgroupStorageSize =
inspector.GetWorkgroupStorageSize(entryPoint.name); inspector.GetWorkgroupStorageSize(entryPoint.name);
DAWN_INVALID_IF(workgroupStorageSize > kMaxComputeWorkgroupStorageSize, DAWN_INVALID_IF(workgroupStorageSize > limits.v1.maxComputeWorkgroupStorageSize,
"The total use of workgroup storage (%u bytes) is larger than " "The total use of workgroup storage (%u bytes) is larger than "
"the maximum allowed (%u bytes).", "the maximum allowed (%u bytes).",
workgroupStorageSize, kMaxComputeWorkgroupStorageSize); workgroupStorageSize, limits.v1.maxComputeWorkgroupStorageSize);
metadata->localWorkgroupSize.x = entryPoint.workgroup_size_x; metadata->localWorkgroupSize.x = entryPoint.workgroup_size_x;
metadata->localWorkgroupSize.y = entryPoint.workgroup_size_y; metadata->localWorkgroupSize.y = entryPoint.workgroup_size_y;

View File

@ -90,8 +90,8 @@ namespace dawn_native {
return DAWN_VALIDATION_ERROR("Swapchain size can't be empty"); return DAWN_VALIDATION_ERROR("Swapchain size can't be empty");
} }
if (descriptor->width > kMaxTextureDimension2D || if (descriptor->width > device->GetLimits().v1.maxTextureDimension2D ||
descriptor->height > kMaxTextureDimension2D) { descriptor->height > device->GetLimits().v1.maxTextureDimension2D) {
return DAWN_VALIDATION_ERROR("Swapchain size too big"); return DAWN_VALIDATION_ERROR("Swapchain size too big");
} }
} }

View File

@ -173,19 +173,21 @@ namespace dawn_native {
return {}; return {};
} }
MaybeError ValidateTextureSize(const TextureDescriptor* descriptor, const Format* format) { MaybeError ValidateTextureSize(const DeviceBase* device,
const TextureDescriptor* descriptor,
const Format* format) {
ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0 && ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0 &&
descriptor->size.depthOrArrayLayers != 0); descriptor->size.depthOrArrayLayers != 0);
const CombinedLimits& limits = device->GetLimits();
Extent3D maxExtent; Extent3D maxExtent;
switch (descriptor->dimension) { switch (descriptor->dimension) {
case wgpu::TextureDimension::e2D: case wgpu::TextureDimension::e2D:
maxExtent = {kMaxTextureDimension2D, kMaxTextureDimension2D, maxExtent = {limits.v1.maxTextureDimension2D, limits.v1.maxTextureDimension2D,
kMaxTextureArrayLayers}; limits.v1.maxTextureArrayLayers};
break; break;
case wgpu::TextureDimension::e3D: case wgpu::TextureDimension::e3D:
maxExtent = {kMaxTextureDimension3D, kMaxTextureDimension3D, maxExtent = {limits.v1.maxTextureDimension3D, limits.v1.maxTextureDimension3D,
kMaxTextureDimension3D}; limits.v1.maxTextureDimension3D};
break; break;
case wgpu::TextureDimension::e1D: case wgpu::TextureDimension::e1D:
default: default:
@ -210,8 +212,6 @@ namespace dawn_native {
"Texture mip level count (%u) exceeds the maximum (%u) for its size (%s).", "Texture mip level count (%u) exceeds the maximum (%u) for its size (%s).",
descriptor->mipLevelCount, Log2(maxMippedDimension) + 1, &descriptor->size); descriptor->mipLevelCount, Log2(maxMippedDimension) + 1, &descriptor->size);
ASSERT(descriptor->mipLevelCount <= kMaxTexture2DMipLevels);
if (format->isCompressed) { if (format->isCompressed) {
const TexelBlockInfo& blockInfo = const TexelBlockInfo& blockInfo =
format->GetAspectInfo(wgpu::TextureAspect::All).block; format->GetAspectInfo(wgpu::TextureAspect::All).block;
@ -308,7 +308,7 @@ namespace dawn_native {
"The dimension (%s) of a texture with a depth/stencil format (%s) is not 2D.", "The dimension (%s) of a texture with a depth/stencil format (%s) is not 2D.",
descriptor->dimension, format->format); descriptor->dimension, format->format);
DAWN_TRY(ValidateTextureSize(descriptor, format)); DAWN_TRY(ValidateTextureSize(device, descriptor, format));
// TODO(crbug.com/dawn/838): Implement a workaround for this issue. // TODO(crbug.com/dawn/838): Implement a workaround for this issue.
// Readbacks from the non-zero mip of a stencil texture may contain garbage data. // Readbacks from the non-zero mip of a stencil texture may contain garbage data.
@ -555,12 +555,7 @@ namespace dawn_native {
uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel, uint32_t TextureBase::GetSubresourceIndex(uint32_t mipLevel,
uint32_t arraySlice, uint32_t arraySlice,
Aspect aspect) const { Aspect aspect) const {
ASSERT(arraySlice <= kMaxTextureArrayLayers);
ASSERT(mipLevel <= kMaxTexture2DMipLevels);
ASSERT(HasOneBit(aspect)); ASSERT(HasOneBit(aspect));
static_assert(
kMaxTexture2DMipLevels <= std::numeric_limits<uint32_t>::max() / kMaxTextureArrayLayers,
"texture size overflows uint32_t");
return mipLevel + return mipLevel +
GetNumMipLevels() * (arraySlice + GetArrayLayers() * GetAspectIndex(aspect)); GetNumMipLevels() * (arraySlice + GetArrayLayers() * GetAspectIndex(aspect));
} }

View File

@ -14,6 +14,7 @@
#include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/AdapterVk.h"
#include "dawn_native/Limits.h"
#include "dawn_native/vulkan/BackendVk.h" #include "dawn_native/vulkan/BackendVk.h"
#include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/DeviceVk.h"
@ -78,6 +79,9 @@ namespace dawn_native { namespace vulkan {
} }
MaybeError Adapter::CheckCoreWebGPUSupport() { MaybeError Adapter::CheckCoreWebGPUSupport() {
Limits baseLimits;
GetDefaultLimits(&baseLimits);
// Needed for viewport Y-flip. // Needed for viewport Y-flip.
if (!mDeviceInfo.HasExt(DeviceExt::Maintenance1)) { if (!mDeviceInfo.HasExt(DeviceExt::Maintenance1)) {
return DAWN_INTERNAL_ERROR("Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance1 required."); return DAWN_INTERNAL_ERROR("Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance1 required.");
@ -118,106 +122,110 @@ namespace dawn_native { namespace vulkan {
// Check base WebGPU limits are supported. // Check base WebGPU limits are supported.
const VkPhysicalDeviceLimits& limits = mDeviceInfo.properties.limits; const VkPhysicalDeviceLimits& limits = mDeviceInfo.properties.limits;
if (limits.maxImageDimension1D < kMaxTextureDimension1D) { if (limits.maxImageDimension1D < baseLimits.maxTextureDimension1D) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension1D"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension1D");
} }
if (limits.maxImageDimension2D < kMaxTextureDimension2D || if (limits.maxImageDimension2D < baseLimits.maxTextureDimension2D ||
limits.maxImageDimensionCube < kMaxTextureDimension2D || limits.maxImageDimensionCube < baseLimits.maxTextureDimension2D ||
limits.maxFramebufferWidth < kMaxTextureDimension2D || limits.maxFramebufferWidth < baseLimits.maxTextureDimension2D ||
limits.maxFramebufferHeight < kMaxTextureDimension2D || limits.maxFramebufferHeight < baseLimits.maxTextureDimension2D ||
limits.maxViewportDimensions[0] < kMaxTextureDimension2D || limits.maxViewportDimensions[0] < baseLimits.maxTextureDimension2D ||
limits.maxViewportDimensions[1] < kMaxTextureDimension2D || limits.maxViewportDimensions[1] < baseLimits.maxTextureDimension2D ||
limits.viewportBoundsRange[1] < kMaxTextureDimension2D) { limits.viewportBoundsRange[1] < baseLimits.maxTextureDimension2D) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension2D"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension2D");
} }
if (limits.maxImageDimension3D < kMaxTextureDimension3D) { if (limits.maxImageDimension3D < baseLimits.maxTextureDimension3D) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension3D"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureDimension3D");
} }
if (limits.maxImageArrayLayers < kMaxTextureArrayLayers) { if (limits.maxImageArrayLayers < baseLimits.maxTextureArrayLayers) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureArrayLayers"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxTextureArrayLayers");
} }
if (limits.maxBoundDescriptorSets < kMaxBindGroups) { if (limits.maxBoundDescriptorSets < baseLimits.maxBindGroups) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxBindGroups"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxBindGroups");
} }
if (limits.maxDescriptorSetUniformBuffersDynamic < if (limits.maxDescriptorSetUniformBuffersDynamic <
kMaxDynamicUniformBuffersPerPipelineLayout) { baseLimits.maxDynamicUniformBuffersPerPipelineLayout) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxDynamicUniformBuffersPerPipelineLayout"); "Insufficient Vulkan limits for maxDynamicUniformBuffersPerPipelineLayout");
} }
if (limits.maxDescriptorSetStorageBuffersDynamic < if (limits.maxDescriptorSetStorageBuffersDynamic <
kMaxDynamicStorageBuffersPerPipelineLayout) { baseLimits.maxDynamicStorageBuffersPerPipelineLayout) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxDynamicStorageBuffersPerPipelineLayout"); "Insufficient Vulkan limits for maxDynamicStorageBuffersPerPipelineLayout");
} }
if (limits.maxPerStageDescriptorSampledImages < kMaxSampledTexturesPerShaderStage) { if (limits.maxPerStageDescriptorSampledImages <
baseLimits.maxSampledTexturesPerShaderStage) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxSampledTexturesPerShaderStage"); "Insufficient Vulkan limits for maxSampledTexturesPerShaderStage");
} }
if (limits.maxPerStageDescriptorSamplers < kMaxSamplersPerShaderStage) { if (limits.maxPerStageDescriptorSamplers < baseLimits.maxSamplersPerShaderStage) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxSamplersPerShaderStage"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxSamplersPerShaderStage");
} }
if (limits.maxPerStageDescriptorStorageBuffers < kMaxStorageBuffersPerShaderStage) { if (limits.maxPerStageDescriptorStorageBuffers <
baseLimits.maxStorageBuffersPerShaderStage) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxStorageBuffersPerShaderStage"); "Insufficient Vulkan limits for maxStorageBuffersPerShaderStage");
} }
if (limits.maxPerStageDescriptorStorageImages < kMaxStorageTexturesPerShaderStage) { if (limits.maxPerStageDescriptorStorageImages <
baseLimits.maxStorageTexturesPerShaderStage) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxStorageTexturesPerShaderStage"); "Insufficient Vulkan limits for maxStorageTexturesPerShaderStage");
} }
if (limits.maxPerStageDescriptorUniformBuffers < kMaxUniformBuffersPerShaderStage) { if (limits.maxPerStageDescriptorUniformBuffers <
baseLimits.maxUniformBuffersPerShaderStage) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxUniformBuffersPerShaderStage"); "Insufficient Vulkan limits for maxUniformBuffersPerShaderStage");
} }
if (limits.maxUniformBufferRange < kMaxUniformBufferBindingSize) { if (limits.maxUniformBufferRange < baseLimits.maxUniformBufferBindingSize) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxUniformBufferBindingSize"); "Insufficient Vulkan limits for maxUniformBufferBindingSize");
} }
if (limits.maxStorageBufferRange < kMaxStorageBufferBindingSize) { if (limits.maxStorageBufferRange < baseLimits.maxStorageBufferBindingSize) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxStorageBufferBindingSize"); "Insufficient Vulkan limits for maxStorageBufferBindingSize");
} }
if (limits.minUniformBufferOffsetAlignment > kMinUniformBufferOffsetAlignment) { if (limits.minUniformBufferOffsetAlignment > baseLimits.minUniformBufferOffsetAlignment) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for minUniformBufferOffsetAlignment"); "Insufficient Vulkan limits for minUniformBufferOffsetAlignment");
} }
if (limits.minStorageBufferOffsetAlignment > kMinStorageBufferOffsetAlignment) { if (limits.minStorageBufferOffsetAlignment > baseLimits.minStorageBufferOffsetAlignment) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for minStorageBufferOffsetAlignment"); "Insufficient Vulkan limits for minStorageBufferOffsetAlignment");
} }
if (limits.maxVertexInputBindings < kMaxVertexBuffers) { if (limits.maxVertexInputBindings < baseLimits.maxVertexBuffers) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBuffers"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBuffers");
} }
if (limits.maxVertexInputAttributes < kMaxVertexAttributes) { if (limits.maxVertexInputAttributes < baseLimits.maxVertexAttributes) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexAttributes"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexAttributes");
} }
if (limits.maxVertexInputBindingStride < kMaxVertexBufferArrayStride || if (limits.maxVertexInputBindingStride < baseLimits.maxVertexBufferArrayStride ||
limits.maxVertexInputAttributeOffset < kMaxVertexBufferArrayStride - 1) { limits.maxVertexInputAttributeOffset < baseLimits.maxVertexBufferArrayStride - 1) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBufferArrayStride"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxVertexBufferArrayStride");
} }
if (limits.maxVertexOutputComponents < kMaxInterStageShaderComponents || if (limits.maxVertexOutputComponents < baseLimits.maxInterStageShaderComponents ||
limits.maxFragmentInputComponents < kMaxInterStageShaderComponents) { limits.maxFragmentInputComponents < baseLimits.maxInterStageShaderComponents) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxInterStageShaderComponents"); "Insufficient Vulkan limits for maxInterStageShaderComponents");
} }
if (limits.maxComputeSharedMemorySize < kMaxComputeWorkgroupStorageSize) { if (limits.maxComputeSharedMemorySize < baseLimits.maxComputeWorkgroupStorageSize) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxComputeWorkgroupStorageSize"); "Insufficient Vulkan limits for maxComputeWorkgroupStorageSize");
} }
if (limits.maxComputeWorkGroupInvocations < kMaxComputeWorkgroupInvocations) { if (limits.maxComputeWorkGroupInvocations < baseLimits.maxComputeInvocationsPerWorkgroup) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxComputeWorkgroupInvocations"); "Insufficient Vulkan limits for maxComputeInvocationsPerWorkgroup");
} }
if (limits.maxComputeWorkGroupSize[0] < kMaxComputeWorkgroupSizeX || if (limits.maxComputeWorkGroupSize[0] < baseLimits.maxComputeWorkgroupSizeX ||
limits.maxComputeWorkGroupSize[1] < kMaxComputeWorkgroupSizeY || limits.maxComputeWorkGroupSize[1] < baseLimits.maxComputeWorkgroupSizeY ||
limits.maxComputeWorkGroupSize[2] < kMaxComputeWorkgroupSizeZ) { limits.maxComputeWorkGroupSize[2] < baseLimits.maxComputeWorkgroupSizeZ) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxComputeWorkgroupSize"); "Insufficient Vulkan limits for maxComputeWorkgroupSize");
} }
if (limits.maxComputeWorkGroupCount[0] < kMaxComputePerDimensionDispatchSize || if (limits.maxComputeWorkGroupCount[0] < baseLimits.maxComputeWorkgroupsPerDimension ||
limits.maxComputeWorkGroupCount[1] < kMaxComputePerDimensionDispatchSize || limits.maxComputeWorkGroupCount[1] < baseLimits.maxComputeWorkgroupsPerDimension ||
limits.maxComputeWorkGroupCount[2] < kMaxComputePerDimensionDispatchSize) { limits.maxComputeWorkGroupCount[2] < baseLimits.maxComputeWorkgroupsPerDimension) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan limits for maxComputePerDimensionDispatchSize"); "Insufficient Vulkan limits for maxComputeWorkgroupsPerDimension");
} }
if (limits.maxColorAttachments < kMaxColorAttachments) { if (limits.maxColorAttachments < kMaxColorAttachments) {
return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxColorAttachments"); return DAWN_INTERNAL_ERROR("Insufficient Vulkan limits for maxColorAttachments");
@ -239,9 +247,9 @@ namespace dawn_native { namespace vulkan {
uint32_t vendorId = mDeviceInfo.properties.vendorID; uint32_t vendorId = mDeviceInfo.properties.vendorID;
if (!gpu_info::IsAMD(vendorId) && !gpu_info::IsIntel(vendorId) && if (!gpu_info::IsAMD(vendorId) && !gpu_info::IsIntel(vendorId) &&
!gpu_info::IsNvidia(vendorId)) { !gpu_info::IsNvidia(vendorId)) {
if (limits.maxFragmentCombinedOutputResources < kMaxColorAttachments + if (limits.maxFragmentCombinedOutputResources <
kMaxStorageTexturesPerShaderStage + kMaxColorAttachments + baseLimits.maxStorageTexturesPerShaderStage +
kMaxStorageBuffersPerShaderStage) { baseLimits.maxStorageBuffersPerShaderStage) {
return DAWN_INTERNAL_ERROR( return DAWN_INTERNAL_ERROR(
"Insufficient Vulkan maxFragmentCombinedOutputResources limit"); "Insufficient Vulkan maxFragmentCombinedOutputResources limit");
} }

View File

@ -862,6 +862,13 @@ const wgpu::AdapterProperties& DawnTestBase::GetAdapterProperties() const {
return mParam.adapterProperties; return mParam.adapterProperties;
} }
wgpu::SupportedLimits DawnTestBase::GetSupportedLimits() {
WGPUSupportedLimits supportedLimits;
supportedLimits.nextInChain = nullptr;
dawn_native::GetProcs().deviceGetLimits(backendDevice, &supportedLimits);
return *reinterpret_cast<wgpu::SupportedLimits*>(&supportedLimits);
}
bool DawnTestBase::SupportsFeatures(const std::vector<const char*>& features) { bool DawnTestBase::SupportsFeatures(const std::vector<const char*>& features) {
ASSERT(mBackendAdapter); ASSERT(mBackendAdapter);
std::set<std::string> supportedFeaturesSet; std::set<std::string> supportedFeaturesSet;

View File

@ -485,6 +485,11 @@ class DawnTestBase {
const wgpu::AdapterProperties& GetAdapterProperties() const; const wgpu::AdapterProperties& GetAdapterProperties() const;
// TODO(crbug.com/dawn/689): Use limits returned from the wire
// This is implemented here because tests need to always query
// the |backendDevice| since limits are not implemented in the wire.
wgpu::SupportedLimits GetSupportedLimits();
private: private:
utils::ScopedAutoreleasePool mObjCAutoreleasePool; utils::ScopedAutoreleasePool mObjCAutoreleasePool;
AdapterTestParam mParam; AdapterTestParam mParam;

View File

@ -23,6 +23,11 @@ constexpr static uint32_t kRTSize = 8;
class BindGroupTests : public DawnTest { class BindGroupTests : public DawnTest {
protected: protected:
void SetUp() override {
DawnTest::SetUp();
mMinUniformBufferOffsetAlignment =
GetSupportedLimits().limits.minUniformBufferOffsetAlignment;
}
wgpu::CommandBuffer CreateSimpleComputeCommandBuffer(const wgpu::ComputePipeline& pipeline, wgpu::CommandBuffer CreateSimpleComputeCommandBuffer(const wgpu::ComputePipeline& pipeline,
const wgpu::BindGroup& bindGroup) { const wgpu::BindGroup& bindGroup) {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@ -116,6 +121,8 @@ class BindGroupTests : public DawnTest {
return device.CreateRenderPipeline(&pipelineDescriptor); return device.CreateRenderPipeline(&pipelineDescriptor);
} }
uint32_t mMinUniformBufferOffsetAlignment;
}; };
// Test a bindgroup reused in two command buffers in the same call to queue.Submit(). // Test a bindgroup reused in two command buffers in the same call to queue.Submit().
@ -649,7 +656,7 @@ TEST_P(BindGroupTests, SetDynamicBindGroupBeforePipeline) {
std::array<float, 4> color0 = {1, 0, 0, 0.501}; std::array<float, 4> color0 = {1, 0, 0, 0.501};
std::array<float, 4> color1 = {0, 1, 0, 0.501}; std::array<float, 4> color1 = {0, 1, 0, 0.501};
size_t color1Offset = Align(sizeof(color0), kMinUniformBufferOffsetAlignment); size_t color1Offset = Align(sizeof(color0), mMinUniformBufferOffsetAlignment);
std::vector<uint8_t> data(color1Offset + sizeof(color1)); std::vector<uint8_t> data(color1Offset + sizeof(color1));
memcpy(data.data(), color0.data(), sizeof(color0)); memcpy(data.data(), color0.data(), sizeof(color0));
@ -719,7 +726,7 @@ TEST_P(BindGroupTests, BindGroupsPersistAfterPipelineChange) {
std::array<float, 4> color0 = {1, 0, 0, 0.5}; std::array<float, 4> color0 = {1, 0, 0, 0.5};
std::array<float, 4> color1 = {0, 1, 0, 0.5}; std::array<float, 4> color1 = {0, 1, 0, 0.5};
size_t color1Offset = Align(sizeof(color0), kMinUniformBufferOffsetAlignment); size_t color1Offset = Align(sizeof(color0), mMinUniformBufferOffsetAlignment);
std::vector<uint8_t> data(color1Offset + sizeof(color1)); std::vector<uint8_t> data(color1Offset + sizeof(color1));
memcpy(data.data(), color0.data(), sizeof(color0)); memcpy(data.data(), color0.data(), sizeof(color0));
@ -806,9 +813,9 @@ TEST_P(BindGroupTests, DrawThenChangePipelineAndBindGroup) {
std::array<float, 4> color2 = {0, 0, 0, 0.501}; std::array<float, 4> color2 = {0, 0, 0, 0.501};
std::array<float, 4> color3 = {0, 0, 1, 0}; std::array<float, 4> color3 = {0, 0, 1, 0};
size_t color1Offset = Align(sizeof(color0), kMinUniformBufferOffsetAlignment); size_t color1Offset = Align(sizeof(color0), mMinUniformBufferOffsetAlignment);
size_t color2Offset = Align(color1Offset + sizeof(color1), kMinUniformBufferOffsetAlignment); size_t color2Offset = Align(color1Offset + sizeof(color1), mMinUniformBufferOffsetAlignment);
size_t color3Offset = Align(color2Offset + sizeof(color2), kMinUniformBufferOffsetAlignment); size_t color3Offset = Align(color2Offset + sizeof(color2), mMinUniformBufferOffsetAlignment);
std::vector<uint8_t> data(color3Offset + sizeof(color3), 0); std::vector<uint8_t> data(color3Offset + sizeof(color3), 0);
memcpy(data.data(), color0.data(), sizeof(color0)); memcpy(data.data(), color0.data(), sizeof(color0));
@ -906,9 +913,9 @@ TEST_P(BindGroupTests, DrawThenChangePipelineTwiceAndBindGroup) {
std::array<float, 4> color3 = {0, 0, 0, 1}; std::array<float, 4> color3 = {0, 0, 0, 1};
size_t color0Offset = 0; size_t color0Offset = 0;
size_t color1Offset = Align(color0Offset + sizeof(color0), kMinUniformBufferOffsetAlignment); size_t color1Offset = Align(color0Offset + sizeof(color0), mMinUniformBufferOffsetAlignment);
size_t color2Offset = Align(color1Offset + sizeof(color1), kMinUniformBufferOffsetAlignment); size_t color2Offset = Align(color1Offset + sizeof(color1), mMinUniformBufferOffsetAlignment);
size_t color3Offset = Align(color2Offset + sizeof(color2), kMinUniformBufferOffsetAlignment); size_t color3Offset = Align(color2Offset + sizeof(color2), mMinUniformBufferOffsetAlignment);
std::vector<uint8_t> data(color3Offset + sizeof(color3), 0); std::vector<uint8_t> data(color3Offset + sizeof(color3), 0);
memcpy(data.data(), color0.data(), sizeof(color0)); memcpy(data.data(), color0.data(), sizeof(color0));
@ -985,14 +992,14 @@ TEST_P(BindGroupTests, DynamicOffsetOrder) {
// We will put the following values and the respective offsets into a buffer. // We will put the following values and the respective offsets into a buffer.
// The test will ensure that the correct dynamic offset is applied to each buffer by reading the // The test will ensure that the correct dynamic offset is applied to each buffer by reading the
// value from an offset binding. // value from an offset binding.
std::array<uint32_t, 3> offsets = {3 * kMinUniformBufferOffsetAlignment, std::array<uint32_t, 3> offsets = {3 * mMinUniformBufferOffsetAlignment,
1 * kMinUniformBufferOffsetAlignment, 1 * mMinUniformBufferOffsetAlignment,
2 * kMinUniformBufferOffsetAlignment}; 2 * mMinUniformBufferOffsetAlignment};
std::array<uint32_t, 3> values = {21, 67, 32}; std::array<uint32_t, 3> values = {21, 67, 32};
// Create three buffers large enough to by offset by the largest offset. // Create three buffers large enough to by offset by the largest offset.
wgpu::BufferDescriptor bufferDescriptor; wgpu::BufferDescriptor bufferDescriptor;
bufferDescriptor.size = 3 * kMinUniformBufferOffsetAlignment + sizeof(uint32_t); bufferDescriptor.size = 3 * mMinUniformBufferOffsetAlignment + sizeof(uint32_t);
bufferDescriptor.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst; bufferDescriptor.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst;
wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDescriptor); wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDescriptor);
@ -1075,19 +1082,19 @@ TEST_P(BindGroupTests, DynamicAndNonDynamicBindingsDoNotConflictAfterRemapping)
uint32_t dynamicBufferBindingNumber = dynamicBufferFirst ? 0 : 1; uint32_t dynamicBufferBindingNumber = dynamicBufferFirst ? 0 : 1;
uint32_t bufferBindingNumber = dynamicBufferFirst ? 1 : 0; uint32_t bufferBindingNumber = dynamicBufferFirst ? 1 : 0;
std::array<uint32_t, 1> offsets{kMinUniformBufferOffsetAlignment}; std::array<uint32_t, 1> offsets{mMinUniformBufferOffsetAlignment};
std::array<uint32_t, 2> values = {21, 67}; std::array<uint32_t, 2> values = {21, 67};
// Create three buffers large enough to by offset by the largest offset. // Create three buffers large enough to by offset by the largest offset.
wgpu::BufferDescriptor bufferDescriptor; wgpu::BufferDescriptor bufferDescriptor;
bufferDescriptor.size = 2 * kMinUniformBufferOffsetAlignment + sizeof(uint32_t); bufferDescriptor.size = 2 * mMinUniformBufferOffsetAlignment + sizeof(uint32_t);
bufferDescriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst; bufferDescriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
wgpu::Buffer dynamicBuffer = device.CreateBuffer(&bufferDescriptor); wgpu::Buffer dynamicBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
// Populate the values // Populate the values
queue.WriteBuffer(dynamicBuffer, kMinUniformBufferOffsetAlignment, queue.WriteBuffer(dynamicBuffer, mMinUniformBufferOffsetAlignment,
&values[dynamicBufferBindingNumber], sizeof(uint32_t)); &values[dynamicBufferBindingNumber], sizeof(uint32_t));
queue.WriteBuffer(buffer, 0, &values[bufferBindingNumber], sizeof(uint32_t)); queue.WriteBuffer(buffer, 0, &values[bufferBindingNumber], sizeof(uint32_t));

View File

@ -18,8 +18,6 @@
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"
constexpr uint32_t kRTSize = 400; constexpr uint32_t kRTSize = 400;
constexpr uint32_t kBufferElementsCount = kMinUniformBufferOffsetAlignment / sizeof(uint32_t) + 2;
constexpr uint32_t kBufferSize = kBufferElementsCount * sizeof(uint32_t);
constexpr uint32_t kBindingSize = 8; constexpr uint32_t kBindingSize = 8;
class DynamicBufferOffsetTests : public DawnTest { class DynamicBufferOffsetTests : public DawnTest {
@ -27,24 +25,29 @@ class DynamicBufferOffsetTests : public DawnTest {
void SetUp() override { void SetUp() override {
DawnTest::SetUp(); DawnTest::SetUp();
mMinUniformBufferOffsetAlignment =
GetSupportedLimits().limits.minUniformBufferOffsetAlignment;
// Mix up dynamic and non dynamic resources in one bind group and using not continuous // Mix up dynamic and non dynamic resources in one bind group and using not continuous
// binding number to cover more cases. // binding number to cover more cases.
std::array<uint32_t, kBufferElementsCount> uniformData = {0}; std::vector<uint32_t> uniformData(mMinUniformBufferOffsetAlignment / sizeof(uint32_t) + 2);
uniformData[0] = 1; uniformData[0] = 1;
uniformData[1] = 2; uniformData[1] = 2;
mUniformBuffers[0] = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, mUniformBuffers[0] = utils::CreateBufferFromData(device, uniformData.data(),
sizeof(uint32_t) * uniformData.size(),
wgpu::BufferUsage::Uniform); wgpu::BufferUsage::Uniform);
uniformData[uniformData.size() - 2] = 5; uniformData[uniformData.size() - 2] = 5;
uniformData[uniformData.size() - 1] = 6; uniformData[uniformData.size() - 1] = 6;
// Dynamic uniform buffer // Dynamic uniform buffer
mUniformBuffers[1] = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, mUniformBuffers[1] = utils::CreateBufferFromData(device, uniformData.data(),
sizeof(uint32_t) * uniformData.size(),
wgpu::BufferUsage::Uniform); wgpu::BufferUsage::Uniform);
wgpu::BufferDescriptor storageBufferDescriptor; wgpu::BufferDescriptor storageBufferDescriptor;
storageBufferDescriptor.size = kBufferSize; storageBufferDescriptor.size = sizeof(uint32_t) * uniformData.size();
storageBufferDescriptor.usage = storageBufferDescriptor.usage =
wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
@ -72,7 +75,8 @@ class DynamicBufferOffsetTests : public DawnTest {
{4, mStorageBuffers[1], 0, kBindingSize}}); {4, mStorageBuffers[1], 0, kBindingSize}});
// Extra uniform buffer for inheriting test // Extra uniform buffer for inheriting test
mUniformBuffers[2] = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, mUniformBuffers[2] = utils::CreateBufferFromData(device, uniformData.data(),
sizeof(uint32_t) * uniformData.size(),
wgpu::BufferUsage::Uniform); wgpu::BufferUsage::Uniform);
// Bind group layout for inheriting test // Bind group layout for inheriting test
@ -86,6 +90,7 @@ class DynamicBufferOffsetTests : public DawnTest {
} }
// Create objects to use as resources inside test bind groups. // Create objects to use as resources inside test bind groups.
uint32_t mMinUniformBufferOffsetAlignment;
wgpu::BindGroup mBindGroups[2]; wgpu::BindGroup mBindGroups[2];
wgpu::BindGroupLayout mBindGroupLayouts[2]; wgpu::BindGroupLayout mBindGroupLayouts[2];
wgpu::Buffer mUniformBuffers[3]; wgpu::Buffer mUniformBuffers[3];
@ -227,8 +232,8 @@ TEST_P(DynamicBufferOffsetTests, SetDynamicOffsetsRenderPipeline) {
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
std::array<uint32_t, 2> offsets = {kMinUniformBufferOffsetAlignment, std::array<uint32_t, 2> offsets = {mMinUniformBufferOffsetAlignment,
kMinUniformBufferOffsetAlignment}; mMinUniformBufferOffsetAlignment};
wgpu::RenderPassEncoder renderPassEncoder = wgpu::RenderPassEncoder renderPassEncoder =
commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
renderPassEncoder.SetPipeline(pipeline); renderPassEncoder.SetPipeline(pipeline);
@ -241,7 +246,7 @@ TEST_P(DynamicBufferOffsetTests, SetDynamicOffsetsRenderPipeline) {
std::vector<uint32_t> expectedData = {6, 8}; std::vector<uint32_t> expectedData = {6, 8};
EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0);
EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1],
kMinUniformBufferOffsetAlignment, expectedData.size()); mMinUniformBufferOffsetAlignment, expectedData.size());
} }
// Dynamic offsets are all zero and no effect to result. // Dynamic offsets are all zero and no effect to result.
@ -267,8 +272,8 @@ TEST_P(DynamicBufferOffsetTests, BasicComputePipeline) {
TEST_P(DynamicBufferOffsetTests, SetDynamicOffsetsComputePipeline) { TEST_P(DynamicBufferOffsetTests, SetDynamicOffsetsComputePipeline) {
wgpu::ComputePipeline pipeline = CreateComputePipeline(); wgpu::ComputePipeline pipeline = CreateComputePipeline();
std::array<uint32_t, 2> offsets = {kMinUniformBufferOffsetAlignment, std::array<uint32_t, 2> offsets = {mMinUniformBufferOffsetAlignment,
kMinUniformBufferOffsetAlignment}; mMinUniformBufferOffsetAlignment};
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
@ -281,7 +286,7 @@ TEST_P(DynamicBufferOffsetTests, SetDynamicOffsetsComputePipeline) {
std::vector<uint32_t> expectedData = {6, 8}; std::vector<uint32_t> expectedData = {6, 8};
EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1],
kMinUniformBufferOffsetAlignment, expectedData.size()); mMinUniformBufferOffsetAlignment, expectedData.size());
} }
// Test inherit dynamic offsets on render pipeline // Test inherit dynamic offsets on render pipeline
@ -293,8 +298,8 @@ TEST_P(DynamicBufferOffsetTests, InheritDynamicOffsetsRenderPipeline) {
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
std::array<uint32_t, 2> offsets = {kMinUniformBufferOffsetAlignment, std::array<uint32_t, 2> offsets = {mMinUniformBufferOffsetAlignment,
kMinUniformBufferOffsetAlignment}; mMinUniformBufferOffsetAlignment};
wgpu::RenderPassEncoder renderPassEncoder = wgpu::RenderPassEncoder renderPassEncoder =
commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
renderPassEncoder.SetPipeline(pipeline); renderPassEncoder.SetPipeline(pipeline);
@ -310,7 +315,7 @@ TEST_P(DynamicBufferOffsetTests, InheritDynamicOffsetsRenderPipeline) {
std::vector<uint32_t> expectedData = {12, 16}; std::vector<uint32_t> expectedData = {12, 16};
EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0); EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0);
EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1],
kMinUniformBufferOffsetAlignment, expectedData.size()); mMinUniformBufferOffsetAlignment, expectedData.size());
} }
// Test inherit dynamic offsets on compute pipeline // Test inherit dynamic offsets on compute pipeline
@ -322,8 +327,8 @@ TEST_P(DynamicBufferOffsetTests, InheritDynamicOffsetsComputePipeline) {
wgpu::ComputePipeline pipeline = CreateComputePipeline(); wgpu::ComputePipeline pipeline = CreateComputePipeline();
wgpu::ComputePipeline testPipeline = CreateComputePipeline(true); wgpu::ComputePipeline testPipeline = CreateComputePipeline(true);
std::array<uint32_t, 2> offsets = {kMinUniformBufferOffsetAlignment, std::array<uint32_t, 2> offsets = {mMinUniformBufferOffsetAlignment,
kMinUniformBufferOffsetAlignment}; mMinUniformBufferOffsetAlignment};
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
@ -339,7 +344,7 @@ TEST_P(DynamicBufferOffsetTests, InheritDynamicOffsetsComputePipeline) {
std::vector<uint32_t> expectedData = {12, 16}; std::vector<uint32_t> expectedData = {12, 16};
EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1],
kMinUniformBufferOffsetAlignment, expectedData.size()); mMinUniformBufferOffsetAlignment, expectedData.size());
} }
// Setting multiple dynamic offsets for the same bindgroup in one render pass. // Setting multiple dynamic offsets for the same bindgroup in one render pass.
@ -350,8 +355,8 @@ TEST_P(DynamicBufferOffsetTests, UpdateDynamicOffsetsMultipleTimesRenderPipeline
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
std::array<uint32_t, 2> offsets = {kMinUniformBufferOffsetAlignment, std::array<uint32_t, 2> offsets = {mMinUniformBufferOffsetAlignment,
kMinUniformBufferOffsetAlignment}; mMinUniformBufferOffsetAlignment};
std::array<uint32_t, 2> testOffsets = {0, 0}; std::array<uint32_t, 2> testOffsets = {0, 0};
wgpu::RenderPassEncoder renderPassEncoder = wgpu::RenderPassEncoder renderPassEncoder =
@ -374,8 +379,8 @@ TEST_P(DynamicBufferOffsetTests, UpdateDynamicOffsetsMultipleTimesRenderPipeline
TEST_P(DynamicBufferOffsetTests, UpdateDynamicOffsetsMultipleTimesComputePipeline) { TEST_P(DynamicBufferOffsetTests, UpdateDynamicOffsetsMultipleTimesComputePipeline) {
wgpu::ComputePipeline pipeline = CreateComputePipeline(); wgpu::ComputePipeline pipeline = CreateComputePipeline();
std::array<uint32_t, 2> offsets = {kMinUniformBufferOffsetAlignment, std::array<uint32_t, 2> offsets = {mMinUniformBufferOffsetAlignment,
kMinUniformBufferOffsetAlignment}; mMinUniformBufferOffsetAlignment};
std::array<uint32_t, 2> testOffsets = {0, 0}; std::array<uint32_t, 2> testOffsets = {0, 0};
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();

View File

@ -115,6 +115,7 @@ TEST_P(SwapChainValidationTests, CreationSuccess) {
// Checks that the creation size must be a valid 2D texture size. // Checks that the creation size must be a valid 2D texture size.
TEST_P(SwapChainValidationTests, InvalidCreationSize) { TEST_P(SwapChainValidationTests, InvalidCreationSize) {
wgpu::Limits supportedLimits = GetSupportedLimits().limits;
// A width of 0 is invalid. // A width of 0 is invalid.
{ {
wgpu::SwapChainDescriptor desc = goodDescriptor; wgpu::SwapChainDescriptor desc = goodDescriptor;
@ -128,23 +129,23 @@ TEST_P(SwapChainValidationTests, InvalidCreationSize) {
ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc));
} }
// A width of kMaxTextureDimension2D is valid but kMaxTextureDimension2D + 1 isn't. // A width of maxTextureDimension2D is valid but maxTextureDimension2D + 1 isn't.
{ {
wgpu::SwapChainDescriptor desc = goodDescriptor; wgpu::SwapChainDescriptor desc = goodDescriptor;
desc.width = kMaxTextureDimension2D; desc.width = supportedLimits.maxTextureDimension2D;
device.CreateSwapChain(surface, &desc); device.CreateSwapChain(surface, &desc);
desc.width = kMaxTextureDimension2D + 1; desc.width = supportedLimits.maxTextureDimension2D + 1;
ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc));
} }
// A height of kMaxTextureDimension2D is valid but kMaxTextureDimension2D + 1 isn't. // A height of maxTextureDimension2D is valid but maxTextureDimension2D + 1 isn't.
{ {
wgpu::SwapChainDescriptor desc = goodDescriptor; wgpu::SwapChainDescriptor desc = goodDescriptor;
desc.height = kMaxTextureDimension2D; desc.height = supportedLimits.maxTextureDimension2D;
device.CreateSwapChain(surface, &desc); device.CreateSwapChain(surface, &desc);
desc.height = kMaxTextureDimension2D + 1; desc.height = supportedLimits.maxTextureDimension2D + 1;
ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc));
} }
} }

View File

@ -270,7 +270,8 @@ void DrawCallPerf::SetUp() {
DawnPerfTestWithParams::SetUp(); DawnPerfTestWithParams::SetUp();
// Compute aligned uniform / vertex data sizes. // Compute aligned uniform / vertex data sizes.
mAlignedUniformSize = Align(kUniformSize, kMinUniformBufferOffsetAlignment); mAlignedUniformSize =
Align(kUniformSize, GetSupportedLimits().limits.minUniformBufferOffsetAlignment);
mAlignedVertexDataSize = Align(sizeof(kVertexData), 4); mAlignedVertexDataSize = Align(sizeof(kVertexData), 4);
// Initialize uniform buffer data. // Initialize uniform buffer data.

View File

@ -708,8 +708,10 @@ TEST_F(BindGroupValidationTest, BufferBindingOOB) {
// Tests constraints to be sure the uniform buffer binding isn't too large // Tests constraints to be sure the uniform buffer binding isn't too large
TEST_F(BindGroupValidationTest, MaxUniformBufferBindingSize) { TEST_F(BindGroupValidationTest, MaxUniformBufferBindingSize) {
wgpu::Limits supportedLimits = GetSupportedLimits().limits;
wgpu::BufferDescriptor descriptor; wgpu::BufferDescriptor descriptor;
descriptor.size = 2 * kMaxUniformBufferBindingSize; descriptor.size = 2 * supportedLimits.maxUniformBufferBindingSize;
descriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage; descriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage;
wgpu::Buffer buffer = device.CreateBuffer(&descriptor); wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
@ -717,7 +719,8 @@ TEST_F(BindGroupValidationTest, MaxUniformBufferBindingSize) {
device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform}}); device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform}});
// Success case, this is exactly the limit // Success case, this is exactly the limit
utils::MakeBindGroup(device, uniformLayout, {{0, buffer, 0, kMaxUniformBufferBindingSize}}); utils::MakeBindGroup(device, uniformLayout,
{{0, buffer, 0, supportedLimits.maxUniformBufferBindingSize}});
wgpu::BindGroupLayout doubleUniformLayout = utils::MakeBindGroupLayout( wgpu::BindGroupLayout doubleUniformLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform}, device, {{0, wgpu::ShaderStage::Vertex, wgpu::BufferBindingType::Uniform},
@ -725,12 +728,13 @@ TEST_F(BindGroupValidationTest, MaxUniformBufferBindingSize) {
// Success case, individual bindings don't exceed the limit // Success case, individual bindings don't exceed the limit
utils::MakeBindGroup(device, doubleUniformLayout, utils::MakeBindGroup(device, doubleUniformLayout,
{{0, buffer, 0, kMaxUniformBufferBindingSize}, {{0, buffer, 0, supportedLimits.maxUniformBufferBindingSize},
{1, buffer, kMaxUniformBufferBindingSize, kMaxUniformBufferBindingSize}}); {1, buffer, supportedLimits.maxUniformBufferBindingSize,
supportedLimits.maxUniformBufferBindingSize}});
// Error case, this is above the limit // Error case, this is above the limit
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, uniformLayout, ASSERT_DEVICE_ERROR(utils::MakeBindGroup(
{{0, buffer, 0, kMaxUniformBufferBindingSize + 1}})); device, uniformLayout, {{0, buffer, 0, supportedLimits.maxUniformBufferBindingSize + 1}}));
// Making sure the constraint doesn't apply to storage buffers // Making sure the constraint doesn't apply to storage buffers
wgpu::BindGroupLayout readonlyStorageLayout = utils::MakeBindGroupLayout( wgpu::BindGroupLayout readonlyStorageLayout = utils::MakeBindGroupLayout(
@ -740,14 +744,17 @@ TEST_F(BindGroupValidationTest, MaxUniformBufferBindingSize) {
// Success case, storage buffer can still be created. // Success case, storage buffer can still be created.
utils::MakeBindGroup(device, readonlyStorageLayout, utils::MakeBindGroup(device, readonlyStorageLayout,
{{0, buffer, 0, 2 * kMaxUniformBufferBindingSize}}); {{0, buffer, 0, 2 * supportedLimits.maxUniformBufferBindingSize}});
utils::MakeBindGroup(device, storageLayout, {{0, buffer, 0, 2 * kMaxUniformBufferBindingSize}}); utils::MakeBindGroup(device, storageLayout,
{{0, buffer, 0, 2 * supportedLimits.maxUniformBufferBindingSize}});
} }
// Tests constraints to be sure the storage buffer binding isn't too large // Tests constraints to be sure the storage buffer binding isn't too large
TEST_F(BindGroupValidationTest, MaxStorageBufferBindingSize) { TEST_F(BindGroupValidationTest, MaxStorageBufferBindingSize) {
wgpu::Limits supportedLimits = GetSupportedLimits().limits;
wgpu::BufferDescriptor descriptor; wgpu::BufferDescriptor descriptor;
descriptor.size = 2 * kMaxStorageBufferBindingSize; descriptor.size = 2 * supportedLimits.maxStorageBufferBindingSize;
descriptor.usage = wgpu::BufferUsage::Storage; descriptor.usage = wgpu::BufferUsage::Storage;
wgpu::Buffer buffer = device.CreateBuffer(&descriptor); wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
@ -755,10 +762,12 @@ TEST_F(BindGroupValidationTest, MaxStorageBufferBindingSize) {
device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}}); device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}});
// Success case, this is exactly the limit // Success case, this is exactly the limit
utils::MakeBindGroup(device, uniformLayout, {{0, buffer, 0, kMaxStorageBufferBindingSize}}); utils::MakeBindGroup(device, uniformLayout,
{{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize}});
// Success case, this is one less than the limit (check it is not an alignment constraint) // Success case, this is one less than the limit (check it is not an alignment constraint)
utils::MakeBindGroup(device, uniformLayout, {{0, buffer, 0, kMaxStorageBufferBindingSize - 1}}); utils::MakeBindGroup(device, uniformLayout,
{{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize - 1}});
wgpu::BindGroupLayout doubleUniformLayout = utils::MakeBindGroupLayout( wgpu::BindGroupLayout doubleUniformLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage}, device, {{0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Storage},
@ -766,12 +775,13 @@ TEST_F(BindGroupValidationTest, MaxStorageBufferBindingSize) {
// Success case, individual bindings don't exceed the limit // Success case, individual bindings don't exceed the limit
utils::MakeBindGroup(device, doubleUniformLayout, utils::MakeBindGroup(device, doubleUniformLayout,
{{0, buffer, 0, kMaxStorageBufferBindingSize}, {{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize},
{1, buffer, kMaxStorageBufferBindingSize, kMaxStorageBufferBindingSize}}); {1, buffer, supportedLimits.maxStorageBufferBindingSize,
supportedLimits.maxStorageBufferBindingSize}});
// Error case, this is above the limit // Error case, this is above the limit
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, uniformLayout, ASSERT_DEVICE_ERROR(utils::MakeBindGroup(
{{0, buffer, 0, kMaxStorageBufferBindingSize + 1}})); device, uniformLayout, {{0, buffer, 0, supportedLimits.maxStorageBufferBindingSize + 1}}));
} }
// Test what happens when the layout is an error. // Test what happens when the layout is an error.
@ -1306,7 +1316,6 @@ TEST_F(BindGroupLayoutValidationTest, MultisampledTextureSampleType) {
}); });
} }
constexpr uint64_t kBufferSize = 3 * kMinUniformBufferOffsetAlignment + 8;
constexpr uint32_t kBindingSize = 9; constexpr uint32_t kBindingSize = 9;
class SetBindGroupValidationTest : public ValidationTest { class SetBindGroupValidationTest : public ValidationTest {
@ -1323,6 +1332,9 @@ class SetBindGroupValidationTest : public ValidationTest {
wgpu::BufferBindingType::Storage, true}, wgpu::BufferBindingType::Storage, true},
{3, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, {3, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BufferBindingType::ReadOnlyStorage, true}}); wgpu::BufferBindingType::ReadOnlyStorage, true}});
mMinUniformBufferOffsetAlignment =
GetSupportedLimits().limits.minUniformBufferOffsetAlignment;
mBufferSize = 3 * mMinUniformBufferOffsetAlignment + 8;
} }
wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) { wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) {
@ -1431,14 +1443,18 @@ class SetBindGroupValidationTest : public ValidationTest {
commandEncoder.Finish(); commandEncoder.Finish();
} }
} }
protected:
uint32_t mMinUniformBufferOffsetAlignment;
uint64_t mBufferSize;
}; };
// This is the test case that should work. // This is the test case that should work.
TEST_F(SetBindGroupValidationTest, Basic) { TEST_F(SetBindGroupValidationTest, Basic) {
// Set up the bind group. // Set up the bind group.
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1461,9 +1477,9 @@ TEST_F(SetBindGroupValidationTest, MissingBindGroup) {
// Setting bind group after a draw / dispatch should re-verify the layout is compatible // Setting bind group after a draw / dispatch should re-verify the layout is compatible
TEST_F(SetBindGroupValidationTest, VerifyGroupIfChangedAfterAction) { TEST_F(SetBindGroupValidationTest, VerifyGroupIfChangedAfterAction) {
// Set up the bind group // Set up the bind group
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1510,9 +1526,9 @@ TEST_F(SetBindGroupValidationTest, VerifyGroupIfChangedAfterAction) {
// Test cases that test dynamic offsets count mismatch with bind group layout. // Test cases that test dynamic offsets count mismatch with bind group layout.
TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) { TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) {
// Set up bind group. // Set up bind group.
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1534,9 +1550,9 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) {
// Test cases that test dynamic offsets not aligned // Test cases that test dynamic offsets not aligned
TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) { TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) {
// Set up bind group. // Set up bind group.
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1554,9 +1570,9 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) {
// Test cases that test dynamic uniform buffer out of bound situation. // Test cases that test dynamic uniform buffer out of bound situation.
TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicUniformBuffer) { TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicUniformBuffer) {
// Set up bind group. // Set up bind group.
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1574,9 +1590,9 @@ TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicUniformBuffer) {
// Test cases that test dynamic storage buffer out of bound situation. // Test cases that test dynamic storage buffer out of bound situation.
TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicStorageBuffer) { TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicStorageBuffer) {
// Set up bind group. // Set up bind group.
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1594,9 +1610,9 @@ TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicStorageBuffer) {
// Test cases that test dynamic uniform buffer out of bound situation because of binding size. // Test cases that test dynamic uniform buffer out of bound situation because of binding size.
TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicUniformBuffer) { TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicUniformBuffer) {
// Set up bind group, but binding size is larger than // Set up bind group, but binding size is larger than
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1614,9 +1630,9 @@ TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicUniformBuffer) {
// Test cases that test dynamic storage buffer out of bound situation because of binding size. // Test cases that test dynamic storage buffer out of bound situation because of binding size.
TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicStorageBuffer) { TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicStorageBuffer) {
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer readonlyStorageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout,
{{0, uniformBuffer, 0, kBindingSize}, {{0, uniformBuffer, 0, kBindingSize},
{1, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize},
@ -1650,11 +1666,11 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
// end of the buffer. Any mismatch applying too-large of an offset to a smaller buffer will hit // end of the buffer. Any mismatch applying too-large of an offset to a smaller buffer will hit
// the out-of-bounds condition during validation. // the out-of-bounds condition during validation.
wgpu::Buffer buffer3x = wgpu::Buffer buffer3x =
CreateBuffer(3 * kMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage); CreateBuffer(3 * mMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage);
wgpu::Buffer buffer2x = wgpu::Buffer buffer2x =
CreateBuffer(2 * kMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage); CreateBuffer(2 * mMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage);
wgpu::Buffer buffer1x = wgpu::Buffer buffer1x =
CreateBuffer(1 * kMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Uniform); CreateBuffer(1 * mMinUniformBufferOffsetAlignment + 4, wgpu::BufferUsage::Uniform);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl,
{ {
{0, buffer3x, 0, 4}, {0, buffer3x, 0, 4},
@ -1678,7 +1694,7 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
// Offset the first binding to touch the end of the buffer. Should succeed. // Offset the first binding to touch the end of the buffer. Should succeed.
// Will fail if the offset is applied to the first or second bindings since their buffers // Will fail if the offset is applied to the first or second bindings since their buffers
// are too small. // are too small.
offsets = {/* binding 0 */ 3 * kMinUniformBufferOffsetAlignment, offsets = {/* binding 0 */ 3 * mMinUniformBufferOffsetAlignment,
/* binding 2 */ 0, /* binding 2 */ 0,
/* binding 3 */ 0}; /* binding 3 */ 0};
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
@ -1690,7 +1706,7 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
{ {
// Offset the second binding to touch the end of the buffer. Should succeed. // Offset the second binding to touch the end of the buffer. Should succeed.
offsets = {/* binding 0 */ 0, offsets = {/* binding 0 */ 0,
/* binding 2 */ 1 * kMinUniformBufferOffsetAlignment, /* binding 2 */ 1 * mMinUniformBufferOffsetAlignment,
/* binding 3 */ 0}; /* binding 3 */ 0};
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
@ -1704,7 +1720,7 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
// is too small. // is too small.
offsets = {/* binding 0 */ 0, offsets = {/* binding 0 */ 0,
/* binding 2 */ 0, /* binding 2 */ 0,
/* binding 3 */ 2 * kMinUniformBufferOffsetAlignment}; /* binding 3 */ 2 * mMinUniformBufferOffsetAlignment};
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
@ -1713,9 +1729,9 @@ TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) {
} }
{ {
// Offset each binding to touch the end of their buffer. Should succeed. // Offset each binding to touch the end of their buffer. Should succeed.
offsets = {/* binding 0 */ 3 * kMinUniformBufferOffsetAlignment, offsets = {/* binding 0 */ 3 * mMinUniformBufferOffsetAlignment,
/* binding 2 */ 1 * kMinUniformBufferOffsetAlignment, /* binding 2 */ 1 * mMinUniformBufferOffsetAlignment,
/* binding 3 */ 2 * kMinUniformBufferOffsetAlignment}; /* binding 3 */ 2 * mMinUniformBufferOffsetAlignment};
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data());
@ -1745,6 +1761,8 @@ class SetBindGroupPersistenceValidationTest : public ValidationTest {
[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> { [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
return vec4<f32>(); return vec4<f32>();
})"); })");
mBufferSize = 3 * GetSupportedLimits().limits.minUniformBufferOffsetAlignment + 8;
} }
wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) { wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) {
@ -1824,6 +1842,9 @@ class SetBindGroupPersistenceValidationTest : public ValidationTest {
return std::make_tuple(bindGroupLayouts, pipeline); return std::make_tuple(bindGroupLayouts, pipeline);
} }
protected:
uint32_t mBufferSize;
private: private:
wgpu::ShaderModule mVsModule; wgpu::ShaderModule mVsModule;
}; };
@ -1843,8 +1864,8 @@ TEST_F(SetBindGroupPersistenceValidationTest, BindGroupBeforePipeline) {
}}, }},
}}); }});
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroup0 = utils::MakeBindGroup( wgpu::BindGroup bindGroup0 = utils::MakeBindGroup(
device, bindGroupLayouts[0], device, bindGroupLayouts[0],
@ -1897,8 +1918,8 @@ TEST_F(SetBindGroupPersistenceValidationTest, NotVulkanInheritance) {
}}, }},
}}); }});
wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); wgpu::Buffer uniformBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); wgpu::Buffer storageBuffer = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
wgpu::BindGroup bindGroupA0 = utils::MakeBindGroup( wgpu::BindGroup bindGroupA0 = utils::MakeBindGroup(
device, bindGroupLayoutsA[0], device, bindGroupLayoutsA[0],
@ -2152,6 +2173,11 @@ TEST_F(BindGroupLayoutCompatibilityTest, ExternalTextureBindGroupLayoutCompatibi
class BindingsValidationTest : public BindGroupLayoutCompatibilityTest { class BindingsValidationTest : public BindGroupLayoutCompatibilityTest {
public: public:
void SetUp() override {
BindGroupLayoutCompatibilityTest::SetUp();
mBufferSize = 3 * GetSupportedLimits().limits.minUniformBufferOffsetAlignment + 8;
}
void TestRenderPassBindings(const wgpu::BindGroup* bg, void TestRenderPassBindings(const wgpu::BindGroup* bg,
uint32_t count, uint32_t count,
wgpu::RenderPipeline pipeline, wgpu::RenderPipeline pipeline,
@ -2191,6 +2217,7 @@ class BindingsValidationTest : public BindGroupLayoutCompatibilityTest {
} }
} }
uint32_t mBufferSize;
static constexpr uint32_t kBindingNum = 3; static constexpr uint32_t kBindingNum = 3;
}; };
@ -2263,7 +2290,7 @@ TEST_F(BindingsValidationTest, BindGroupsWithMoreBindingsThanPipelineLayout) {
bgl[i] = utils::MakeBindGroupLayout( bgl[i] = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BufferBindingType::Storage}}); wgpu::BufferBindingType::Storage}});
buffer[i] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); buffer[i] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}}); bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}});
} }
@ -2284,7 +2311,7 @@ TEST_F(BindingsValidationTest, BindGroupsWithMoreBindingsThanPipelineLayout) {
wgpu::BufferBindingType::ReadOnlyStorage}, wgpu::BufferBindingType::ReadOnlyStorage},
{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BufferBindingType::Uniform}}); wgpu::BufferBindingType::Uniform}});
buffer[1] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform); buffer[1] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform);
bg[1] = utils::MakeBindGroup(device, bgl[1], {{0, buffer[1]}, {1, buffer[1]}}); bg[1] = utils::MakeBindGroup(device, bgl[1], {{0, buffer[1]}, {1, buffer[1]}});
TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false); TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false);
@ -2304,7 +2331,7 @@ TEST_F(BindingsValidationTest, BindGroupsWithLessBindingsThanPipelineLayout) {
bgl[i] = utils::MakeBindGroupLayout( bgl[i] = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BufferBindingType::Storage}}); wgpu::BufferBindingType::Storage}});
buffer[i] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); buffer[i] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Storage);
bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}}); bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}});
} }
@ -2329,7 +2356,7 @@ TEST_F(BindingsValidationTest, BindGroupsWithLessBindingsThanPipelineLayout) {
bgl[2] = utils::MakeBindGroupLayout( bgl[2] = utils::MakeBindGroupLayout(
device, {{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, device, {{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
wgpu::BufferBindingType::Uniform}}); wgpu::BufferBindingType::Uniform}});
buffer[2] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); buffer[2] = CreateBuffer(mBufferSize, wgpu::BufferUsage::Uniform);
bg[2] = utils::MakeBindGroup(device, bgl[2], {{1, buffer[2]}}); bg[2] = utils::MakeBindGroup(device, bgl[2], {{1, buffer[2]}});
TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false); TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false);

View File

@ -57,27 +57,30 @@ TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_SmallestValid) {
// Check that the largest allowed dispatch is OK. // Check that the largest allowed dispatch is OK.
TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_LargestValid) { TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_LargestValid) {
constexpr uint32_t kMax = kMaxComputePerDimensionDispatchSize; const uint32_t max = GetSupportedLimits().limits.maxComputeWorkgroupsPerDimension;
TestDispatch(kMax, kMax, kMax); TestDispatch(max, max, max);
} }
// Check that exceeding the maximum on the X dimension results in validation failure. // Check that exceeding the maximum on the X dimension results in validation failure.
TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidX) { TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidX) {
ASSERT_DEVICE_ERROR(TestDispatch(kMaxComputePerDimensionDispatchSize + 1, 1, 1)); const uint32_t max = GetSupportedLimits().limits.maxComputeWorkgroupsPerDimension;
ASSERT_DEVICE_ERROR(TestDispatch(max + 1, 1, 1));
} }
// Check that exceeding the maximum on the Y dimension results in validation failure. // Check that exceeding the maximum on the Y dimension results in validation failure.
TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidY) { TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidY) {
ASSERT_DEVICE_ERROR(TestDispatch(1, kMaxComputePerDimensionDispatchSize + 1, 1)); const uint32_t max = GetSupportedLimits().limits.maxComputeWorkgroupsPerDimension;
ASSERT_DEVICE_ERROR(TestDispatch(1, max + 1, 1));
} }
// Check that exceeding the maximum on the Z dimension results in validation failure. // Check that exceeding the maximum on the Z dimension results in validation failure.
TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidZ) { TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidZ) {
ASSERT_DEVICE_ERROR(TestDispatch(1, 1, kMaxComputePerDimensionDispatchSize + 1)); const uint32_t max = GetSupportedLimits().limits.maxComputeWorkgroupsPerDimension;
ASSERT_DEVICE_ERROR(TestDispatch(1, 1, max + 1));
} }
// Check that exceeding the maximum on all dimensions results in validation failure. // Check that exceeding the maximum on all dimensions results in validation failure.
TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidAll) { TEST_F(ComputeValidationTest, PerDimensionDispatchSizeLimits_InvalidAll) {
constexpr uint32_t kMax = kMaxComputePerDimensionDispatchSize; const uint32_t max = GetSupportedLimits().limits.maxComputeWorkgroupsPerDimension;
ASSERT_DEVICE_ERROR(TestDispatch(kMax + 1, kMax + 1, kMax + 1)); ASSERT_DEVICE_ERROR(TestDispatch(max + 1, max + 1, max + 1));
} }

View File

@ -444,29 +444,37 @@ TEST_F(ShaderModuleValidationTest, ComputeWorkgroupSizeLimits) {
utils::CreateShaderModule(device, ss.str().c_str()); utils::CreateShaderModule(device, ss.str().c_str());
}; };
MakeShaderWithWorkgroupSize(1, 1, 1); wgpu::Limits supportedLimits = GetSupportedLimits().limits;
MakeShaderWithWorkgroupSize(kMaxComputeWorkgroupSizeX, 1, 1);
MakeShaderWithWorkgroupSize(1, kMaxComputeWorkgroupSizeY, 1);
MakeShaderWithWorkgroupSize(1, 1, kMaxComputeWorkgroupSizeZ);
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupSize(kMaxComputeWorkgroupSizeX + 1, 1, 1)); MakeShaderWithWorkgroupSize(1, 1, 1);
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupSize(1, kMaxComputeWorkgroupSizeY + 1, 1)); MakeShaderWithWorkgroupSize(supportedLimits.maxComputeWorkgroupSizeX, 1, 1);
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupSize(1, 1, kMaxComputeWorkgroupSizeZ + 1)); MakeShaderWithWorkgroupSize(1, supportedLimits.maxComputeWorkgroupSizeY, 1);
MakeShaderWithWorkgroupSize(1, 1, supportedLimits.maxComputeWorkgroupSizeZ);
ASSERT_DEVICE_ERROR(
MakeShaderWithWorkgroupSize(supportedLimits.maxComputeWorkgroupSizeX + 1, 1, 1));
ASSERT_DEVICE_ERROR(
MakeShaderWithWorkgroupSize(1, supportedLimits.maxComputeWorkgroupSizeY + 1, 1));
ASSERT_DEVICE_ERROR(
MakeShaderWithWorkgroupSize(1, 1, supportedLimits.maxComputeWorkgroupSizeZ + 1));
// No individual dimension exceeds its limit, but the combined size should definitely exceed the // No individual dimension exceeds its limit, but the combined size should definitely exceed the
// total invocation limit. // total invocation limit.
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupSize( ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupSize(supportedLimits.maxComputeWorkgroupSizeX,
kMaxComputeWorkgroupSizeX, kMaxComputeWorkgroupSizeY, kMaxComputeWorkgroupSizeZ)); supportedLimits.maxComputeWorkgroupSizeY,
supportedLimits.maxComputeWorkgroupSizeZ));
} }
// Tests that we validate workgroup storage size limits. // Tests that we validate workgroup storage size limits.
TEST_F(ShaderModuleValidationTest, ComputeWorkgroupStorageSizeLimits) { TEST_F(ShaderModuleValidationTest, ComputeWorkgroupStorageSizeLimits) {
DAWN_SKIP_TEST_IF(!HasToggleEnabled("use_tint_generator")); DAWN_SKIP_TEST_IF(!HasToggleEnabled("use_tint_generator"));
wgpu::Limits supportedLimits = GetSupportedLimits().limits;
constexpr uint32_t kVec4Size = 16; constexpr uint32_t kVec4Size = 16;
constexpr uint32_t kMaxVec4Count = kMaxComputeWorkgroupStorageSize / kVec4Size; const uint32_t maxVec4Count = supportedLimits.maxComputeWorkgroupStorageSize / kVec4Size;
constexpr uint32_t kMat4Size = 64; constexpr uint32_t kMat4Size = 64;
constexpr uint32_t kMaxMat4Count = kMaxComputeWorkgroupStorageSize / kMat4Size; const uint32_t maxMat4Count = supportedLimits.maxComputeWorkgroupStorageSize / kMat4Size;
auto MakeShaderWithWorkgroupStorage = [this](uint32_t vec4_count, uint32_t mat4_count) { auto MakeShaderWithWorkgroupStorage = [this](uint32_t vec4_count, uint32_t mat4_count) {
std::ostringstream ss; std::ostringstream ss;
@ -484,14 +492,14 @@ TEST_F(ShaderModuleValidationTest, ComputeWorkgroupStorageSizeLimits) {
}; };
MakeShaderWithWorkgroupStorage(1, 1); MakeShaderWithWorkgroupStorage(1, 1);
MakeShaderWithWorkgroupStorage(kMaxVec4Count, 0); MakeShaderWithWorkgroupStorage(maxVec4Count, 0);
MakeShaderWithWorkgroupStorage(0, kMaxMat4Count); MakeShaderWithWorkgroupStorage(0, maxMat4Count);
MakeShaderWithWorkgroupStorage(kMaxVec4Count - 4, 1); MakeShaderWithWorkgroupStorage(maxVec4Count - 4, 1);
MakeShaderWithWorkgroupStorage(4, kMaxMat4Count - 1); MakeShaderWithWorkgroupStorage(4, maxMat4Count - 1);
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(kMaxVec4Count + 1, 0)); ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(maxVec4Count + 1, 0));
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(kMaxVec4Count - 3, 1)); ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(maxVec4Count - 3, 1));
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(0, kMaxMat4Count + 1)); ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(0, maxMat4Count + 1));
ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(4, kMaxMat4Count)); ASSERT_DEVICE_ERROR(MakeShaderWithWorkgroupStorage(4, maxMat4Count));
} }
// Test that numeric ID must be unique // Test that numeric ID must be unique

View File

@ -15,6 +15,7 @@
#include "tests/unittests/validation/ValidationTest.h" #include "tests/unittests/validation/ValidationTest.h"
#include "common/Constants.h" #include "common/Constants.h"
#include "common/Math.h"
#include "utils/ComboRenderPipelineDescriptor.h" #include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/TextureUtils.h" #include "utils/TextureUtils.h"
#include "utils/WGPUHelpers.h" #include "utils/WGPUHelpers.h"
@ -264,12 +265,24 @@ namespace {
device.CreateTexture(&descriptor); device.CreateTexture(&descriptor);
} }
// Mip level exceeding kMaxTexture2DMipLevels not allowed // Mip level equal to the maximum for a 2D texture is allowed
{ {
uint32_t maxTextureDimension2D = GetSupportedLimits().limits.maxTextureDimension2D;
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = 1 >> kMaxTexture2DMipLevels; descriptor.size.width = maxTextureDimension2D;
descriptor.size.height = 1 >> kMaxTexture2DMipLevels; descriptor.size.height = maxTextureDimension2D;
descriptor.mipLevelCount = kMaxTexture2DMipLevels + 1u; descriptor.mipLevelCount = Log2(maxTextureDimension2D) + 1u;
device.CreateTexture(&descriptor);
}
// Mip level exceeding the maximum for a 2D texture not allowed
{
uint32_t maxTextureDimension2D = GetSupportedLimits().limits.maxTextureDimension2D;
wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = maxTextureDimension2D;
descriptor.size.height = maxTextureDimension2D;
descriptor.mipLevelCount = Log2(maxTextureDimension2D) + 2u;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
} }
@ -278,26 +291,27 @@ namespace {
// Test the validation of array layer count // Test the validation of array layer count
TEST_F(TextureValidationTest, ArrayLayerCount) { TEST_F(TextureValidationTest, ArrayLayerCount) {
wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor();
wgpu::Limits supportedLimits = GetSupportedLimits().limits;
// Array layer count exceeding kMaxTextureArrayLayers is not allowed for 2D texture // Array layer count exceeding maxTextureArrayLayers is not allowed for 2D texture
{ {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.depthOrArrayLayers = kMaxTextureArrayLayers + 1u; descriptor.size.depthOrArrayLayers = supportedLimits.maxTextureArrayLayers + 1u;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
} }
// Array layer count less than kMaxTextureArrayLayers is allowed // Array layer count less than maxTextureArrayLayers is allowed
{ {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.depthOrArrayLayers = kMaxTextureArrayLayers >> 1; descriptor.size.depthOrArrayLayers = supportedLimits.maxTextureArrayLayers >> 1;
device.CreateTexture(&descriptor); device.CreateTexture(&descriptor);
} }
// Array layer count equal to kMaxTextureArrayLayers is allowed // Array layer count equal to maxTextureArrayLayers is allowed
{ {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.depthOrArrayLayers = kMaxTextureArrayLayers; descriptor.size.depthOrArrayLayers = supportedLimits.maxTextureArrayLayers;
device.CreateTexture(&descriptor); device.CreateTexture(&descriptor);
} }
} }
@ -305,15 +319,16 @@ namespace {
// Test the validation of 2D texture size // Test the validation of 2D texture size
TEST_F(TextureValidationTest, 2DTextureSize) { TEST_F(TextureValidationTest, 2DTextureSize) {
wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor();
wgpu::Limits supportedLimits = GetSupportedLimits().limits;
// Out-of-bound texture dimension is not allowed // Out-of-bound texture dimension is not allowed
{ {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = kMaxTextureDimension2D + 1u; descriptor.size.width = supportedLimits.maxTextureDimension2D + 1u;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size.width = 1; descriptor.size.width = 1;
descriptor.size.height = kMaxTextureDimension2D + 1u; descriptor.size.height = supportedLimits.maxTextureDimension2D + 1u;
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
} }
@ -334,16 +349,16 @@ namespace {
// Texture size less than max dimension is allowed // Texture size less than max dimension is allowed
{ {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = kMaxTextureDimension2D >> 1; descriptor.size.width = supportedLimits.maxTextureDimension2D >> 1;
descriptor.size.height = kMaxTextureDimension2D >> 1; descriptor.size.height = supportedLimits.maxTextureDimension2D >> 1;
device.CreateTexture(&descriptor); device.CreateTexture(&descriptor);
} }
// Texture size equal to max dimension is allowed // Texture size equal to max dimension is allowed
{ {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.size.width = kMaxTextureDimension2D; descriptor.size.width = supportedLimits.maxTextureDimension2D;
descriptor.size.height = kMaxTextureDimension2D; descriptor.size.height = supportedLimits.maxTextureDimension2D;
descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.dimension = wgpu::TextureDimension::e2D;
device.CreateTexture(&descriptor); device.CreateTexture(&descriptor);
} }
@ -352,19 +367,20 @@ namespace {
// Test the validation of 3D texture size // Test the validation of 3D texture size
TEST_F(TextureValidationTest, 3DTextureSize) { TEST_F(TextureValidationTest, 3DTextureSize) {
wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor();
wgpu::Limits supportedLimits = GetSupportedLimits().limits;
// Out-of-bound texture dimension is not allowed // Out-of-bound texture dimension is not allowed
{ {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.dimension = wgpu::TextureDimension::e3D; descriptor.dimension = wgpu::TextureDimension::e3D;
descriptor.size = {kMaxTextureDimension3D + 1u, 1, 1}; descriptor.size = {supportedLimits.maxTextureDimension3D + 1u, 1, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, kMaxTextureDimension3D + 1u, 1}; descriptor.size = {1, supportedLimits.maxTextureDimension3D + 1u, 1};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
descriptor.size = {1, 1, kMaxTextureDimension3D + 1u}; descriptor.size = {1, 1, supportedLimits.maxTextureDimension3D + 1u};
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
} }
@ -388,8 +404,9 @@ namespace {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.dimension = wgpu::TextureDimension::e3D; descriptor.dimension = wgpu::TextureDimension::e3D;
descriptor.size = {kMaxTextureDimension3D >> 1, kMaxTextureDimension3D >> 1, descriptor.size = {supportedLimits.maxTextureDimension3D >> 1,
kMaxTextureDimension3D >> 1}; supportedLimits.maxTextureDimension3D >> 1,
supportedLimits.maxTextureDimension3D >> 1};
device.CreateTexture(&descriptor); device.CreateTexture(&descriptor);
} }
@ -398,8 +415,9 @@ namespace {
wgpu::TextureDescriptor descriptor = defaultDescriptor; wgpu::TextureDescriptor descriptor = defaultDescriptor;
descriptor.dimension = wgpu::TextureDimension::e3D; descriptor.dimension = wgpu::TextureDimension::e3D;
descriptor.size = {kMaxTextureDimension3D, kMaxTextureDimension3D, descriptor.size = {supportedLimits.maxTextureDimension3D,
kMaxTextureDimension3D}; supportedLimits.maxTextureDimension3D,
supportedLimits.maxTextureDimension3D};
device.CreateTexture(&descriptor); device.CreateTexture(&descriptor);
} }
} }

View File

@ -179,6 +179,13 @@ bool ValidationTest::HasToggleEnabled(const char* toggle) const {
}) != toggles.end(); }) != toggles.end();
} }
wgpu::SupportedLimits ValidationTest::GetSupportedLimits() {
WGPUSupportedLimits supportedLimits;
supportedLimits.nextInChain = nullptr;
dawn_native::GetProcs().deviceGetLimits(backendDevice, &supportedLimits);
return *reinterpret_cast<wgpu::SupportedLimits*>(&supportedLimits);
}
WGPUDevice ValidationTest::CreateTestDevice() { WGPUDevice ValidationTest::CreateTestDevice() {
// Disabled disallowing unsafe APIs so we can test them. // Disabled disallowing unsafe APIs so we can test them.
dawn_native::DeviceDescriptor deviceDescriptor; dawn_native::DeviceDescriptor deviceDescriptor;

View File

@ -96,6 +96,11 @@ class ValidationTest : public testing::Test {
bool HasToggleEnabled(const char* toggle) const; bool HasToggleEnabled(const char* toggle) const;
// TODO(crbug.com/dawn/689): Use limits returned from the wire
// This is implemented here because tests need to always query
// the |backendDevice| since limits are not implemented in the wire.
wgpu::SupportedLimits GetSupportedLimits();
protected: protected:
virtual WGPUDevice CreateTestDevice(); virtual WGPUDevice CreateTestDevice();