mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-08 21:19:14 +00:00
This patch adds validation code for API side for readonly storage buffer. It also adds unit tests for API validation. BUG=dawn:180 Change-Id: I9a97c5f3aa23e720619d138ca55d7b17f08d64c9 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12620 Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Yunchao He <yunchao.he@intel.com>
364 lines
16 KiB
C++
364 lines
16 KiB
C++
// Copyright 2019 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "dawn_native/CommandValidation.h"
|
|
|
|
#include "common/BitSetIterator.h"
|
|
#include "dawn_native/BindGroup.h"
|
|
#include "dawn_native/Buffer.h"
|
|
#include "dawn_native/CommandBufferStateTracker.h"
|
|
#include "dawn_native/Commands.h"
|
|
#include "dawn_native/PassResourceUsageTracker.h"
|
|
#include "dawn_native/RenderBundle.h"
|
|
#include "dawn_native/RenderPipeline.h"
|
|
|
|
namespace dawn_native {
|
|
|
|
namespace {
|
|
|
|
void TrackBindGroupResourceUsage(BindGroupBase* group,
|
|
PassResourceUsageTracker* usageTracker) {
|
|
const auto& layoutInfo = group->GetLayout()->GetBindingInfo();
|
|
|
|
for (uint32_t i : IterateBitSet(layoutInfo.mask)) {
|
|
wgpu::BindingType type = layoutInfo.types[i];
|
|
|
|
switch (type) {
|
|
case wgpu::BindingType::UniformBuffer: {
|
|
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
|
|
usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Uniform);
|
|
} break;
|
|
|
|
case wgpu::BindingType::StorageBuffer: {
|
|
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
|
|
usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Storage);
|
|
} break;
|
|
|
|
case wgpu::BindingType::SampledTexture: {
|
|
TextureBase* texture = group->GetBindingAsTextureView(i)->GetTexture();
|
|
usageTracker->TextureUsedAs(texture, wgpu::TextureUsage::Sampled);
|
|
} break;
|
|
|
|
case wgpu::BindingType::ReadonlyStorageBuffer: {
|
|
BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
|
|
usageTracker->BufferUsedAs(buffer, kReadOnlyStorage);
|
|
} break;
|
|
|
|
case wgpu::BindingType::Sampler:
|
|
break;
|
|
|
|
case wgpu::BindingType::StorageTexture:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline MaybeError ValidateRenderBundleCommand(CommandIterator* commands,
|
|
Command type,
|
|
PassResourceUsageTracker* usageTracker,
|
|
CommandBufferStateTracker* commandBufferState,
|
|
const AttachmentState* attachmentState,
|
|
uint64_t* debugGroupStackSize,
|
|
const char* disallowedMessage) {
|
|
switch (type) {
|
|
case Command::Draw: {
|
|
commands->NextCommand<DrawCmd>();
|
|
DAWN_TRY(commandBufferState->ValidateCanDraw());
|
|
} break;
|
|
|
|
case Command::DrawIndexed: {
|
|
commands->NextCommand<DrawIndexedCmd>();
|
|
DAWN_TRY(commandBufferState->ValidateCanDrawIndexed());
|
|
} break;
|
|
|
|
case Command::DrawIndirect: {
|
|
DrawIndirectCmd* cmd = commands->NextCommand<DrawIndirectCmd>();
|
|
DAWN_TRY(commandBufferState->ValidateCanDraw());
|
|
usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
|
|
wgpu::BufferUsage::Indirect);
|
|
} break;
|
|
|
|
case Command::DrawIndexedIndirect: {
|
|
DrawIndexedIndirectCmd* cmd = commands->NextCommand<DrawIndexedIndirectCmd>();
|
|
DAWN_TRY(commandBufferState->ValidateCanDrawIndexed());
|
|
usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
|
|
wgpu::BufferUsage::Indirect);
|
|
} break;
|
|
|
|
case Command::InsertDebugMarker: {
|
|
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
|
commands->NextData<char>(cmd->length + 1);
|
|
} break;
|
|
|
|
case Command::PopDebugGroup: {
|
|
commands->NextCommand<PopDebugGroupCmd>();
|
|
DAWN_TRY(ValidateCanPopDebugGroup(*debugGroupStackSize));
|
|
*debugGroupStackSize -= 1;
|
|
} break;
|
|
|
|
case Command::PushDebugGroup: {
|
|
PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
|
|
commands->NextData<char>(cmd->length + 1);
|
|
*debugGroupStackSize += 1;
|
|
} break;
|
|
|
|
case Command::SetRenderPipeline: {
|
|
SetRenderPipelineCmd* cmd = commands->NextCommand<SetRenderPipelineCmd>();
|
|
RenderPipelineBase* pipeline = cmd->pipeline.Get();
|
|
|
|
if (DAWN_UNLIKELY(pipeline->GetAttachmentState() != attachmentState)) {
|
|
return DAWN_VALIDATION_ERROR("Pipeline attachment state is not compatible");
|
|
}
|
|
commandBufferState->SetRenderPipeline(pipeline);
|
|
} break;
|
|
|
|
case Command::SetBindGroup: {
|
|
SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
|
|
if (cmd->dynamicOffsetCount > 0) {
|
|
commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
|
|
}
|
|
|
|
TrackBindGroupResourceUsage(cmd->group.Get(), usageTracker);
|
|
commandBufferState->SetBindGroup(cmd->index, cmd->group.Get());
|
|
} break;
|
|
|
|
case Command::SetIndexBuffer: {
|
|
SetIndexBufferCmd* cmd = commands->NextCommand<SetIndexBufferCmd>();
|
|
|
|
usageTracker->BufferUsedAs(cmd->buffer.Get(), wgpu::BufferUsage::Index);
|
|
commandBufferState->SetIndexBuffer();
|
|
} break;
|
|
|
|
case Command::SetVertexBuffer: {
|
|
SetVertexBufferCmd* cmd = commands->NextCommand<SetVertexBufferCmd>();
|
|
|
|
usageTracker->BufferUsedAs(cmd->buffer.Get(), wgpu::BufferUsage::Vertex);
|
|
commandBufferState->SetVertexBuffer(cmd->slot);
|
|
} break;
|
|
|
|
default:
|
|
return DAWN_VALIDATION_ERROR(disallowedMessage);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize) {
|
|
if (debugGroupStackSize == 0) {
|
|
return DAWN_VALIDATION_ERROR("Pop must be balanced by a corresponding Push.");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize) {
|
|
if (debugGroupStackSize != 0) {
|
|
return DAWN_VALIDATION_ERROR("Each Push must be balanced by a corresponding Pop.");
|
|
}
|
|
return {};
|
|
}
|
|
|
|
MaybeError ValidateRenderBundle(CommandIterator* commands,
|
|
const AttachmentState* attachmentState,
|
|
PassResourceUsage* resourceUsage) {
|
|
PassResourceUsageTracker usageTracker;
|
|
CommandBufferStateTracker commandBufferState;
|
|
uint64_t debugGroupStackSize = 0;
|
|
|
|
Command type;
|
|
while (commands->NextCommandId(&type)) {
|
|
DAWN_TRY(ValidateRenderBundleCommand(commands, type, &usageTracker, &commandBufferState,
|
|
attachmentState, &debugGroupStackSize,
|
|
"Command disallowed inside a render bundle"));
|
|
}
|
|
|
|
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
|
|
DAWN_TRY(usageTracker.ValidateRenderPassUsages());
|
|
ASSERT(resourceUsage != nullptr);
|
|
*resourceUsage = usageTracker.AcquireResourceUsage();
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError ValidateRenderPass(CommandIterator* commands,
|
|
BeginRenderPassCmd* renderPass,
|
|
std::vector<PassResourceUsage>* perPassResourceUsages) {
|
|
PassResourceUsageTracker usageTracker;
|
|
CommandBufferStateTracker commandBufferState;
|
|
uint64_t debugGroupStackSize = 0;
|
|
|
|
// Track usage of the render pass attachments
|
|
for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
|
|
RenderPassColorAttachmentInfo* colorAttachment = &renderPass->colorAttachments[i];
|
|
TextureBase* texture = colorAttachment->view->GetTexture();
|
|
usageTracker.TextureUsedAs(texture, wgpu::TextureUsage::OutputAttachment);
|
|
|
|
TextureViewBase* resolveTarget = colorAttachment->resolveTarget.Get();
|
|
if (resolveTarget != nullptr) {
|
|
usageTracker.TextureUsedAs(resolveTarget->GetTexture(),
|
|
wgpu::TextureUsage::OutputAttachment);
|
|
}
|
|
}
|
|
|
|
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
|
|
TextureBase* texture = renderPass->depthStencilAttachment.view->GetTexture();
|
|
usageTracker.TextureUsedAs(texture, wgpu::TextureUsage::OutputAttachment);
|
|
}
|
|
|
|
Command type;
|
|
while (commands->NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::EndRenderPass: {
|
|
commands->NextCommand<EndRenderPassCmd>();
|
|
|
|
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
|
|
DAWN_TRY(usageTracker.ValidateRenderPassUsages());
|
|
ASSERT(perPassResourceUsages != nullptr);
|
|
perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
|
|
|
|
return {};
|
|
} break;
|
|
|
|
case Command::ExecuteBundles: {
|
|
ExecuteBundlesCmd* cmd = commands->NextCommand<ExecuteBundlesCmd>();
|
|
auto bundles = commands->NextData<Ref<RenderBundleBase>>(cmd->count);
|
|
for (uint32_t i = 0; i < cmd->count; ++i) {
|
|
if (DAWN_UNLIKELY(renderPass->attachmentState.Get() !=
|
|
bundles[i]->GetAttachmentState())) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Render bundle is not compatible with render pass");
|
|
}
|
|
|
|
const PassResourceUsage& usages = bundles[i]->GetResourceUsage();
|
|
for (uint32_t i = 0; i < usages.buffers.size(); ++i) {
|
|
usageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < usages.textures.size(); ++i) {
|
|
usageTracker.TextureUsedAs(usages.textures[i], usages.textureUsages[i]);
|
|
}
|
|
}
|
|
|
|
if (cmd->count > 0) {
|
|
// Reset state. It is invalidated after render bundle execution.
|
|
commandBufferState = CommandBufferStateTracker{};
|
|
}
|
|
|
|
} break;
|
|
|
|
case Command::SetStencilReference: {
|
|
commands->NextCommand<SetStencilReferenceCmd>();
|
|
} break;
|
|
|
|
case Command::SetBlendColor: {
|
|
commands->NextCommand<SetBlendColorCmd>();
|
|
} break;
|
|
|
|
case Command::SetViewport: {
|
|
commands->NextCommand<SetViewportCmd>();
|
|
} break;
|
|
|
|
case Command::SetScissorRect: {
|
|
commands->NextCommand<SetScissorRectCmd>();
|
|
} break;
|
|
|
|
default:
|
|
DAWN_TRY(ValidateRenderBundleCommand(
|
|
commands, type, &usageTracker, &commandBufferState,
|
|
renderPass->attachmentState.Get(), &debugGroupStackSize,
|
|
"Command disallowed inside a render pass"));
|
|
}
|
|
}
|
|
|
|
UNREACHABLE();
|
|
return DAWN_VALIDATION_ERROR("Unfinished render pass");
|
|
}
|
|
|
|
MaybeError ValidateComputePass(CommandIterator* commands,
|
|
std::vector<PassResourceUsage>* perPassResourceUsages) {
|
|
PassResourceUsageTracker usageTracker;
|
|
CommandBufferStateTracker commandBufferState;
|
|
uint64_t debugGroupStackSize = 0;
|
|
|
|
Command type;
|
|
while (commands->NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::EndComputePass: {
|
|
commands->NextCommand<EndComputePassCmd>();
|
|
|
|
DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize));
|
|
DAWN_TRY(usageTracker.ValidateComputePassUsages());
|
|
ASSERT(perPassResourceUsages != nullptr);
|
|
perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
|
|
return {};
|
|
} break;
|
|
|
|
case Command::Dispatch: {
|
|
commands->NextCommand<DispatchCmd>();
|
|
DAWN_TRY(commandBufferState.ValidateCanDispatch());
|
|
} break;
|
|
|
|
case Command::DispatchIndirect: {
|
|
DispatchIndirectCmd* cmd = commands->NextCommand<DispatchIndirectCmd>();
|
|
DAWN_TRY(commandBufferState.ValidateCanDispatch());
|
|
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
|
|
wgpu::BufferUsage::Indirect);
|
|
} break;
|
|
|
|
case Command::InsertDebugMarker: {
|
|
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
|
commands->NextData<char>(cmd->length + 1);
|
|
} break;
|
|
|
|
case Command::PopDebugGroup: {
|
|
commands->NextCommand<PopDebugGroupCmd>();
|
|
DAWN_TRY(ValidateCanPopDebugGroup(debugGroupStackSize));
|
|
debugGroupStackSize--;
|
|
} break;
|
|
|
|
case Command::PushDebugGroup: {
|
|
PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
|
|
commands->NextData<char>(cmd->length + 1);
|
|
debugGroupStackSize++;
|
|
} break;
|
|
|
|
case Command::SetComputePipeline: {
|
|
SetComputePipelineCmd* cmd = commands->NextCommand<SetComputePipelineCmd>();
|
|
ComputePipelineBase* pipeline = cmd->pipeline.Get();
|
|
commandBufferState.SetComputePipeline(pipeline);
|
|
} break;
|
|
|
|
case Command::SetBindGroup: {
|
|
SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
|
|
if (cmd->dynamicOffsetCount > 0) {
|
|
commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
|
|
}
|
|
|
|
TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
|
|
commandBufferState.SetBindGroup(cmd->index, cmd->group.Get());
|
|
} break;
|
|
|
|
default:
|
|
return DAWN_VALIDATION_ERROR("Command disallowed inside a compute pass");
|
|
}
|
|
}
|
|
|
|
UNREACHABLE();
|
|
return DAWN_VALIDATION_ERROR("Unfinished compute pass");
|
|
}
|
|
|
|
} // namespace dawn_native
|