• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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/RenderPassEncoder.h"
16 
17 #include "common/Constants.h"
18 #include "dawn_native/Buffer.h"
19 #include "dawn_native/CommandEncoder.h"
20 #include "dawn_native/CommandValidation.h"
21 #include "dawn_native/Commands.h"
22 #include "dawn_native/Device.h"
23 #include "dawn_native/ObjectType_autogen.h"
24 #include "dawn_native/QuerySet.h"
25 #include "dawn_native/RenderBundle.h"
26 #include "dawn_native/RenderPipeline.h"
27 
28 #include <math.h>
29 #include <cstring>
30 
31 namespace dawn_native {
32     namespace {
33 
34         // Check the query at queryIndex is unavailable, otherwise it cannot be written.
ValidateQueryIndexOverwrite(QuerySetBase * querySet,uint32_t queryIndex,const QueryAvailabilityMap & queryAvailabilityMap)35         MaybeError ValidateQueryIndexOverwrite(QuerySetBase* querySet,
36                                                uint32_t queryIndex,
37                                                const QueryAvailabilityMap& queryAvailabilityMap) {
38             auto it = queryAvailabilityMap.find(querySet);
39             DAWN_INVALID_IF(it != queryAvailabilityMap.end() && it->second[queryIndex],
40                             "Query index %u of %s is written to twice in a render pass.",
41                             queryIndex, querySet);
42 
43             return {};
44         }
45 
46     }  // namespace
47 
48     // The usage tracker is passed in here, because it is prepopulated with usages from the
49     // BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the
50     // command, then this wouldn't be necessary.
RenderPassEncoder(DeviceBase * device,const RenderPassDescriptor * descriptor,CommandEncoder * commandEncoder,EncodingContext * encodingContext,RenderPassResourceUsageTracker usageTracker,Ref<AttachmentState> attachmentState,QuerySetBase * occlusionQuerySet,uint32_t renderTargetWidth,uint32_t renderTargetHeight,bool depthReadOnly,bool stencilReadOnly)51     RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
52                                          const RenderPassDescriptor* descriptor,
53                                          CommandEncoder* commandEncoder,
54                                          EncodingContext* encodingContext,
55                                          RenderPassResourceUsageTracker usageTracker,
56                                          Ref<AttachmentState> attachmentState,
57                                          QuerySetBase* occlusionQuerySet,
58                                          uint32_t renderTargetWidth,
59                                          uint32_t renderTargetHeight,
60                                          bool depthReadOnly,
61                                          bool stencilReadOnly)
62         : RenderEncoderBase(device,
63                             descriptor->label,
64                             encodingContext,
65                             std::move(attachmentState),
66                             depthReadOnly,
67                             stencilReadOnly),
68           mCommandEncoder(commandEncoder),
69           mRenderTargetWidth(renderTargetWidth),
70           mRenderTargetHeight(renderTargetHeight),
71           mOcclusionQuerySet(occlusionQuerySet) {
72         mUsageTracker = std::move(usageTracker);
73         TrackInDevice();
74     }
75 
RenderPassEncoder(DeviceBase * device,CommandEncoder * commandEncoder,EncodingContext * encodingContext,ErrorTag errorTag)76     RenderPassEncoder::RenderPassEncoder(DeviceBase* device,
77                                          CommandEncoder* commandEncoder,
78                                          EncodingContext* encodingContext,
79                                          ErrorTag errorTag)
80         : RenderEncoderBase(device, encodingContext, errorTag), mCommandEncoder(commandEncoder) {
81     }
82 
MakeError(DeviceBase * device,CommandEncoder * commandEncoder,EncodingContext * encodingContext)83     RenderPassEncoder* RenderPassEncoder::MakeError(DeviceBase* device,
84                                                     CommandEncoder* commandEncoder,
85                                                     EncodingContext* encodingContext) {
86         return new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError);
87     }
88 
DestroyImpl()89     void RenderPassEncoder::DestroyImpl() {
90         RenderEncoderBase::DestroyImpl();
91         // Ensure that the pass has exited. This is done for passes only since validation requires
92         // they exit before destruction while bundles do not.
93         mEncodingContext->EnsurePassExited(this);
94     }
95 
GetType() const96     ObjectType RenderPassEncoder::GetType() const {
97         return ObjectType::RenderPassEncoder;
98     }
99 
TrackQueryAvailability(QuerySetBase * querySet,uint32_t queryIndex)100     void RenderPassEncoder::TrackQueryAvailability(QuerySetBase* querySet, uint32_t queryIndex) {
101         DAWN_ASSERT(querySet != nullptr);
102 
103         // Track the query availability with true on render pass for rewrite validation and query
104         // reset on render pass on Vulkan
105         mUsageTracker.TrackQueryAvailability(querySet, queryIndex);
106 
107         // Track it again on command encoder for zero-initializing when resolving unused queries.
108         mCommandEncoder->TrackQueryAvailability(querySet, queryIndex);
109     }
110 
APIEndPass()111     void RenderPassEncoder::APIEndPass() {
112         if (mEncodingContext->TryEncode(
113                 this,
114                 [&](CommandAllocator* allocator) -> MaybeError {
115                     if (IsValidationEnabled()) {
116                         DAWN_TRY(ValidateProgrammableEncoderEnd());
117 
118                         DAWN_INVALID_IF(
119                             mOcclusionQueryActive,
120                             "Render pass %s ended with incomplete occlusion query index %u of %s.",
121                             this, mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get());
122                     }
123 
124                     allocator->Allocate<EndRenderPassCmd>(Command::EndRenderPass);
125                     DAWN_TRY(mEncodingContext->ExitRenderPass(this, std::move(mUsageTracker),
126                                                               mCommandEncoder.Get(),
127                                                               std::move(mIndirectDrawMetadata)));
128                     return {};
129                 },
130                 "encoding %s.EndPass().", this)) {
131         }
132     }
133 
APISetStencilReference(uint32_t reference)134     void RenderPassEncoder::APISetStencilReference(uint32_t reference) {
135         mEncodingContext->TryEncode(
136             this,
137             [&](CommandAllocator* allocator) -> MaybeError {
138                 SetStencilReferenceCmd* cmd =
139                     allocator->Allocate<SetStencilReferenceCmd>(Command::SetStencilReference);
140                 cmd->reference = reference;
141 
142                 return {};
143             },
144             "encoding %s.SetStencilReference(%u).", this, reference);
145     }
146 
APISetBlendConstant(const Color * color)147     void RenderPassEncoder::APISetBlendConstant(const Color* color) {
148         mEncodingContext->TryEncode(
149             this,
150             [&](CommandAllocator* allocator) -> MaybeError {
151                 SetBlendConstantCmd* cmd =
152                     allocator->Allocate<SetBlendConstantCmd>(Command::SetBlendConstant);
153                 cmd->color = *color;
154 
155                 return {};
156             },
157             "encoding %s.SetBlendConstant(%s).", this, color);
158     }
159 
APISetViewport(float x,float y,float width,float height,float minDepth,float maxDepth)160     void RenderPassEncoder::APISetViewport(float x,
161                                            float y,
162                                            float width,
163                                            float height,
164                                            float minDepth,
165                                            float maxDepth) {
166         mEncodingContext->TryEncode(
167             this,
168             [&](CommandAllocator* allocator) -> MaybeError {
169                 if (IsValidationEnabled()) {
170                     DAWN_INVALID_IF(
171                         (isnan(x) || isnan(y) || isnan(width) || isnan(height) || isnan(minDepth) ||
172                          isnan(maxDepth)),
173                         "A parameter of the viewport (x: %f, y: %f, width: %f, height: %f, "
174                         "minDepth: %f, maxDepth: %f) is NaN.",
175                         x, y, width, height, minDepth, maxDepth);
176 
177                     DAWN_INVALID_IF(
178                         x < 0 || y < 0 || width < 0 || height < 0,
179                         "Viewport bounds (x: %f, y: %f, width: %f, height: %f) contains a negative "
180                         "value.",
181                         x, y, width, height);
182 
183                     DAWN_INVALID_IF(
184                         x + width > mRenderTargetWidth || y + height > mRenderTargetHeight,
185                         "Viewport bounds (x: %f, y: %f, width: %f, height: %f) are not contained "
186                         "in "
187                         "the render target dimensions (%u x %u).",
188                         x, y, width, height, mRenderTargetWidth, mRenderTargetHeight);
189 
190                     // Check for depths being in [0, 1] and min <= max in 3 checks instead of 5.
191                     DAWN_INVALID_IF(minDepth < 0 || minDepth > maxDepth || maxDepth > 1,
192                                     "Viewport minDepth (%f) and maxDepth (%f) are not in [0, 1] or "
193                                     "minDepth was "
194                                     "greater than maxDepth.",
195                                     minDepth, maxDepth);
196                 }
197 
198                 SetViewportCmd* cmd = allocator->Allocate<SetViewportCmd>(Command::SetViewport);
199                 cmd->x = x;
200                 cmd->y = y;
201                 cmd->width = width;
202                 cmd->height = height;
203                 cmd->minDepth = minDepth;
204                 cmd->maxDepth = maxDepth;
205 
206                 return {};
207             },
208             "encoding %s.SetViewport(%f, %f, %f, %f, %f, %f).", this, x, y, width, height, minDepth,
209             maxDepth);
210     }
211 
APISetScissorRect(uint32_t x,uint32_t y,uint32_t width,uint32_t height)212     void RenderPassEncoder::APISetScissorRect(uint32_t x,
213                                               uint32_t y,
214                                               uint32_t width,
215                                               uint32_t height) {
216         mEncodingContext->TryEncode(
217             this,
218             [&](CommandAllocator* allocator) -> MaybeError {
219                 if (IsValidationEnabled()) {
220                     DAWN_INVALID_IF(
221                         width > mRenderTargetWidth || height > mRenderTargetHeight ||
222                             x > mRenderTargetWidth - width || y > mRenderTargetHeight - height,
223                         "Scissor rect (x: %u, y: %u, width: %u, height: %u) is not contained in "
224                         "the render target dimensions (%u x %u).",
225                         x, y, width, height, mRenderTargetWidth, mRenderTargetHeight);
226                 }
227 
228                 SetScissorRectCmd* cmd =
229                     allocator->Allocate<SetScissorRectCmd>(Command::SetScissorRect);
230                 cmd->x = x;
231                 cmd->y = y;
232                 cmd->width = width;
233                 cmd->height = height;
234 
235                 return {};
236             },
237             "encoding %s.SetScissorRect(%u, %u, %u, %u).", this, x, y, width, height);
238     }
239 
APIExecuteBundles(uint32_t count,RenderBundleBase * const * renderBundles)240     void RenderPassEncoder::APIExecuteBundles(uint32_t count,
241                                               RenderBundleBase* const* renderBundles) {
242         mEncodingContext->TryEncode(
243             this,
244             [&](CommandAllocator* allocator) -> MaybeError {
245                 if (IsValidationEnabled()) {
246                     const AttachmentState* attachmentState = GetAttachmentState();
247                     bool depthReadOnlyInPass = IsDepthReadOnly();
248                     bool stencilReadOnlyInPass = IsStencilReadOnly();
249                     for (uint32_t i = 0; i < count; ++i) {
250                         DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i]));
251 
252                         // TODO(dawn:563): Give more detail about why the states are incompatible.
253                         DAWN_INVALID_IF(
254                             attachmentState != renderBundles[i]->GetAttachmentState(),
255                             "Attachment state of renderBundles[%i] (%s) is not compatible with "
256                             "attachment state of %s.",
257                             i, renderBundles[i], this);
258 
259                         bool depthReadOnlyInBundle = renderBundles[i]->IsDepthReadOnly();
260                         DAWN_INVALID_IF(
261                             depthReadOnlyInPass && !depthReadOnlyInBundle,
262                             "DepthReadOnly (%u) of renderBundle[%i] (%s) is not compatible "
263                             "with DepthReadOnly (%u) of %s.",
264                             depthReadOnlyInBundle, i, renderBundles[i], depthReadOnlyInPass, this);
265 
266                         bool stencilReadOnlyInBundle = renderBundles[i]->IsStencilReadOnly();
267                         DAWN_INVALID_IF(stencilReadOnlyInPass && !stencilReadOnlyInBundle,
268                                         "StencilReadOnly (%u) of renderBundle[%i] (%s) is not "
269                                         "compatible with StencilReadOnly (%u) of %s.",
270                                         stencilReadOnlyInBundle, i, renderBundles[i],
271                                         stencilReadOnlyInPass, this);
272                     }
273                 }
274 
275                 mCommandBufferState = CommandBufferStateTracker{};
276 
277                 ExecuteBundlesCmd* cmd =
278                     allocator->Allocate<ExecuteBundlesCmd>(Command::ExecuteBundles);
279                 cmd->count = count;
280 
281                 Ref<RenderBundleBase>* bundles =
282                     allocator->AllocateData<Ref<RenderBundleBase>>(count);
283                 for (uint32_t i = 0; i < count; ++i) {
284                     bundles[i] = renderBundles[i];
285 
286                     const RenderPassResourceUsage& usages = bundles[i]->GetResourceUsage();
287                     for (uint32_t i = 0; i < usages.buffers.size(); ++i) {
288                         mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
289                     }
290 
291                     for (uint32_t i = 0; i < usages.textures.size(); ++i) {
292                         mUsageTracker.AddRenderBundleTextureUsage(usages.textures[i],
293                                                                   usages.textureUsages[i]);
294                     }
295 
296                     if (IsValidationEnabled()) {
297                         mIndirectDrawMetadata.AddBundle(renderBundles[i]);
298                     }
299                 }
300 
301                 return {};
302             },
303             "encoding %s.ExecuteBundles(%u, ...).", this, count);
304     }
305 
APIBeginOcclusionQuery(uint32_t queryIndex)306     void RenderPassEncoder::APIBeginOcclusionQuery(uint32_t queryIndex) {
307         mEncodingContext->TryEncode(
308             this,
309             [&](CommandAllocator* allocator) -> MaybeError {
310                 if (IsValidationEnabled()) {
311                     DAWN_INVALID_IF(mOcclusionQuerySet.Get() == nullptr,
312                                     "The occlusionQuerySet in RenderPassDescriptor is not set.");
313 
314                     // The type of querySet has been validated by ValidateRenderPassDescriptor
315 
316                     DAWN_INVALID_IF(queryIndex >= mOcclusionQuerySet->GetQueryCount(),
317                                     "Query index (%u) exceeds the number of queries (%u) in %s.",
318                                     queryIndex, mOcclusionQuerySet->GetQueryCount(),
319                                     mOcclusionQuerySet.Get());
320 
321                     DAWN_INVALID_IF(mOcclusionQueryActive,
322                                     "An occlusion query (%u) in %s is already active.",
323                                     mCurrentOcclusionQueryIndex, mOcclusionQuerySet.Get());
324 
325                     DAWN_TRY_CONTEXT(
326                         ValidateQueryIndexOverwrite(mOcclusionQuerySet.Get(), queryIndex,
327                                                     mUsageTracker.GetQueryAvailabilityMap()),
328                         "validating the occlusion query index (%u) in %s", queryIndex,
329                         mOcclusionQuerySet.Get());
330                 }
331 
332                 // Record the current query index for endOcclusionQuery.
333                 mCurrentOcclusionQueryIndex = queryIndex;
334                 mOcclusionQueryActive = true;
335 
336                 BeginOcclusionQueryCmd* cmd =
337                     allocator->Allocate<BeginOcclusionQueryCmd>(Command::BeginOcclusionQuery);
338                 cmd->querySet = mOcclusionQuerySet.Get();
339                 cmd->queryIndex = queryIndex;
340 
341                 return {};
342             },
343             "encoding %s.BeginOcclusionQuery(%u).", this, queryIndex);
344     }
345 
APIEndOcclusionQuery()346     void RenderPassEncoder::APIEndOcclusionQuery() {
347         mEncodingContext->TryEncode(
348             this,
349             [&](CommandAllocator* allocator) -> MaybeError {
350                 if (IsValidationEnabled()) {
351                     DAWN_INVALID_IF(!mOcclusionQueryActive, "No occlusion queries are active.");
352                 }
353 
354                 TrackQueryAvailability(mOcclusionQuerySet.Get(), mCurrentOcclusionQueryIndex);
355 
356                 mOcclusionQueryActive = false;
357 
358                 EndOcclusionQueryCmd* cmd =
359                     allocator->Allocate<EndOcclusionQueryCmd>(Command::EndOcclusionQuery);
360                 cmd->querySet = mOcclusionQuerySet.Get();
361                 cmd->queryIndex = mCurrentOcclusionQueryIndex;
362 
363                 return {};
364             },
365             "encoding %s.EndOcclusionQuery().", this);
366     }
367 
APIWriteTimestamp(QuerySetBase * querySet,uint32_t queryIndex)368     void RenderPassEncoder::APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) {
369         mEncodingContext->TryEncode(
370             this,
371             [&](CommandAllocator* allocator) -> MaybeError {
372                 if (IsValidationEnabled()) {
373                     DAWN_TRY(GetDevice()->ValidateObject(querySet));
374                     DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex));
375                     DAWN_TRY_CONTEXT(
376                         ValidateQueryIndexOverwrite(querySet, queryIndex,
377                                                     mUsageTracker.GetQueryAvailabilityMap()),
378                         "validating the timestamp query index (%u) of %s", queryIndex, querySet);
379                 }
380 
381                 TrackQueryAvailability(querySet, queryIndex);
382 
383                 WriteTimestampCmd* cmd =
384                     allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp);
385                 cmd->querySet = querySet;
386                 cmd->queryIndex = queryIndex;
387 
388                 return {};
389             },
390             "encoding %s.WriteTimestamp(%s, %u).", this, querySet, queryIndex);
391     }
392 
393 }  // namespace dawn_native
394