blob: 05695fb6e4b9090c69ab4519066c7fe195df7bb8 [file] [log] [blame]
// Copyright 2019 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dawn/native/RenderEncoderBase.h"
#include <math.h>
#include <cstring>
#include <utility>
#include "dawn/common/Constants.h"
#include "dawn/common/Log.h"
#include "dawn/native/Buffer.h"
#include "dawn/native/CommandEncoder.h"
#include "dawn/native/CommandValidation.h"
#include "dawn/native/Commands.h"
#include "dawn/native/Device.h"
#include "dawn/native/RenderPipeline.h"
#include "dawn/native/ValidationUtils_autogen.h"
namespace dawn::native {
RenderEncoderBase::RenderEncoderBase(DeviceBase* device,
StringView label,
EncodingContext* encodingContext,
Ref<AttachmentState> attachmentState,
bool depthReadOnly,
bool stencilReadOnly)
: ProgrammableEncoder(device, label, encodingContext),
mIndirectDrawMetadata(device->GetLimits()),
mAttachmentState(std::move(attachmentState)),
mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)),
mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) {
mDepthReadOnly = depthReadOnly;
mStencilReadOnly = stencilReadOnly;
}
RenderEncoderBase::RenderEncoderBase(DeviceBase* device,
EncodingContext* encodingContext,
ErrorTag errorTag,
StringView label)
: ProgrammableEncoder(device, encodingContext, errorTag, label),
mIndirectDrawMetadata(device->GetLimits()),
mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)),
mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) {}
void RenderEncoderBase::DestroyImpl() {
// Remove reference to the attachment state so that we don't have lingering references to
// it preventing it from being uncached in the device.
mAttachmentState = nullptr;
}
const AttachmentState* RenderEncoderBase::GetAttachmentState() const {
DAWN_ASSERT(!IsError());
DAWN_ASSERT(mAttachmentState != nullptr);
return mAttachmentState.Get();
}
bool RenderEncoderBase::IsDepthReadOnly() const {
DAWN_ASSERT(!IsError());
return mDepthReadOnly;
}
bool RenderEncoderBase::IsStencilReadOnly() const {
DAWN_ASSERT(!IsError());
return mStencilReadOnly;
}
uint64_t RenderEncoderBase::GetDrawCount() const {
DAWN_ASSERT(!IsError());
return mDrawCount;
}
Ref<AttachmentState> RenderEncoderBase::AcquireAttachmentState() {
return std::move(mAttachmentState);
}
void RenderEncoderBase::APIDraw(uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
if (vertexCount == 0) {
GetDevice()->EmitWarningOnce(absl::StrFormat(
"Calling %s.Draw with a vertex count of 0 is unusual.", this));
}
if (instanceCount == 0) {
GetDevice()->EmitWarningOnce(absl::StrFormat(
"Calling %s.Draw with an instance count of 0 is unusual.", this));
}
DAWN_TRY(mCommandBufferState.ValidateCanDraw());
if (GetDevice()->IsCompatibilityMode()) {
DAWN_TRY(mCommandBufferState.ValidateNoDifferentTextureViewsOnSameTexture());
}
DAWN_INVALID_IF(mDisableBaseInstance && firstInstance != 0,
"First instance (%u) must be zero.", firstInstance);
DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForVertexBuffer(vertexCount,
firstVertex));
DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForInstanceBuffer(instanceCount,
firstInstance));
}
DrawCmd* draw = allocator->Allocate<DrawCmd>(Command::Draw);
draw->vertexCount = vertexCount;
draw->instanceCount = instanceCount;
draw->firstVertex = firstVertex;
draw->firstInstance = firstInstance;
mDrawCount++;
return {};
},
"encoding %s.Draw(%u, %u, %u, %u).", this, vertexCount, instanceCount, firstVertex,
firstInstance);
}
void RenderEncoderBase::APIDrawIndexed(uint32_t indexCount,
uint32_t instanceCount,
uint32_t firstIndex,
int32_t baseVertex,
uint32_t firstInstance) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
if (indexCount == 0) {
GetDevice()->EmitWarningOnce(absl::StrFormat(
"Calling %s.Draw with an index count of 0 is unusual.", this));
}
if (instanceCount == 0) {
GetDevice()->EmitWarningOnce(absl::StrFormat(
"Calling %s.Draw with an instance count of 0 is unusual.", this));
}
DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed());
if (GetDevice()->IsCompatibilityMode()) {
DAWN_TRY(mCommandBufferState.ValidateNoDifferentTextureViewsOnSameTexture());
}
DAWN_INVALID_IF(mDisableBaseInstance && firstInstance != 0,
"First instance (%u) must be zero.", firstInstance);
DAWN_INVALID_IF(mDisableBaseVertex && baseVertex != 0,
"Base vertex (%u) must be zero.", baseVertex);
DAWN_TRY(mCommandBufferState.ValidateIndexBufferInRange(indexCount, firstIndex));
// DrawIndexed only validate instance step mode vertex buffer
DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForInstanceBuffer(instanceCount,
firstInstance));
}
DrawIndexedCmd* draw = allocator->Allocate<DrawIndexedCmd>(Command::DrawIndexed);
draw->indexCount = indexCount;
draw->instanceCount = instanceCount;
draw->firstIndex = firstIndex;
draw->baseVertex = baseVertex;
draw->firstInstance = firstInstance;
mDrawCount++;
return {};
},
"encoding %s.DrawIndexed(%u, %u, %u, %i, %u).", this, indexCount, instanceCount, firstIndex,
baseVertex, firstInstance);
}
void RenderEncoderBase::APIDrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer));
DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
DAWN_TRY(mCommandBufferState.ValidateCanDraw());
if (GetDevice()->IsCompatibilityMode()) {
DAWN_TRY(mCommandBufferState.ValidateNoDifferentTextureViewsOnSameTexture());
}
DAWN_INVALID_IF(indirectOffset % 4 != 0,
"Indirect offset (%u) is not a multiple of 4.", indirectOffset);
DAWN_INVALID_IF(
indirectOffset >= indirectBuffer->GetSize() ||
kDrawIndirectSize > indirectBuffer->GetSize() - indirectOffset,
"Indirect offset (%u) is out of bounds of indirect buffer %s size (%u).",
indirectOffset, indirectBuffer, indirectBuffer->GetSize());
}
DrawIndirectCmd* cmd = allocator->Allocate<DrawIndirectCmd>(Command::DrawIndirect);
bool duplicateBaseVertexInstance =
GetDevice()->ShouldDuplicateParametersForDrawIndirect(
mCommandBufferState.GetRenderPipeline());
if (IsValidationEnabled() || duplicateBaseVertexInstance) {
// Later, EncodeIndirectDrawValidationCommands will allocate a scratch storage
// buffer which will store the validated or duplicated indirect data. The buffer
// and offset will be updated to point to it.
// |EncodeIndirectDrawValidationCommands| is called at the end of encoding the
// render pass, while the |cmd| pointer is still valid.
cmd->indirectBuffer = nullptr;
mIndirectDrawMetadata.AddIndirectDraw(indirectBuffer, indirectOffset,
duplicateBaseVertexInstance, cmd);
// We only set usage as `kIndirectBufferForFrontendValidation` so that the indirect
// usage can be ignored in the backends because `indirectBuffer` is actually not
// used as an indirect buffer.
mUsageTracker.BufferUsedAs(indirectBuffer, kIndirectBufferForFrontendValidation);
} else {
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
mUsageTracker.BufferUsedAs(indirectBuffer,
kIndirectBufferForFrontendValidation |
kIndirectBufferForBackendResourceTracking);
}
mDrawCount++;
return {};
},
"encoding %s.DrawIndirect(%s, %u).", this, indirectBuffer, indirectOffset);
}
void RenderEncoderBase::APIDrawIndexedIndirect(BufferBase* indirectBuffer,
uint64_t indirectOffset) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer));
DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed());
if (GetDevice()->IsCompatibilityMode()) {
DAWN_TRY(mCommandBufferState.ValidateNoDifferentTextureViewsOnSameTexture());
}
DAWN_INVALID_IF(indirectOffset % 4 != 0,
"Indirect offset (%u) is not a multiple of 4.", indirectOffset);
DAWN_INVALID_IF(
(indirectOffset >= indirectBuffer->GetSize() ||
kDrawIndexedIndirectSize > indirectBuffer->GetSize() - indirectOffset),
"Indirect offset (%u) is out of bounds of indirect buffer %s size (%u).",
indirectOffset, indirectBuffer, indirectBuffer->GetSize());
}
DrawIndexedIndirectCmd* cmd =
allocator->Allocate<DrawIndexedIndirectCmd>(Command::DrawIndexedIndirect);
bool duplicateBaseVertexInstance =
GetDevice()->ShouldDuplicateParametersForDrawIndirect(
mCommandBufferState.GetRenderPipeline());
bool applyIndexBufferOffsetToFirstIndex =
GetDevice()->ShouldApplyIndexBufferOffsetToFirstIndex();
if (IsValidationEnabled() || duplicateBaseVertexInstance ||
applyIndexBufferOffsetToFirstIndex) {
// Later, EncodeIndirectDrawValidationCommands will allocate a scratch storage
// buffer which will store the validated or duplicated indirect data. The buffer
// and offset will be updated to point to it.
// |EncodeIndirectDrawValidationCommands| is called at the end of encoding the
// render pass, while the |cmd| pointer is still valid.
cmd->indirectBuffer = nullptr;
mIndirectDrawMetadata.AddIndexedIndirectDraw(
mCommandBufferState.GetIndexFormat(), mCommandBufferState.GetIndexBufferSize(),
mCommandBufferState.GetIndexBufferOffset(), indirectBuffer, indirectOffset,
duplicateBaseVertexInstance, cmd);
// We only set usage as `kIndirectBufferForFrontendValidation` so that the indirect
// usage can be ignored in the backends because `indirectBuffer` is actually not
// used as an indirect buffer.
mUsageTracker.BufferUsedAs(indirectBuffer, kIndirectBufferForFrontendValidation);
} else {
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
mUsageTracker.BufferUsedAs(indirectBuffer,
kIndirectBufferForFrontendValidation |
kIndirectBufferForBackendResourceTracking);
}
mDrawCount++;
return {};
},
"encoding %s.DrawIndexedIndirect(%s, %u).", this, indirectBuffer, indirectOffset);
}
void RenderEncoderBase::APIMultiDrawIndirect(BufferBase* indirectBuffer,
uint64_t indirectOffset,
uint32_t maxDrawCount,
BufferBase* drawCountBuffer,
uint64_t drawCountBufferOffset) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF(!GetDevice()->HasFeature(Feature::MultiDrawIndirect),
"%s is not enabled.", wgpu::FeatureName::MultiDrawIndirect);
DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer));
DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
DAWN_INVALID_IF(indirectOffset % 4 != 0,
"Indirect offset (%u) is not a multiple of 4.", indirectOffset);
DAWN_INVALID_IF(indirectOffset >= indirectBuffer->GetSize(),
"Indirect offset (%u) is larger than or equal to the size (%u) of "
"the indirect buffer.",
indirectOffset, indirectBuffer->GetSize());
// maxDrawCount * kDrawIndirectSize can't overflow because
// kDrawIndirectSize is a uint64_t which results in a uint64_t
DAWN_INVALID_IF(
indirectBuffer->GetSize() - indirectOffset < maxDrawCount * kDrawIndirectSize,
"Indirect Buffer size (%u) and offset (%u) can not hold maxDrawCount "
"(%u) draw commands.",
indirectBuffer->GetSize(), indirectOffset, maxDrawCount);
// draw count buffer is optional
if (drawCountBuffer != nullptr) {
DAWN_TRY(GetDevice()->ValidateObject(drawCountBuffer));
DAWN_TRY(ValidateCanUseAs(drawCountBuffer, wgpu::BufferUsage::Indirect));
DAWN_INVALID_IF(drawCountBufferOffset % 4 != 0,
"Draw count buffer offset (%u) is not a multiple of 4.",
drawCountBufferOffset);
DAWN_INVALID_IF(
drawCountBufferOffset >= drawCountBuffer->GetSize(),
"Draw count buffer offset (%u) is larger than or equal to the size "
"(%u) of the draw count buffer.",
drawCountBufferOffset, drawCountBuffer->GetSize());
// Can't underflow because the offset is checked to be smaller than the buffer.
DAWN_INVALID_IF(drawCountBuffer->GetSize() - drawCountBufferOffset < 4,
"Draw count buffer offset (%u) is out of bounds of the draw "
"count buffer %s size (%u).",
drawCountBufferOffset, drawCountBuffer,
drawCountBuffer->GetSize());
}
DAWN_TRY(mCommandBufferState.ValidateCanDraw());
if (GetDevice()->IsCompatibilityMode()) {
DAWN_TRY(mCommandBufferState.ValidateNoDifferentTextureViewsOnSameTexture());
}
}
MultiDrawIndirectCmd* cmd =
allocator->Allocate<MultiDrawIndirectCmd>(Command::MultiDrawIndirect);
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
cmd->maxDrawCount = maxDrawCount;
cmd->drawCountBuffer = drawCountBuffer;
cmd->drawCountOffset = drawCountBufferOffset;
bool duplicateBaseVertexInstance =
GetDevice()->ShouldDuplicateParametersForDrawIndirect(
mCommandBufferState.GetRenderPipeline());
mIndirectDrawMetadata.AddMultiDrawIndirect(
mCommandBufferState.GetRenderPipeline()->GetPrimitiveTopology(),
duplicateBaseVertexInstance, cmd);
if (GetDevice()->IsValidationEnabled() ||
GetDevice()->MayRequireDuplicationOfIndirectParameters()) {
// We only set usage as `kIndirectBufferForFrontendValidation` because
// `indirectBuffer` may not be used as an indirect buffer. The usage of
// `indirectBuffer` may be updated in `EncodeIndirectDrawValidationCommands()` in
// `EncodingContext::ExitRenderPass()`, which is only called when above conditions
// are both met.
mUsageTracker.BufferUsedAs(indirectBuffer, kIndirectBufferForFrontendValidation);
} else {
mUsageTracker.BufferUsedAs(indirectBuffer,
kIndirectBufferForFrontendValidation |
kIndirectBufferForBackendResourceTracking);
}
if (drawCountBuffer != nullptr) {
mUsageTracker.BufferUsedAs(drawCountBuffer,
kIndirectBufferForFrontendValidation |
kIndirectBufferForBackendResourceTracking);
}
mDrawCount += maxDrawCount;
return {};
},
"encoding %s.MultiDrawIndirect(%s, %u, %u, %s, %u).", this, indirectBuffer, indirectOffset,
maxDrawCount, drawCountBuffer, drawCountBufferOffset);
}
void RenderEncoderBase::APIMultiDrawIndexedIndirect(BufferBase* indirectBuffer,
uint64_t indirectOffset,
uint32_t maxDrawCount,
BufferBase* drawCountBuffer,
uint64_t drawCountBufferOffset) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF(!GetDevice()->HasFeature(Feature::MultiDrawIndirect),
"%s is not enabled.", wgpu::FeatureName::MultiDrawIndirect);
DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer));
DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
DAWN_INVALID_IF(indirectOffset % 4 != 0,
"Indirect offset (%u) is not a multiple of 4.", indirectOffset);
DAWN_INVALID_IF(indirectOffset >= indirectBuffer->GetSize(),
"Indirect offset (%u) is larger than or equal to the size (%u) of "
"the indirect buffer.",
indirectOffset, indirectBuffer->GetSize());
// maxDrawCount * kDrawIndexedIndirectSize can't overflow because
// kDrawIndexedIndirectSize is a uint64_t which results in a uint64_t
DAWN_INVALID_IF(
indirectBuffer->GetSize() - indirectOffset <
maxDrawCount * kDrawIndexedIndirectSize,
"Indirect Buffer size (%u) and offset (%u) can not hold maxDrawCount "
"(%u) draw commands.",
indirectBuffer->GetSize(), indirectOffset, maxDrawCount);
// draw count buffer is optional
if (drawCountBuffer != nullptr) {
DAWN_TRY(GetDevice()->ValidateObject(drawCountBuffer));
DAWN_TRY(ValidateCanUseAs(drawCountBuffer, wgpu::BufferUsage::Indirect));
DAWN_INVALID_IF(drawCountBufferOffset % 4 != 0,
"Draw count buffer offset (%u) is not a multiple of 4.",
drawCountBufferOffset);
DAWN_INVALID_IF(
drawCountBufferOffset >= drawCountBuffer->GetSize(),
"Draw count buffer offset (%u) is larger than or equal to the size "
"(%u) of the draw count buffer.",
drawCountBufferOffset, drawCountBuffer->GetSize());
// Can't underflow because the offset is checked to be smaller than the buffer.
DAWN_INVALID_IF(drawCountBuffer->GetSize() - drawCountBufferOffset < 4,
"Draw count buffer offset (%u) is out of bounds of the draw "
"count buffer %s size (%u).",
drawCountBufferOffset, drawCountBuffer,
drawCountBuffer->GetSize());
}
DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed());
if (GetDevice()->IsCompatibilityMode()) {
DAWN_TRY(mCommandBufferState.ValidateNoDifferentTextureViewsOnSameTexture());
}
}
MultiDrawIndexedIndirectCmd* cmd =
allocator->Allocate<MultiDrawIndexedIndirectCmd>(Command::MultiDrawIndexedIndirect);
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
cmd->maxDrawCount = maxDrawCount;
cmd->drawCountBuffer = drawCountBuffer;
cmd->drawCountOffset = drawCountBufferOffset;
bool duplicateBaseVertexInstance =
GetDevice()->ShouldDuplicateParametersForDrawIndirect(
mCommandBufferState.GetRenderPipeline());
mIndirectDrawMetadata.AddMultiDrawIndexedIndirect(
mCommandBufferState.GetIndexBuffer(), mCommandBufferState.GetIndexFormat(),
mCommandBufferState.GetIndexBufferSize(),
mCommandBufferState.GetIndexBufferOffset(),
mCommandBufferState.GetRenderPipeline()->GetPrimitiveTopology(),
duplicateBaseVertexInstance, cmd);
if (GetDevice()->IsValidationEnabled() ||
GetDevice()->MayRequireDuplicationOfIndirectParameters()) {
// We only set usage as `kIndirectBufferForFrontendValidation` because
// `indirectBuffer` may not be used as an indirect buffer. The usage of
// `indirectBuffer` may be updated in `EncodeIndirectDrawValidationCommands()` in
// `EncodingContext::ExitRenderPass()`, which is only called when above conditions
// are both met.
mUsageTracker.BufferUsedAs(indirectBuffer, kIndirectBufferForFrontendValidation);
} else {
mUsageTracker.BufferUsedAs(indirectBuffer,
kIndirectBufferForFrontendValidation |
kIndirectBufferForBackendResourceTracking);
}
if (drawCountBuffer != nullptr) {
mUsageTracker.BufferUsedAs(drawCountBuffer,
kIndirectBufferForFrontendValidation |
kIndirectBufferForBackendResourceTracking);
}
mDrawCount += maxDrawCount;
return {};
},
"encoding %s.MultiDrawIndexedIndirect(%s, %u, %u, %s, %u).", this, indirectBuffer,
indirectOffset, maxDrawCount, drawCountBuffer, drawCountBufferOffset);
}
void RenderEncoderBase::APISetPipeline(RenderPipelineBase* pipeline) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(pipeline));
DAWN_INVALID_IF(pipeline->GetAttachmentState() != mAttachmentState.Get(),
"Attachment state of %s is not compatible with %s.\n"
"%s expects an attachment state of %s.\n"
"%s has an attachment state of %s.",
pipeline, this, this, mAttachmentState.Get(), pipeline,
pipeline->GetAttachmentState());
DAWN_INVALID_IF(pipeline->WritesDepth() && mDepthReadOnly,
"%s writes depth while %s's depthReadOnly is true", pipeline, this);
DAWN_INVALID_IF(pipeline->WritesStencil() && mStencilReadOnly,
"%s writes stencil while %s's stencilReadOnly is true", pipeline,
this);
}
mCommandBufferState.SetRenderPipeline(pipeline);
SetRenderPipelineCmd* cmd =
allocator->Allocate<SetRenderPipelineCmd>(Command::SetRenderPipeline);
cmd->pipeline = pipeline;
return {};
},
"encoding %s.SetPipeline(%s).", this, pipeline);
}
void RenderEncoderBase::APISetIndexBuffer(BufferBase* buffer,
wgpu::IndexFormat format,
uint64_t offset,
uint64_t size) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(buffer));
DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Index));
DAWN_TRY(ValidateIndexFormat(format));
DAWN_INVALID_IF(format == wgpu::IndexFormat::Undefined,
"Index format must be specified");
DAWN_INVALID_IF(offset % uint64_t(IndexFormatSize(format)) != 0,
"Index buffer offset (%u) is not a multiple of the size (%u) "
"of %s.",
offset, IndexFormatSize(format), format);
uint64_t bufferSize = buffer->GetSize();
DAWN_INVALID_IF(offset > bufferSize,
"Index buffer offset (%u) is larger than the size (%u) of %s.",
offset, bufferSize, buffer);
uint64_t remainingSize = bufferSize - offset;
if (size == wgpu::kWholeSize) {
size = remainingSize;
} else {
DAWN_INVALID_IF(size > remainingSize,
"Index buffer range (offset: %u, size: %u) doesn't fit in "
"the size (%u) of "
"%s.",
offset, size, bufferSize, buffer);
}
} else {
if (size == wgpu::kWholeSize) {
DAWN_ASSERT(buffer->GetSize() >= offset);
size = buffer->GetSize() - offset;
}
}
mCommandBufferState.SetIndexBuffer(buffer, format, offset, size);
SetIndexBufferCmd* cmd =
allocator->Allocate<SetIndexBufferCmd>(Command::SetIndexBuffer);
cmd->buffer = buffer;
cmd->format = format;
cmd->offset = offset;
cmd->size = size;
mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Index);
return {};
},
"encoding %s.SetIndexBuffer(%s, %s, %u, %u).", this, buffer, format, offset, size);
}
void RenderEncoderBase::APISetVertexBuffer(uint32_t slot,
BufferBase* buffer,
uint64_t offset,
uint64_t size) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF(slot >= kMaxVertexBuffers,
"Vertex buffer slot (%u) is larger the maximum (%u)", slot,
kMaxVertexBuffers - 1);
if (buffer == nullptr) {
DAWN_INVALID_IF(offset != 0, "Offset (%u) must be 0 if buffer is null", offset);
DAWN_INVALID_IF(
size != 0 && size != wgpu::kWholeSize,
"Size (%u) must be either 0 or wgpu::kWholeSize if buffer is null", size);
} else {
DAWN_TRY(GetDevice()->ValidateObject(buffer));
DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Vertex));
DAWN_INVALID_IF(offset % 4 != 0,
"Vertex buffer offset (%u) is not a multiple of 4", offset);
uint64_t bufferSize = buffer->GetSize();
DAWN_INVALID_IF(offset > bufferSize,
"Vertex buffer offset (%u) is larger than the size (%u) of %s.",
offset, bufferSize, buffer);
uint64_t remainingSize = bufferSize - offset;
if (size == wgpu::kWholeSize) {
size = remainingSize;
} else {
DAWN_INVALID_IF(size > remainingSize,
"Vertex buffer range (offset: %u, size: %u) doesn't fit in "
"the size (%u) "
"of %s.",
offset, size, bufferSize, buffer);
}
}
} else {
if (size == wgpu::kWholeSize && buffer != nullptr) {
DAWN_ASSERT(buffer->GetSize() >= offset);
size = buffer->GetSize() - offset;
}
}
VertexBufferSlot vbSlot = VertexBufferSlot(static_cast<uint8_t>(slot));
if (buffer == nullptr) {
mCommandBufferState.UnsetVertexBuffer(vbSlot);
} else {
mCommandBufferState.SetVertexBuffer(vbSlot, size);
SetVertexBufferCmd* cmd =
allocator->Allocate<SetVertexBufferCmd>(Command::SetVertexBuffer);
cmd->slot = vbSlot;
cmd->buffer = buffer;
cmd->offset = offset;
cmd->size = size;
mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Vertex);
}
return {};
},
"encoding %s.SetVertexBuffer(%u, %s, %u, %u).", this, slot, buffer, offset, size);
}
void RenderEncoderBase::APISetBindGroup(uint32_t groupIndexIn,
BindGroupBase* group,
uint32_t dynamicOffsetCount,
const uint32_t* dynamicOffsets) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
BindGroupIndex groupIndex(groupIndexIn);
if (IsValidationEnabled()) {
DAWN_TRY(
ValidateSetBindGroup(groupIndex, group, dynamicOffsetCount, dynamicOffsets));
}
if (group == nullptr) {
mCommandBufferState.UnsetBindGroup(groupIndex);
} else {
RecordSetBindGroup(allocator, groupIndex, group, dynamicOffsetCount,
dynamicOffsets);
mCommandBufferState.SetBindGroup(groupIndex, group, dynamicOffsetCount,
dynamicOffsets);
mUsageTracker.AddBindGroup(group);
}
return {};
},
"encoding %s.SetBindGroup(%u, %s, %u, ...).", this, groupIndexIn, group,
dynamicOffsetCount);
}
} // namespace dawn::native