• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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