blob: d64865dcb673b60e54a46c83a3e8c725357f3f8c [file] [log] [blame]
// 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.
#ifndef DAWNNATIVE_ENCODINGCONTEXT_H_
#define DAWNNATIVE_ENCODINGCONTEXT_H_
#include "dawn_native/CommandAllocator.h"
#include "dawn_native/Error.h"
#include "dawn_native/ErrorData.h"
#include "dawn_native/IndirectDrawMetadata.h"
#include "dawn_native/PassResourceUsageTracker.h"
#include "dawn_native/dawn_platform.h"
#include <string>
namespace dawn::native {
class CommandEncoder;
class DeviceBase;
class ApiObjectBase;
// Base class for allocating/iterating commands.
// It performs error tracking as well as encoding state for render/compute passes.
class EncodingContext {
public:
EncodingContext(DeviceBase* device, const ApiObjectBase* initialEncoder);
~EncodingContext();
// Marks the encoding context as destroyed so that any future encodes will fail, and all
// encoded commands are released.
void Destroy();
CommandIterator AcquireCommands();
CommandIterator* GetIterator();
// Functions to handle encoder errors
void HandleError(std::unique_ptr<ErrorData> error);
inline bool ConsumedError(MaybeError maybeError) {
if (DAWN_UNLIKELY(maybeError.IsError())) {
HandleError(maybeError.AcquireError());
return true;
}
return false;
}
template <typename... Args>
inline bool ConsumedError(MaybeError maybeError,
const char* formatStr,
const Args&... args) {
if (DAWN_UNLIKELY(maybeError.IsError())) {
std::unique_ptr<ErrorData> error = maybeError.AcquireError();
if (error->GetType() == InternalErrorType::Validation) {
std::string out;
absl::UntypedFormatSpec format(formatStr);
if (absl::FormatUntyped(&out, format, {absl::FormatArg(args)...})) {
error->AppendContext(std::move(out));
} else {
error->AppendContext(absl::StrFormat(
"[Failed to format error message: \"%s\"].", formatStr));
}
}
HandleError(std::move(error));
return true;
}
return false;
}
inline bool CheckCurrentEncoder(const ApiObjectBase* encoder) {
if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
if (mDestroyed) {
HandleError(
DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", encoder));
} else if (mCurrentEncoder != mTopLevelEncoder) {
// The top level encoder was used when a pass encoder was current.
HandleError(DAWN_FORMAT_VALIDATION_ERROR(
"Command cannot be recorded while %s is active.", mCurrentEncoder));
} else {
HandleError(DAWN_FORMAT_VALIDATION_ERROR(
"Recording in an error or already ended %s.", encoder));
}
return false;
}
return true;
}
template <typename EncodeFunction>
inline bool TryEncode(const ApiObjectBase* encoder, EncodeFunction&& encodeFunction) {
if (!CheckCurrentEncoder(encoder)) {
return false;
}
ASSERT(!mWasMovedToIterator);
return !ConsumedError(encodeFunction(&mPendingCommands));
}
template <typename EncodeFunction, typename... Args>
inline bool TryEncode(const ApiObjectBase* encoder,
EncodeFunction&& encodeFunction,
const char* formatStr,
const Args&... args) {
if (!CheckCurrentEncoder(encoder)) {
return false;
}
ASSERT(!mWasMovedToIterator);
return !ConsumedError(encodeFunction(&mPendingCommands), formatStr, args...);
}
// Must be called prior to encoding a BeginRenderPassCmd. Note that it's OK to call this
// and then not actually call EnterPass+ExitRenderPass, for example if some other pass setup
// failed validation before the BeginRenderPassCmd could be encoded.
void WillBeginRenderPass();
// Functions to set current encoder state
void EnterPass(const ApiObjectBase* passEncoder);
MaybeError ExitRenderPass(const ApiObjectBase* passEncoder,
RenderPassResourceUsageTracker usageTracker,
CommandEncoder* commandEncoder,
IndirectDrawMetadata indirectDrawMetadata);
void ExitComputePass(const ApiObjectBase* passEncoder, ComputePassResourceUsage usages);
MaybeError Finish();
// Called when a pass encoder is deleted. Provides an opportunity to clean up if it's the
// mCurrentEncoder.
void EnsurePassExited(const ApiObjectBase* passEncoder);
const RenderPassUsages& GetRenderPassUsages() const;
const ComputePassUsages& GetComputePassUsages() const;
RenderPassUsages AcquireRenderPassUsages();
ComputePassUsages AcquireComputePassUsages();
void PushDebugGroupLabel(const char* groupLabel);
void PopDebugGroupLabel();
private:
void CommitCommands(CommandAllocator allocator);
bool IsFinished() const;
void MoveToIterator();
DeviceBase* mDevice;
// There can only be two levels of encoders. Top-level and render/compute pass.
// The top level encoder is the encoder the EncodingContext is created with.
// It doubles as flag to check if encoding has been Finished.
const ApiObjectBase* mTopLevelEncoder;
// The current encoder must be the same as the encoder provided to TryEncode,
// otherwise an error is produced. It may be nullptr if the EncodingContext is an error.
// The current encoder changes with Enter/ExitPass which should be called by
// CommandEncoder::Begin/EndPass.
const ApiObjectBase* mCurrentEncoder;
RenderPassUsages mRenderPassUsages;
bool mWereRenderPassUsagesAcquired = false;
ComputePassUsages mComputePassUsages;
bool mWereComputePassUsagesAcquired = false;
CommandAllocator mPendingCommands;
std::vector<CommandAllocator> mAllocators;
CommandIterator mIterator;
bool mWasMovedToIterator = false;
bool mWereCommandsAcquired = false;
bool mDestroyed = false;
std::unique_ptr<ErrorData> mError;
std::vector<std::string> mDebugGroupLabels;
};
} // namespace dawn::native
#endif // DAWNNATIVE_ENCODINGCONTEXT_H_