blob: 5740c9ad59f89a8a1b49719d40561b9bf0aed3b3 [file] [log] [blame]
// Copyright 2018 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/RenderPassEncoder.h"
#include <math.h>
#include <cstring>
#include <utility>
#include "dawn/common/Constants.h"
#include "dawn/native/Buffer.h"
#include "dawn/native/ChainUtils.h"
#include "dawn/native/CommandEncoder.h"
#include "dawn/native/CommandValidation.h"
#include "dawn/native/Commands.h"
#include "dawn/native/Device.h"
#include "dawn/native/ObjectType_autogen.h"
#include "dawn/native/QuerySet.h"
#include "dawn/native/RenderBundle.h"
#include "dawn/native/RenderPipeline.h"
namespace dawn::native {
namespace {
// Check the query at queryIndex is unavailable, otherwise it cannot be written.
MaybeError ValidateQueryIndexOverwrite(QuerySetBase* querySet,
uint32_t queryIndex,
const QueryAvailabilityMap& queryAvailabilityMap) {
auto it = queryAvailabilityMap.find(querySet);
DAWN_INVALID_IF(it != queryAvailabilityMap.end() && it->second[queryIndex],
"Query index %u of %s is written to twice in a render pass.", queryIndex,
querySet);
return {};
}
} // namespace
// The usage tracker is passed in here, because it is prepopulated with usages from the
// BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the
// command, then this wouldn't be necessary.
RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
const UnpackedPtr<RenderPassDescriptor>& descriptor,
CommandEncoder* commandEncoder,
EncodingContext* encodingContext,
RenderPassResourceUsageTracker usageTracker,
Ref<AttachmentState> attachmentState,
uint32_t renderTargetWidth,
uint32_t renderTargetHeight,
bool depthReadOnly,
bool stencilReadOnly,
std::function<void()> endCallback)
: RenderEncoderBase(device,
descriptor->label,
encodingContext,
std::move(attachmentState),
depthReadOnly,
stencilReadOnly),
mCommandEncoder(commandEncoder),
mRenderTargetWidth(renderTargetWidth),
mRenderTargetHeight(renderTargetHeight),
mOcclusionQuerySet(descriptor->occlusionQuerySet),
mEndCallback(std::move(endCallback)) {
mUsageTracker = std::move(usageTracker);
if (auto* maxDrawCountInfo = descriptor.Get<RenderPassDescriptorMaxDrawCount>()) {
mMaxDrawCount = maxDrawCountInfo->maxDrawCount;
}
GetObjectTrackingList()->Track(this);
}
// static
Ref<RenderPassEncoder> RenderPassEncoder::Create(
DeviceBase* device,
const UnpackedPtr<RenderPassDescriptor>& descriptor,
CommandEncoder* commandEncoder,
EncodingContext* encodingContext,
RenderPassResourceUsageTracker usageTracker,
Ref<AttachmentState> attachmentState,
uint32_t renderTargetWidth,
uint32_t renderTargetHeight,
bool depthReadOnly,
bool stencilReadOnly,
std::function<void()> endCallback) {
return AcquireRef(new RenderPassEncoder(device, descriptor, commandEncoder, encodingContext,
std::move(usageTracker), std::move(attachmentState),
renderTargetWidth, renderTargetHeight, depthReadOnly,
stencilReadOnly, std::move(endCallback)));
}
RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
CommandEncoder* commandEncoder,
EncodingContext* encodingContext,
ErrorTag errorTag,
const char* label)
: RenderEncoderBase(device, encodingContext, errorTag, label),
mCommandEncoder(commandEncoder) {}
// static
Ref<RenderPassEncoder> RenderPassEncoder::MakeError(DeviceBase* device,
CommandEncoder* commandEncoder,
EncodingContext* encodingContext,
const char* label) {
return AcquireRef(
new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError, label));
}
RenderPassEncoder::~RenderPassEncoder() {
mEncodingContext = nullptr;
}
void RenderPassEncoder::DestroyImpl() {
mIndirectDrawMetadata.ClearIndexedIndirectBufferValidationInfo();
mCommandBufferState.End();
RenderEncoderBase::DestroyImpl();
// Ensure that the pass has exited. This is done for passes only since validation requires
// they exit before destruction while bundles do not.
mEncodingContext->EnsurePassExited(this);
}
ObjectType RenderPassEncoder::GetType() const {
return ObjectType::RenderPassEncoder;
}
void RenderPassEncoder::TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex) {
DAWN_ASSERT(querySet != nullptr);
// Track the query availability with true on render pass for rewrite validation and query
// reset on render pass on Vulkan
mUsageTracker.TrackQueryAvailability(querySet, queryIndex);
// Track it again on command encoder for zero-initializing when resolving unused queries.
mCommandEncoder->TrackQueryAvailability(querySet, queryIndex);
}
void RenderPassEncoder::APIEnd() {
// The encoding context might create additional resources, so we need to lock the device.
auto deviceLock(GetDevice()->GetScopedLock());
End();
}
void RenderPassEncoder::End() {
DAWN_ASSERT(GetDevice()->IsLockedByCurrentThreadIfNeeded());
mCommandBufferState.End();
if (mEnded && IsValidationEnabled()) {
GetDevice()->HandleError(DAWN_VALIDATION_ERROR("%s was already ended.", this));
return;
}
mEnded = true;
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_TRY(ValidateProgrammableEncoderEnd());
DAWN_INVALID_IF(
mOcclusionQueryActive,
"Render pass %s ended with incomplete occlusion query index %u of %s.", this,
mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get());
DAWN_INVALID_IF(mDrawCount > mMaxDrawCount,
"The drawCount (%u) of %s is greater than the maxDrawCount (%u).",
mDrawCount, this, mMaxDrawCount);
}
allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass);
DAWN_TRY(mEncodingContext->ExitRenderPass(this, std::move(mUsageTracker),
mCommandEncoder.Get(),
std::move(mIndirectDrawMetadata)));
return {};
},
"encoding %s.End().", this);
if (mEndCallback) {
mEndCallback();
}
}
void RenderPassEncoder::APISetStencilReference(uint32_t reference) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
SetStencilReferenceCmd* cmd =
allocator->Allocate<SetStencilReferenceCmd>(Command::SetStencilReference);
cmd->reference = reference & 255;
return {};
},
"encoding %s.SetStencilReference(%u).", this, reference);
}
void RenderPassEncoder::APISetBlendConstant(const Color* color) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
SetBlendConstantCmd* cmd =
allocator->Allocate<SetBlendConstantCmd>(Command::SetBlendConstant);
cmd->color = *color;
return {};
},
"encoding %s.SetBlendConstant(%s).", this, color);
}
void RenderPassEncoder::APISetViewport(float x,
float y,
float width,
float height,
float minDepth,
float maxDepth) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF((isnan(x) || isnan(y) || isnan(width) || isnan(height) ||
isnan(minDepth) || isnan(maxDepth)),
"A parameter of the viewport (x: %f, y: %f, width: %f, height: %f, "
"minDepth: %f, maxDepth: %f) is NaN.",
x, y, width, height, minDepth, maxDepth);
DAWN_INVALID_IF(
x < 0 || y < 0 || width < 0 || height < 0,
"Viewport bounds (x: %f, y: %f, width: %f, height: %f) contains a negative "
"value.",
x, y, width, height);
DAWN_INVALID_IF(
x + width > mRenderTargetWidth || y + height > mRenderTargetHeight,
"Viewport bounds (x: %f, y: %f, width: %f, height: %f) are not contained "
"in "
"the render target dimensions (%u x %u).",
x, y, width, height, mRenderTargetWidth, mRenderTargetHeight);
// Check for depths being in [0, 1] and min <= max in 3 checks instead of 5.
DAWN_INVALID_IF(minDepth < 0 || minDepth > maxDepth || maxDepth > 1,
"Viewport minDepth (%f) and maxDepth (%f) are not in [0, 1] or "
"minDepth was "
"greater than maxDepth.",
minDepth, maxDepth);
}
SetViewportCmd* cmd = allocator->Allocate<SetViewportCmd>(Command::SetViewport);
cmd->x = x;
cmd->y = y;
cmd->width = width;
cmd->height = height;
cmd->minDepth = minDepth;
cmd->maxDepth = maxDepth;
return {};
},
"encoding %s.SetViewport(%f, %f, %f, %f, %f, %f).", this, x, y, width, height, minDepth,
maxDepth);
}
void RenderPassEncoder::APISetScissorRect(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF(
width > mRenderTargetWidth || height > mRenderTargetHeight ||
x > mRenderTargetWidth - width || y > mRenderTargetHeight - height,
"Scissor rect (x: %u, y: %u, width: %u, height: %u) is not contained in "
"the render target dimensions (%u x %u).",
x, y, width, height, mRenderTargetWidth, mRenderTargetHeight);
}
SetScissorRectCmd* cmd =
allocator->Allocate<SetScissorRectCmd>(Command::SetScissorRect);
cmd->x = x;
cmd->y = y;
cmd->width = width;
cmd->height = height;
return {};
},
"encoding %s.SetScissorRect(%u, %u, %u, %u).", this, x, y, width, height);
}
void RenderPassEncoder::APIExecuteBundles(uint32_t count, RenderBundleBase* const* renderBundles) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
const AttachmentState* attachmentState = GetAttachmentState();
bool depthReadOnlyInPass = IsDepthReadOnly();
bool stencilReadOnlyInPass = IsStencilReadOnly();
for (uint32_t i = 0; i < count; ++i) {
DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i]));
DAWN_INVALID_IF(attachmentState != renderBundles[i]->GetAttachmentState(),
"Attachment state of renderBundles[%i] (%s) is not "
"compatible with %s.\n"
"%s expects an attachment state of %s.\n"
"renderBundles[%i] (%s) has an attachment state of %s.",
i, renderBundles[i], this, this, attachmentState, i,
renderBundles[i], renderBundles[i]->GetAttachmentState());
bool depthReadOnlyInBundle = renderBundles[i]->IsDepthReadOnly();
DAWN_INVALID_IF(depthReadOnlyInPass && !depthReadOnlyInBundle,
"DepthReadOnly (%u) of renderBundle[%i] (%s) is not compatible "
"with DepthReadOnly (%u) of %s.",
depthReadOnlyInBundle, i, renderBundles[i], depthReadOnlyInPass,
this);
bool stencilReadOnlyInBundle = renderBundles[i]->IsStencilReadOnly();
DAWN_INVALID_IF(stencilReadOnlyInPass && !stencilReadOnlyInBundle,
"StencilReadOnly (%u) of renderBundle[%i] (%s) is not "
"compatible with StencilReadOnly (%u) of %s.",
stencilReadOnlyInBundle, i, renderBundles[i],
stencilReadOnlyInPass, this);
}
}
mCommandBufferState = CommandBufferStateTracker{};
ExecuteBundlesCmd* cmd =
allocator->Allocate<ExecuteBundlesCmd>(Command::ExecuteBundles);
cmd->count = count;
Ref<RenderBundleBase>* bundles = allocator->AllocateData<Ref<RenderBundleBase>>(count);
for (uint32_t i = 0; i < count; ++i) {
bundles[i] = renderBundles[i];
const RenderPassResourceUsage& usages = bundles[i]->GetResourceUsage();
for (uint32_t j = 0; j < usages.buffers.size(); ++j) {
mUsageTracker.BufferUsedAs(usages.buffers[j], usages.bufferSyncInfos[j].usage,
usages.bufferSyncInfos[j].shaderStages);
}
for (uint32_t j = 0; j < usages.textures.size(); ++j) {
mUsageTracker.AddRenderBundleTextureUsage(usages.textures[j],
usages.textureSyncInfos[j]);
}
if (IsValidationEnabled()) {
mIndirectDrawMetadata.AddBundle(renderBundles[i]);
}
mDrawCount += bundles[i]->GetDrawCount();
}
return {};
},
"encoding %s.ExecuteBundles(%u, ...).", this, count);
}
void RenderPassEncoder::APIBeginOcclusionQuery(uint32_t queryIndex) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF(mOcclusionQuerySet.Get() == nullptr,
"The occlusionQuerySet in RenderPassDescriptor is not set.");
// The type of querySet has been validated by ValidateRenderPassDescriptor
DAWN_INVALID_IF(queryIndex >= mOcclusionQuerySet->GetQueryCount(),
"Query index (%u) exceeds the number of queries (%u) in %s.",
queryIndex, mOcclusionQuerySet->GetQueryCount(),
mOcclusionQuerySet.Get());
DAWN_INVALID_IF(mOcclusionQueryActive,
"An occlusion query (%u) in %s is already active.",
mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get());
DAWN_TRY_CONTEXT(
ValidateQueryIndexOverwrite(mOcclusionQuerySet.Get(), queryIndex,
mUsageTracker.GetQueryAvailabilityMap()),
"validating the occlusion query index (%u) in %s", queryIndex,
mOcclusionQuerySet.Get());
}
// Record the current query index for endOcclusionQuery.
mCurrentOcclusionQueryIndex = queryIndex;
mOcclusionQueryActive = true;
BeginOcclusionQueryCmd* cmd =
allocator->Allocate<BeginOcclusionQueryCmd>(Command::BeginOcclusionQuery);
cmd->querySet = mOcclusionQuerySet.Get();
cmd->queryIndex = queryIndex;
return {};
},
"encoding %s.BeginOcclusionQuery(%u).", this, queryIndex);
}
void RenderPassEncoder::APIEndOcclusionQuery() {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF(!mOcclusionQueryActive, "No occlusion queries are active.");
}
TrackQueryAvailability(mOcclusionQuerySet.Get(), mCurrentOcclusionQueryIndex);
mOcclusionQueryActive = false;
EndOcclusionQueryCmd* cmd =
allocator->Allocate<EndOcclusionQueryCmd>(Command::EndOcclusionQuery);
cmd->querySet = mOcclusionQuerySet.Get();
cmd->queryIndex = mCurrentOcclusionQueryIndex;
return {};
},
"encoding %s.EndOcclusionQuery().", this);
}
void RenderPassEncoder::APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_TRY(ValidateTimestampQuery(
GetDevice(), querySet, queryIndex,
Feature::ChromiumExperimentalTimestampQueryInsidePasses));
DAWN_TRY_CONTEXT(ValidateQueryIndexOverwrite(
querySet, queryIndex, mUsageTracker.GetQueryAvailabilityMap()),
"validating the timestamp query index (%u) of %s", queryIndex,
querySet);
}
TrackQueryAvailability(querySet, queryIndex);
WriteTimestampCmd* cmd =
allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);
cmd->querySet = querySet;
cmd->queryIndex = queryIndex;
return {};
},
"encoding %s.WriteTimestamp(%s, %u).", this, querySet, queryIndex);
}
void RenderPassEncoder::APIPixelLocalStorageBarrier() {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
if (IsValidationEnabled()) {
DAWN_INVALID_IF(!GetAttachmentState()->HasPixelLocalStorage(),
"%s does not define any pixel local storage.", this);
}
allocator->Allocate<PixelLocalStorageBarrierCmd>(Command::PixelLocalStorageBarrier);
return {};
},
"encoding %s.PixelLocalStorageBarrier().", this);
}
} // namespace dawn::native