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 #include "dawn_native/EncodingContext.h" 16 17 #include "common/Assert.h" 18 #include "dawn_native/CommandEncoder.h" 19 #include "dawn_native/Commands.h" 20 #include "dawn_native/Device.h" 21 #include "dawn_native/ErrorData.h" 22 #include "dawn_native/IndirectDrawValidationEncoder.h" 23 #include "dawn_native/RenderBundleEncoder.h" 24 25 namespace dawn_native { 26 EncodingContext(DeviceBase * device,const ApiObjectBase * initialEncoder)27 EncodingContext::EncodingContext(DeviceBase* device, const ApiObjectBase* initialEncoder) 28 : mDevice(device), mTopLevelEncoder(initialEncoder), mCurrentEncoder(initialEncoder) { 29 } 30 ~EncodingContext()31 EncodingContext::~EncodingContext() { 32 Destroy(); 33 } 34 Destroy()35 void EncodingContext::Destroy() { 36 if (mDestroyed) { 37 return; 38 } 39 if (!mWereCommandsAcquired) { 40 FreeCommands(GetIterator()); 41 } 42 // If we weren't already finished, then we want to handle an error here so that any calls 43 // to Finish after Destroy will return a meaningful error. 44 if (!IsFinished()) { 45 HandleError(DAWN_FORMAT_VALIDATION_ERROR("Destroyed encoder cannot be finished.")); 46 } 47 mDestroyed = true; 48 mCurrentEncoder = nullptr; 49 } 50 AcquireCommands()51 CommandIterator EncodingContext::AcquireCommands() { 52 MoveToIterator(); 53 ASSERT(!mWereCommandsAcquired); 54 mWereCommandsAcquired = true; 55 return std::move(mIterator); 56 } 57 GetIterator()58 CommandIterator* EncodingContext::GetIterator() { 59 MoveToIterator(); 60 ASSERT(!mWereCommandsAcquired); 61 return &mIterator; 62 } 63 MoveToIterator()64 void EncodingContext::MoveToIterator() { 65 CommitCommands(std::move(mPendingCommands)); 66 if (!mWasMovedToIterator) { 67 mIterator.AcquireCommandBlocks(std::move(mAllocators)); 68 mWasMovedToIterator = true; 69 } 70 } 71 HandleError(std::unique_ptr<ErrorData> error)72 void EncodingContext::HandleError(std::unique_ptr<ErrorData> error) { 73 // Append in reverse so that the most recently set debug group is printed first, like a 74 // call stack. 75 for (auto iter = mDebugGroupLabels.rbegin(); iter != mDebugGroupLabels.rend(); ++iter) { 76 error->AppendDebugGroup(*iter); 77 } 78 79 if (!IsFinished()) { 80 // Encoding should only generate validation errors. 81 ASSERT(error->GetType() == InternalErrorType::Validation); 82 // If the encoding context is not finished, errors are deferred until 83 // Finish() is called. 84 if (mError == nullptr) { 85 mError = std::move(error); 86 } 87 } else { 88 mDevice->HandleError(error->GetType(), error->GetFormattedMessage().c_str()); 89 } 90 } 91 WillBeginRenderPass()92 void EncodingContext::WillBeginRenderPass() { 93 ASSERT(mCurrentEncoder == mTopLevelEncoder); 94 if (mDevice->IsValidationEnabled()) { 95 // When validation is enabled, we are going to want to capture all commands encoded 96 // between and including BeginRenderPassCmd and EndRenderPassCmd, and defer their 97 // sequencing util after we have a chance to insert any necessary validation 98 // commands. To support this we commit any current commands now, so that the 99 // impending BeginRenderPassCmd starts in a fresh CommandAllocator. 100 CommitCommands(std::move(mPendingCommands)); 101 } 102 } 103 EnterPass(const ApiObjectBase * passEncoder)104 void EncodingContext::EnterPass(const ApiObjectBase* passEncoder) { 105 // Assert we're at the top level. 106 ASSERT(mCurrentEncoder == mTopLevelEncoder); 107 ASSERT(passEncoder != nullptr); 108 109 mCurrentEncoder = passEncoder; 110 } 111 ExitRenderPass(const ApiObjectBase * passEncoder,RenderPassResourceUsageTracker usageTracker,CommandEncoder * commandEncoder,IndirectDrawMetadata indirectDrawMetadata)112 MaybeError EncodingContext::ExitRenderPass(const ApiObjectBase* passEncoder, 113 RenderPassResourceUsageTracker usageTracker, 114 CommandEncoder* commandEncoder, 115 IndirectDrawMetadata indirectDrawMetadata) { 116 ASSERT(mCurrentEncoder != mTopLevelEncoder); 117 ASSERT(mCurrentEncoder == passEncoder); 118 119 mCurrentEncoder = mTopLevelEncoder; 120 121 if (mDevice->IsValidationEnabled()) { 122 // With validation enabled, commands were committed just before BeginRenderPassCmd was 123 // encoded by our RenderPassEncoder (see WillBeginRenderPass above). This means 124 // mPendingCommands contains only the commands from BeginRenderPassCmd to 125 // EndRenderPassCmd, inclusive. Now we swap out this allocator with a fresh one to give 126 // the validation encoder a chance to insert its commands first. 127 CommandAllocator renderCommands = std::move(mPendingCommands); 128 DAWN_TRY(EncodeIndirectDrawValidationCommands(mDevice, commandEncoder, &usageTracker, 129 &indirectDrawMetadata)); 130 CommitCommands(std::move(mPendingCommands)); 131 CommitCommands(std::move(renderCommands)); 132 } 133 134 mRenderPassUsages.push_back(usageTracker.AcquireResourceUsage()); 135 return {}; 136 } 137 ExitComputePass(const ApiObjectBase * passEncoder,ComputePassResourceUsage usages)138 void EncodingContext::ExitComputePass(const ApiObjectBase* passEncoder, 139 ComputePassResourceUsage usages) { 140 ASSERT(mCurrentEncoder != mTopLevelEncoder); 141 ASSERT(mCurrentEncoder == passEncoder); 142 143 mCurrentEncoder = mTopLevelEncoder; 144 mComputePassUsages.push_back(std::move(usages)); 145 } 146 EnsurePassExited(const ApiObjectBase * passEncoder)147 void EncodingContext::EnsurePassExited(const ApiObjectBase* passEncoder) { 148 if (mCurrentEncoder != mTopLevelEncoder && mCurrentEncoder == passEncoder) { 149 // The current pass encoder is being deleted. Implicitly end the pass with an error. 150 mCurrentEncoder = mTopLevelEncoder; 151 HandleError(DAWN_FORMAT_VALIDATION_ERROR( 152 "Command buffer recording ended before %s was ended.", passEncoder)); 153 } 154 } 155 GetRenderPassUsages() const156 const RenderPassUsages& EncodingContext::GetRenderPassUsages() const { 157 ASSERT(!mWereRenderPassUsagesAcquired); 158 return mRenderPassUsages; 159 } 160 AcquireRenderPassUsages()161 RenderPassUsages EncodingContext::AcquireRenderPassUsages() { 162 ASSERT(!mWereRenderPassUsagesAcquired); 163 mWereRenderPassUsagesAcquired = true; 164 return std::move(mRenderPassUsages); 165 } 166 GetComputePassUsages() const167 const ComputePassUsages& EncodingContext::GetComputePassUsages() const { 168 ASSERT(!mWereComputePassUsagesAcquired); 169 return mComputePassUsages; 170 } 171 AcquireComputePassUsages()172 ComputePassUsages EncodingContext::AcquireComputePassUsages() { 173 ASSERT(!mWereComputePassUsagesAcquired); 174 mWereComputePassUsagesAcquired = true; 175 return std::move(mComputePassUsages); 176 } 177 PushDebugGroupLabel(const char * groupLabel)178 void EncodingContext::PushDebugGroupLabel(const char* groupLabel) { 179 mDebugGroupLabels.emplace_back(groupLabel); 180 } 181 PopDebugGroupLabel()182 void EncodingContext::PopDebugGroupLabel() { 183 mDebugGroupLabels.pop_back(); 184 } 185 Finish()186 MaybeError EncodingContext::Finish() { 187 DAWN_INVALID_IF(IsFinished(), "Command encoding already finished."); 188 189 const ApiObjectBase* currentEncoder = mCurrentEncoder; 190 const ApiObjectBase* topLevelEncoder = mTopLevelEncoder; 191 192 // Even if finish validation fails, it is now invalid to call any encoding commands, 193 // so we clear the encoders. Note: mTopLevelEncoder == nullptr is used as a flag for 194 // if Finish() has been called. 195 mCurrentEncoder = nullptr; 196 mTopLevelEncoder = nullptr; 197 CommitCommands(std::move(mPendingCommands)); 198 199 if (mError != nullptr) { 200 return std::move(mError); 201 } 202 DAWN_INVALID_IF(currentEncoder != topLevelEncoder, 203 "Command buffer recording ended before %s was ended.", currentEncoder); 204 return {}; 205 } 206 CommitCommands(CommandAllocator allocator)207 void EncodingContext::CommitCommands(CommandAllocator allocator) { 208 if (!allocator.IsEmpty()) { 209 mAllocators.push_back(std::move(allocator)); 210 } 211 } 212 IsFinished() const213 bool EncodingContext::IsFinished() const { 214 return mTopLevelEncoder == nullptr; 215 } 216 217 } // namespace dawn_native 218