mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-13 10:51:35 +00:00
But keep a namespace alias to avoid breaking project that depend on the previous namespace name while they get updated. Done with through the following steps: - git grep -l dawn_native:: | xargs sed -i "" "s/dawn_native::/dawn::native::/g" - git grep -l "namespace dawn_native" | xargs sed -i "" "s/namespace dawn_native/namespace dawn::native/g" - git cl format - Manual fixups in generator/templates (and the addition of namespace_case in dawn_json_generator.py). - The addition of the namespace alias in DawnNative.h Bug: dawn:824 Change-Id: I676cc4e3ced2e0e4bab32a0d66d7eaf9537e3f09 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/75982 Reviewed-by: Loko Kung <lokokung@google.com> Commit-Queue: Corentin Wallez <cwallez@chromium.org> Auto-Submit: Corentin Wallez <cwallez@chromium.org>
408 lines
19 KiB
C++
408 lines
19 KiB
C++
// Copyright 2017 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/CommandBufferStateTracker.h"
|
|
|
|
#include "common/Assert.h"
|
|
#include "common/BitSetIterator.h"
|
|
#include "dawn_native/BindGroup.h"
|
|
#include "dawn_native/ComputePassEncoder.h"
|
|
#include "dawn_native/ComputePipeline.h"
|
|
#include "dawn_native/Forward.h"
|
|
#include "dawn_native/ObjectType_autogen.h"
|
|
#include "dawn_native/PipelineLayout.h"
|
|
#include "dawn_native/RenderPipeline.h"
|
|
|
|
// TODO(dawn:563): None of the error messages in this file include the buffer objects they are
|
|
// validating against. It would be nice to improve that, but difficult to do without incurring
|
|
// additional tracking costs.
|
|
|
|
namespace dawn::native {
|
|
|
|
namespace {
|
|
bool BufferSizesAtLeastAsBig(const ityp::span<uint32_t, uint64_t> unverifiedBufferSizes,
|
|
const std::vector<uint64_t>& pipelineMinBufferSizes) {
|
|
ASSERT(unverifiedBufferSizes.size() == pipelineMinBufferSizes.size());
|
|
|
|
for (uint32_t i = 0; i < unverifiedBufferSizes.size(); ++i) {
|
|
if (unverifiedBufferSizes[i] < pipelineMinBufferSizes[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
enum ValidationAspect {
|
|
VALIDATION_ASPECT_PIPELINE,
|
|
VALIDATION_ASPECT_BIND_GROUPS,
|
|
VALIDATION_ASPECT_VERTEX_BUFFERS,
|
|
VALIDATION_ASPECT_INDEX_BUFFER,
|
|
|
|
VALIDATION_ASPECT_COUNT
|
|
};
|
|
static_assert(VALIDATION_ASPECT_COUNT == CommandBufferStateTracker::kNumAspects, "");
|
|
|
|
static constexpr CommandBufferStateTracker::ValidationAspects kDispatchAspects =
|
|
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS;
|
|
|
|
static constexpr CommandBufferStateTracker::ValidationAspects kDrawAspects =
|
|
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
|
|
1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
|
|
|
|
static constexpr CommandBufferStateTracker::ValidationAspects kDrawIndexedAspects =
|
|
1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
|
|
1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER;
|
|
|
|
static constexpr CommandBufferStateTracker::ValidationAspects kLazyAspects =
|
|
1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_VERTEX_BUFFERS |
|
|
1 << VALIDATION_ASPECT_INDEX_BUFFER;
|
|
|
|
MaybeError CommandBufferStateTracker::ValidateCanDispatch() {
|
|
return ValidateOperation(kDispatchAspects);
|
|
}
|
|
|
|
MaybeError CommandBufferStateTracker::ValidateCanDraw() {
|
|
return ValidateOperation(kDrawAspects);
|
|
}
|
|
|
|
MaybeError CommandBufferStateTracker::ValidateCanDrawIndexed() {
|
|
return ValidateOperation(kDrawIndexedAspects);
|
|
}
|
|
|
|
MaybeError CommandBufferStateTracker::ValidateBufferInRangeForVertexBuffer(
|
|
uint32_t vertexCount,
|
|
uint32_t firstVertex) {
|
|
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
|
|
|
const ityp::bitset<VertexBufferSlot, kMaxVertexBuffers>&
|
|
vertexBufferSlotsUsedAsVertexBuffer =
|
|
lastRenderPipeline->GetVertexBufferSlotsUsedAsVertexBuffer();
|
|
|
|
for (auto usedSlotVertex : IterateBitSet(vertexBufferSlotsUsedAsVertexBuffer)) {
|
|
const VertexBufferInfo& vertexBuffer =
|
|
lastRenderPipeline->GetVertexBuffer(usedSlotVertex);
|
|
uint64_t arrayStride = vertexBuffer.arrayStride;
|
|
uint64_t bufferSize = mVertexBufferSizes[usedSlotVertex];
|
|
|
|
if (arrayStride == 0) {
|
|
DAWN_INVALID_IF(vertexBuffer.usedBytesInStride > bufferSize,
|
|
"Bound vertex buffer size (%u) at slot %u with an arrayStride of 0 "
|
|
"is smaller than the required size for all attributes (%u)",
|
|
bufferSize, static_cast<uint8_t>(usedSlotVertex),
|
|
vertexBuffer.usedBytesInStride);
|
|
} else {
|
|
uint64_t requiredSize =
|
|
(static_cast<uint64_t>(firstVertex) + vertexCount) * arrayStride;
|
|
// firstVertex and vertexCount are in uint32_t, and arrayStride must not
|
|
// be larger than kMaxVertexBufferArrayStride, which is currently 2048. So by
|
|
// doing checks in uint64_t we avoid overflows.
|
|
DAWN_INVALID_IF(
|
|
requiredSize > bufferSize,
|
|
"Vertex range (first: %u, count: %u) requires a larger buffer (%u) than the "
|
|
"bound buffer size (%u) of the vertex buffer at slot %u with stride (%u).",
|
|
firstVertex, vertexCount, requiredSize, bufferSize,
|
|
static_cast<uint8_t>(usedSlotVertex), arrayStride);
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError CommandBufferStateTracker::ValidateBufferInRangeForInstanceBuffer(
|
|
uint32_t instanceCount,
|
|
uint32_t firstInstance) {
|
|
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
|
|
|
const ityp::bitset<VertexBufferSlot, kMaxVertexBuffers>&
|
|
vertexBufferSlotsUsedAsInstanceBuffer =
|
|
lastRenderPipeline->GetVertexBufferSlotsUsedAsInstanceBuffer();
|
|
|
|
for (auto usedSlotInstance : IterateBitSet(vertexBufferSlotsUsedAsInstanceBuffer)) {
|
|
const VertexBufferInfo& vertexBuffer =
|
|
lastRenderPipeline->GetVertexBuffer(usedSlotInstance);
|
|
uint64_t arrayStride = vertexBuffer.arrayStride;
|
|
uint64_t bufferSize = mVertexBufferSizes[usedSlotInstance];
|
|
if (arrayStride == 0) {
|
|
DAWN_INVALID_IF(vertexBuffer.usedBytesInStride > bufferSize,
|
|
"Bound vertex buffer size (%u) at slot %u with an arrayStride of 0 "
|
|
"is smaller than the required size for all attributes (%u)",
|
|
bufferSize, static_cast<uint8_t>(usedSlotInstance),
|
|
vertexBuffer.usedBytesInStride);
|
|
} else {
|
|
uint64_t requiredSize =
|
|
(static_cast<uint64_t>(firstInstance) + instanceCount) * arrayStride;
|
|
// firstInstance and instanceCount are in uint32_t, and arrayStride must
|
|
// not be larger than kMaxVertexBufferArrayStride, which is currently 2048.
|
|
// So by doing checks in uint64_t we avoid overflows.
|
|
DAWN_INVALID_IF(
|
|
requiredSize > bufferSize,
|
|
"Instance range (first: %u, count: %u) requires a larger buffer (%u) than the "
|
|
"bound buffer size (%u) of the vertex buffer at slot %u with stride (%u).",
|
|
firstInstance, instanceCount, requiredSize, bufferSize,
|
|
static_cast<uint8_t>(usedSlotInstance), arrayStride);
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError CommandBufferStateTracker::ValidateIndexBufferInRange(uint32_t indexCount,
|
|
uint32_t firstIndex) {
|
|
// Validate the range of index buffer
|
|
// firstIndex and indexCount are in uint32_t, while IndexFormatSize is 2 (for
|
|
// wgpu::IndexFormat::Uint16) or 4 (for wgpu::IndexFormat::Uint32), so by doing checks in
|
|
// uint64_t we avoid overflows.
|
|
DAWN_INVALID_IF(
|
|
(static_cast<uint64_t>(firstIndex) + indexCount) * IndexFormatSize(mIndexFormat) >
|
|
mIndexBufferSize,
|
|
"Index range (first: %u, count: %u, format: %s) does not fit in index buffer size "
|
|
"(%u).",
|
|
firstIndex, indexCount, mIndexFormat, mIndexBufferSize);
|
|
return {};
|
|
}
|
|
|
|
MaybeError CommandBufferStateTracker::ValidateOperation(ValidationAspects requiredAspects) {
|
|
// Fast return-true path if everything is good
|
|
ValidationAspects missingAspects = requiredAspects & ~mAspects;
|
|
if (missingAspects.none()) {
|
|
return {};
|
|
}
|
|
|
|
// Generate an error immediately if a non-lazy aspect is missing as computing lazy aspects
|
|
// requires the pipeline to be set.
|
|
DAWN_TRY(CheckMissingAspects(missingAspects & ~kLazyAspects));
|
|
|
|
RecomputeLazyAspects(missingAspects);
|
|
|
|
DAWN_TRY(CheckMissingAspects(requiredAspects & ~mAspects));
|
|
|
|
return {};
|
|
}
|
|
|
|
void CommandBufferStateTracker::RecomputeLazyAspects(ValidationAspects aspects) {
|
|
ASSERT(mAspects[VALIDATION_ASPECT_PIPELINE]);
|
|
ASSERT((aspects & ~kLazyAspects).none());
|
|
|
|
if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) {
|
|
bool matches = true;
|
|
|
|
for (BindGroupIndex i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) {
|
|
if (mBindgroups[i] == nullptr ||
|
|
mLastPipelineLayout->GetBindGroupLayout(i) != mBindgroups[i]->GetLayout() ||
|
|
!BufferSizesAtLeastAsBig(mBindgroups[i]->GetUnverifiedBufferSizes(),
|
|
(*mMinBufferSizes)[i])) {
|
|
matches = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (matches) {
|
|
mAspects.set(VALIDATION_ASPECT_BIND_GROUPS);
|
|
}
|
|
}
|
|
|
|
if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
|
|
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
|
|
|
const ityp::bitset<VertexBufferSlot, kMaxVertexBuffers>& requiredVertexBuffers =
|
|
lastRenderPipeline->GetVertexBufferSlotsUsed();
|
|
if (IsSubset(requiredVertexBuffers, mVertexBufferSlotsUsed)) {
|
|
mAspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
|
|
}
|
|
}
|
|
|
|
if (aspects[VALIDATION_ASPECT_INDEX_BUFFER] && mIndexBufferSet) {
|
|
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
|
if (!IsStripPrimitiveTopology(lastRenderPipeline->GetPrimitiveTopology()) ||
|
|
mIndexFormat == lastRenderPipeline->GetStripIndexFormat()) {
|
|
mAspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
|
|
}
|
|
}
|
|
}
|
|
|
|
MaybeError CommandBufferStateTracker::CheckMissingAspects(ValidationAspects aspects) {
|
|
if (!aspects.any()) {
|
|
return {};
|
|
}
|
|
|
|
DAWN_INVALID_IF(aspects[VALIDATION_ASPECT_PIPELINE], "No pipeline set.");
|
|
|
|
if (DAWN_UNLIKELY(aspects[VALIDATION_ASPECT_INDEX_BUFFER])) {
|
|
DAWN_INVALID_IF(!mIndexBufferSet, "Index buffer was not set.");
|
|
|
|
RenderPipelineBase* lastRenderPipeline = GetRenderPipeline();
|
|
wgpu::IndexFormat pipelineIndexFormat = lastRenderPipeline->GetStripIndexFormat();
|
|
|
|
if (IsStripPrimitiveTopology(lastRenderPipeline->GetPrimitiveTopology())) {
|
|
DAWN_INVALID_IF(
|
|
pipelineIndexFormat == wgpu::IndexFormat::Undefined,
|
|
"%s has a strip primitive topology (%s) but a strip index format of %s, which "
|
|
"prevents it for being used for indexed draw calls.",
|
|
lastRenderPipeline, lastRenderPipeline->GetPrimitiveTopology(),
|
|
pipelineIndexFormat);
|
|
|
|
DAWN_INVALID_IF(
|
|
mIndexFormat != pipelineIndexFormat,
|
|
"Strip index format (%s) of %s does not match index buffer format (%s).",
|
|
pipelineIndexFormat, lastRenderPipeline, mIndexFormat);
|
|
}
|
|
|
|
// The chunk of code above should be similar to the one in |RecomputeLazyAspects|.
|
|
// It returns the first invalid state found. We shouldn't be able to reach this line
|
|
// because to have invalid aspects one of the above conditions must have failed earlier.
|
|
// If this is reached, make sure lazy aspects and the error checks above are consistent.
|
|
UNREACHABLE();
|
|
return DAWN_FORMAT_VALIDATION_ERROR("Index buffer is invalid.");
|
|
}
|
|
|
|
// TODO(dawn:563): Indicate which slots were not set.
|
|
DAWN_INVALID_IF(aspects[VALIDATION_ASPECT_VERTEX_BUFFERS],
|
|
"Vertex buffer slots required by %s were not set.", GetRenderPipeline());
|
|
|
|
if (DAWN_UNLIKELY(aspects[VALIDATION_ASPECT_BIND_GROUPS])) {
|
|
for (BindGroupIndex i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) {
|
|
ASSERT(HasPipeline());
|
|
|
|
DAWN_INVALID_IF(mBindgroups[i] == nullptr, "No bind group set at index %u.",
|
|
static_cast<uint32_t>(i));
|
|
|
|
BindGroupLayoutBase* requiredBGL = mLastPipelineLayout->GetBindGroupLayout(i);
|
|
BindGroupLayoutBase* currentBGL = mBindgroups[i]->GetLayout();
|
|
|
|
DAWN_INVALID_IF(
|
|
requiredBGL->GetPipelineCompatibilityToken() != PipelineCompatibilityToken(0) &&
|
|
currentBGL->GetPipelineCompatibilityToken() !=
|
|
requiredBGL->GetPipelineCompatibilityToken(),
|
|
"The current pipeline (%s) was created with a default layout, and is not "
|
|
"compatible with the %s at index %u which uses a %s that was not created by "
|
|
"the pipeline. Either use the bind group layout returned by calling "
|
|
"getBindGroupLayout(%u) on the pipeline when creating the bind group, or "
|
|
"provide an explicit pipeline layout when creating the pipeline.",
|
|
mLastPipeline, mBindgroups[i], static_cast<uint32_t>(i), currentBGL,
|
|
static_cast<uint32_t>(i));
|
|
|
|
DAWN_INVALID_IF(
|
|
requiredBGL->GetPipelineCompatibilityToken() == PipelineCompatibilityToken(0) &&
|
|
currentBGL->GetPipelineCompatibilityToken() !=
|
|
PipelineCompatibilityToken(0),
|
|
"%s at index %u uses a %s which was created as part of the default layout for "
|
|
"a different pipeline than the current one (%s), and as a result is not "
|
|
"compatible. Use an explicit bind group layout when creating bind groups and "
|
|
"an explicit pipeline layout when creating pipelines to share bind groups "
|
|
"between pipelines.",
|
|
mBindgroups[i], static_cast<uint32_t>(i), currentBGL, mLastPipeline);
|
|
|
|
DAWN_INVALID_IF(
|
|
mLastPipelineLayout->GetBindGroupLayout(i) != mBindgroups[i]->GetLayout(),
|
|
"Bind group layout %s of pipeline layout %s does not match layout %s of bind "
|
|
"group %s at index %u.",
|
|
requiredBGL, mLastPipelineLayout, currentBGL, mBindgroups[i],
|
|
static_cast<uint32_t>(i));
|
|
|
|
// TODO(dawn:563): Report the binding sizes and which ones are failing.
|
|
DAWN_INVALID_IF(!BufferSizesAtLeastAsBig(mBindgroups[i]->GetUnverifiedBufferSizes(),
|
|
(*mMinBufferSizes)[i]),
|
|
"Binding sizes are too small for bind group %s at index %u",
|
|
mBindgroups[i], static_cast<uint32_t>(i));
|
|
}
|
|
|
|
// The chunk of code above should be similar to the one in |RecomputeLazyAspects|.
|
|
// It returns the first invalid state found. We shouldn't be able to reach this line
|
|
// because to have invalid aspects one of the above conditions must have failed earlier.
|
|
// If this is reached, make sure lazy aspects and the error checks above are consistent.
|
|
UNREACHABLE();
|
|
return DAWN_FORMAT_VALIDATION_ERROR("Bind groups are invalid.");
|
|
}
|
|
|
|
UNREACHABLE();
|
|
}
|
|
|
|
void CommandBufferStateTracker::SetComputePipeline(ComputePipelineBase* pipeline) {
|
|
SetPipelineCommon(pipeline);
|
|
}
|
|
|
|
void CommandBufferStateTracker::SetRenderPipeline(RenderPipelineBase* pipeline) {
|
|
SetPipelineCommon(pipeline);
|
|
}
|
|
|
|
void CommandBufferStateTracker::SetBindGroup(BindGroupIndex index,
|
|
BindGroupBase* bindgroup,
|
|
uint32_t dynamicOffsetCount,
|
|
const uint32_t* dynamicOffsets) {
|
|
mBindgroups[index] = bindgroup;
|
|
mDynamicOffsets[index].assign(dynamicOffsets, dynamicOffsets + dynamicOffsetCount);
|
|
mAspects.reset(VALIDATION_ASPECT_BIND_GROUPS);
|
|
}
|
|
|
|
void CommandBufferStateTracker::SetIndexBuffer(wgpu::IndexFormat format, uint64_t size) {
|
|
mIndexBufferSet = true;
|
|
mIndexFormat = format;
|
|
mIndexBufferSize = size;
|
|
}
|
|
|
|
void CommandBufferStateTracker::SetVertexBuffer(VertexBufferSlot slot, uint64_t size) {
|
|
mVertexBufferSlotsUsed.set(slot);
|
|
mVertexBufferSizes[slot] = size;
|
|
}
|
|
|
|
void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) {
|
|
mLastPipeline = pipeline;
|
|
mLastPipelineLayout = pipeline != nullptr ? pipeline->GetLayout() : nullptr;
|
|
mMinBufferSizes = pipeline != nullptr ? &pipeline->GetMinBufferSizes() : nullptr;
|
|
|
|
mAspects.set(VALIDATION_ASPECT_PIPELINE);
|
|
|
|
// Reset lazy aspects so they get recomputed on the next operation.
|
|
mAspects &= ~kLazyAspects;
|
|
}
|
|
|
|
BindGroupBase* CommandBufferStateTracker::GetBindGroup(BindGroupIndex index) const {
|
|
return mBindgroups[index];
|
|
}
|
|
|
|
const std::vector<uint32_t>& CommandBufferStateTracker::GetDynamicOffsets(
|
|
BindGroupIndex index) const {
|
|
return mDynamicOffsets[index];
|
|
}
|
|
|
|
bool CommandBufferStateTracker::HasPipeline() const {
|
|
return mLastPipeline != nullptr;
|
|
}
|
|
|
|
RenderPipelineBase* CommandBufferStateTracker::GetRenderPipeline() const {
|
|
ASSERT(HasPipeline() && mLastPipeline->GetType() == ObjectType::RenderPipeline);
|
|
return static_cast<RenderPipelineBase*>(mLastPipeline);
|
|
}
|
|
|
|
ComputePipelineBase* CommandBufferStateTracker::GetComputePipeline() const {
|
|
ASSERT(HasPipeline() && mLastPipeline->GetType() == ObjectType::ComputePipeline);
|
|
return static_cast<ComputePipelineBase*>(mLastPipeline);
|
|
}
|
|
|
|
PipelineLayoutBase* CommandBufferStateTracker::GetPipelineLayout() const {
|
|
return mLastPipelineLayout;
|
|
}
|
|
|
|
wgpu::IndexFormat CommandBufferStateTracker::GetIndexFormat() const {
|
|
return mIndexFormat;
|
|
}
|
|
|
|
uint64_t CommandBufferStateTracker::GetIndexBufferSize() const {
|
|
return mIndexBufferSize;
|
|
}
|
|
|
|
} // namespace dawn::native
|