1 // Copyright 2019 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef DAWNNATIVE_ENCODINGCONTEXT_H_ 16 #define DAWNNATIVE_ENCODINGCONTEXT_H_ 17 18 #include "dawn_native/CommandAllocator.h" 19 #include "dawn_native/Error.h" 20 #include "dawn_native/ErrorData.h" 21 #include "dawn_native/IndirectDrawMetadata.h" 22 #include "dawn_native/PassResourceUsageTracker.h" 23 #include "dawn_native/dawn_platform.h" 24 25 #include <string> 26 27 namespace dawn_native { 28 29 class CommandEncoder; 30 class DeviceBase; 31 class ApiObjectBase; 32 33 // Base class for allocating/iterating commands. 34 // It performs error tracking as well as encoding state for render/compute passes. 35 class EncodingContext { 36 public: 37 EncodingContext(DeviceBase* device, const ApiObjectBase* initialEncoder); 38 ~EncodingContext(); 39 40 // Marks the encoding context as destroyed so that any future encodes will fail, and all 41 // encoded commands are released. 42 void Destroy(); 43 44 CommandIterator AcquireCommands(); 45 CommandIterator* GetIterator(); 46 47 // Functions to handle encoder errors 48 void HandleError(std::unique_ptr<ErrorData> error); 49 ConsumedError(MaybeError maybeError)50 inline bool ConsumedError(MaybeError maybeError) { 51 if (DAWN_UNLIKELY(maybeError.IsError())) { 52 HandleError(maybeError.AcquireError()); 53 return true; 54 } 55 return false; 56 } 57 58 template <typename... Args> ConsumedError(MaybeError maybeError,const char * formatStr,const Args &...args)59 inline bool ConsumedError(MaybeError maybeError, 60 const char* formatStr, 61 const Args&... args) { 62 if (DAWN_UNLIKELY(maybeError.IsError())) { 63 std::unique_ptr<ErrorData> error = maybeError.AcquireError(); 64 if (error->GetType() == InternalErrorType::Validation) { 65 std::string out; 66 absl::UntypedFormatSpec format(formatStr); 67 if (absl::FormatUntyped(&out, format, {absl::FormatArg(args)...})) { 68 error->AppendContext(std::move(out)); 69 } else { 70 error->AppendContext(absl::StrFormat( 71 "[Failed to format error message: \"%s\"].", formatStr)); 72 } 73 } 74 HandleError(std::move(error)); 75 return true; 76 } 77 return false; 78 } 79 CheckCurrentEncoder(const ApiObjectBase * encoder)80 inline bool CheckCurrentEncoder(const ApiObjectBase* encoder) { 81 if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) { 82 if (mDestroyed) { 83 HandleError( 84 DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", encoder)); 85 } else if (mCurrentEncoder != mTopLevelEncoder) { 86 // The top level encoder was used when a pass encoder was current. 87 HandleError(DAWN_FORMAT_VALIDATION_ERROR( 88 "Command cannot be recorded while %s is active.", mCurrentEncoder)); 89 } else { 90 HandleError(DAWN_FORMAT_VALIDATION_ERROR( 91 "Recording in an error or already ended %s.", encoder)); 92 } 93 return false; 94 } 95 return true; 96 } 97 98 template <typename EncodeFunction> TryEncode(const ApiObjectBase * encoder,EncodeFunction && encodeFunction)99 inline bool TryEncode(const ApiObjectBase* encoder, EncodeFunction&& encodeFunction) { 100 if (!CheckCurrentEncoder(encoder)) { 101 return false; 102 } 103 ASSERT(!mWasMovedToIterator); 104 return !ConsumedError(encodeFunction(&mPendingCommands)); 105 } 106 107 template <typename EncodeFunction, typename... Args> TryEncode(const ApiObjectBase * encoder,EncodeFunction && encodeFunction,const char * formatStr,const Args &...args)108 inline bool TryEncode(const ApiObjectBase* encoder, 109 EncodeFunction&& encodeFunction, 110 const char* formatStr, 111 const Args&... args) { 112 if (!CheckCurrentEncoder(encoder)) { 113 return false; 114 } 115 ASSERT(!mWasMovedToIterator); 116 return !ConsumedError(encodeFunction(&mPendingCommands), formatStr, args...); 117 } 118 119 // Must be called prior to encoding a BeginRenderPassCmd. Note that it's OK to call this 120 // and then not actually call EnterPass+ExitRenderPass, for example if some other pass setup 121 // failed validation before the BeginRenderPassCmd could be encoded. 122 void WillBeginRenderPass(); 123 124 // Functions to set current encoder state 125 void EnterPass(const ApiObjectBase* passEncoder); 126 MaybeError ExitRenderPass(const ApiObjectBase* passEncoder, 127 RenderPassResourceUsageTracker usageTracker, 128 CommandEncoder* commandEncoder, 129 IndirectDrawMetadata indirectDrawMetadata); 130 void ExitComputePass(const ApiObjectBase* passEncoder, ComputePassResourceUsage usages); 131 MaybeError Finish(); 132 133 // Called when a pass encoder is deleted. Provides an opportunity to clean up if it's the 134 // mCurrentEncoder. 135 void EnsurePassExited(const ApiObjectBase* passEncoder); 136 137 const RenderPassUsages& GetRenderPassUsages() const; 138 const ComputePassUsages& GetComputePassUsages() const; 139 RenderPassUsages AcquireRenderPassUsages(); 140 ComputePassUsages AcquireComputePassUsages(); 141 142 void PushDebugGroupLabel(const char* groupLabel); 143 void PopDebugGroupLabel(); 144 145 private: 146 void CommitCommands(CommandAllocator allocator); 147 148 bool IsFinished() const; 149 void MoveToIterator(); 150 151 DeviceBase* mDevice; 152 153 // There can only be two levels of encoders. Top-level and render/compute pass. 154 // The top level encoder is the encoder the EncodingContext is created with. 155 // It doubles as flag to check if encoding has been Finished. 156 const ApiObjectBase* mTopLevelEncoder; 157 // The current encoder must be the same as the encoder provided to TryEncode, 158 // otherwise an error is produced. It may be nullptr if the EncodingContext is an error. 159 // The current encoder changes with Enter/ExitPass which should be called by 160 // CommandEncoder::Begin/EndPass. 161 const ApiObjectBase* mCurrentEncoder; 162 163 RenderPassUsages mRenderPassUsages; 164 bool mWereRenderPassUsagesAcquired = false; 165 ComputePassUsages mComputePassUsages; 166 bool mWereComputePassUsagesAcquired = false; 167 168 CommandAllocator mPendingCommands; 169 170 std::vector<CommandAllocator> mAllocators; 171 CommandIterator mIterator; 172 bool mWasMovedToIterator = false; 173 bool mWereCommandsAcquired = false; 174 bool mDestroyed = false; 175 176 std::unique_ptr<ErrorData> mError; 177 std::vector<std::string> mDebugGroupLabels; 178 }; 179 180 } // namespace dawn_native 181 182 #endif // DAWNNATIVE_ENCODINGCONTEXT_H_ 183