blob: 277e2d74bbdf1cc6bc44ffe0c6a91b949e6ef9ed [file] [log] [blame]
// Copyright 2018 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/RenderPassEncoder.h"
#include "common/Constants.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/ObjectType_autogen.h"
#include "dawn_native/QuerySet.h"
#include "dawn_native/RenderBundle.h"
#include "dawn_native/RenderPipeline.h"
#include <math.h>
#include <cstring>
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,
CommandEncoder* commandEncoder,
EncodingContext* encodingContext,
RenderPassResourceUsageTracker usageTracker,
Ref<AttachmentState> attachmentState,
QuerySetBase* occlusionQuerySet,
uint32_t renderTargetWidth,
uint32_t renderTargetHeight,
bool depthReadOnly,
bool stencilReadOnly)
: RenderEncoderBase(device,
encodingContext,
std::move(attachmentState),
depthReadOnly,
stencilReadOnly),
mCommandEncoder(commandEncoder),
mRenderTargetWidth(renderTargetWidth),
mRenderTargetHeight(renderTargetHeight),
mOcclusionQuerySet(occlusionQuerySet) {
mUsageTracker = std::move(usageTracker);
}
RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
CommandEncoder* commandEncoder,
EncodingContext* encodingContext,
ErrorTag errorTag)
: RenderEncoderBase(device, encodingContext, errorTag), mCommandEncoder(commandEncoder) {
}
RenderPassEncoder* RenderPassEncoder::MakeError(DeviceBase* device,
CommandEncoder* commandEncoder,
EncodingContext* encodingContext) {
return new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError);
}
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::APIEndPass() {
if (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());
}
allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass);
DAWN_TRY(mEncodingContext->ExitRenderPass(this, std::move(mUsageTracker),
mCommandEncoder.Get(),
std::move(mIndirectDrawMetadata)));
return {};
},
"encoding %s.EndPass().", this)) {
}
}
void RenderPassEncoder::APISetStencilReference(uint32_t reference) {
mEncodingContext->TryEncode(
this,
[&](CommandAllocator* allocator) -> MaybeError {
SetStencilReferenceCmd* cmd =
allocator->Allocate<SetStencilReferenceCmd>(Command::SetStencilReference);
cmd->reference = reference;
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]));
// TODO(dawn:563): Give more detail about why the states are incompatible.
DAWN_INVALID_IF(
attachmentState != renderBundles[i]->GetAttachmentState(),
"Attachment state of renderBundles[%i] (%s) is not compatible with "
"attachment state of %s.",
i, renderBundles[i], this);
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 i = 0; i < usages.buffers.size(); ++i) {
mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
}
for (uint32_t i = 0; i < usages.textures.size(); ++i) {
mUsageTracker.AddRenderBundleTextureUsage(usages.textures[i],
usages.textureUsages[i]);
}
if (IsValidationEnabled()) {
mIndirectDrawMetadata.AddBundle(renderBundles[i]);
}
}
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(GetDevice()->ValidateObject(querySet));
DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
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);
}
} // namespace dawn_native