• 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/RenderEncoderBase.h"
16 
17 #include "common/Constants.h"
18 #include "common/Log.h"
19 #include "dawn_native/Buffer.h"
20 #include "dawn_native/CommandEncoder.h"
21 #include "dawn_native/CommandValidation.h"
22 #include "dawn_native/Commands.h"
23 #include "dawn_native/Device.h"
24 #include "dawn_native/RenderPipeline.h"
25 #include "dawn_native/ValidationUtils_autogen.h"
26 
27 #include <math.h>
28 #include <cstring>
29 
30 namespace dawn_native {
31 
RenderEncoderBase(DeviceBase * device,const char * label,EncodingContext * encodingContext,Ref<AttachmentState> attachmentState,bool depthReadOnly,bool stencilReadOnly)32     RenderEncoderBase::RenderEncoderBase(DeviceBase* device,
33                                          const char* label,
34                                          EncodingContext* encodingContext,
35                                          Ref<AttachmentState> attachmentState,
36                                          bool depthReadOnly,
37                                          bool stencilReadOnly)
38         : ProgrammableEncoder(device, label, encodingContext),
39           mIndirectDrawMetadata(device->GetLimits()),
40           mAttachmentState(std::move(attachmentState)),
41           mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)),
42           mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) {
43         mDepthReadOnly = depthReadOnly;
44         mStencilReadOnly = stencilReadOnly;
45     }
46 
RenderEncoderBase(DeviceBase * device,EncodingContext * encodingContext,ErrorTag errorTag)47     RenderEncoderBase::RenderEncoderBase(DeviceBase* device,
48                                          EncodingContext* encodingContext,
49                                          ErrorTag errorTag)
50         : ProgrammableEncoder(device, encodingContext, errorTag),
51           mIndirectDrawMetadata(device->GetLimits()),
52           mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)),
53           mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) {
54     }
55 
DestroyImpl()56     void RenderEncoderBase::DestroyImpl() {
57         // Remove reference to the attachment state so that we don't have lingering references to
58         // it preventing it from being uncached in the device.
59         mAttachmentState = nullptr;
60     }
61 
GetAttachmentState() const62     const AttachmentState* RenderEncoderBase::GetAttachmentState() const {
63         ASSERT(!IsError());
64         ASSERT(mAttachmentState != nullptr);
65         return mAttachmentState.Get();
66     }
67 
IsDepthReadOnly() const68     bool RenderEncoderBase::IsDepthReadOnly() const {
69         ASSERT(!IsError());
70         return mDepthReadOnly;
71     }
72 
IsStencilReadOnly() const73     bool RenderEncoderBase::IsStencilReadOnly() const {
74         ASSERT(!IsError());
75         return mStencilReadOnly;
76     }
77 
AcquireAttachmentState()78     Ref<AttachmentState> RenderEncoderBase::AcquireAttachmentState() {
79         return std::move(mAttachmentState);
80     }
81 
APIDraw(uint32_t vertexCount,uint32_t instanceCount,uint32_t firstVertex,uint32_t firstInstance)82     void RenderEncoderBase::APIDraw(uint32_t vertexCount,
83                                     uint32_t instanceCount,
84                                     uint32_t firstVertex,
85                                     uint32_t firstInstance) {
86         mEncodingContext->TryEncode(
87             this,
88             [&](CommandAllocator* allocator) -> MaybeError {
89                 if (IsValidationEnabled()) {
90                     DAWN_TRY(mCommandBufferState.ValidateCanDraw());
91 
92                     DAWN_INVALID_IF(mDisableBaseInstance && firstInstance != 0,
93                                     "First instance (%u) must be zero.", firstInstance);
94 
95                     DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForVertexBuffer(vertexCount,
96                                                                                       firstVertex));
97                     DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForInstanceBuffer(
98                         instanceCount, firstInstance));
99                 }
100 
101                 DrawCmd* draw = allocator->Allocate<DrawCmd>(Command::Draw);
102                 draw->vertexCount = vertexCount;
103                 draw->instanceCount = instanceCount;
104                 draw->firstVertex = firstVertex;
105                 draw->firstInstance = firstInstance;
106 
107                 return {};
108             },
109             "encoding %s.Draw(%u, %u, %u, %u).", this, vertexCount, instanceCount, firstVertex,
110             firstInstance);
111     }
112 
APIDrawIndexed(uint32_t indexCount,uint32_t instanceCount,uint32_t firstIndex,int32_t baseVertex,uint32_t firstInstance)113     void RenderEncoderBase::APIDrawIndexed(uint32_t indexCount,
114                                            uint32_t instanceCount,
115                                            uint32_t firstIndex,
116                                            int32_t baseVertex,
117                                            uint32_t firstInstance) {
118         mEncodingContext->TryEncode(
119             this,
120             [&](CommandAllocator* allocator) -> MaybeError {
121                 if (IsValidationEnabled()) {
122                     DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed());
123 
124                     DAWN_INVALID_IF(mDisableBaseInstance && firstInstance != 0,
125                                     "First instance (%u) must be zero.", firstInstance);
126 
127                     DAWN_INVALID_IF(mDisableBaseVertex && baseVertex != 0,
128                                     "Base vertex (%u) must be zero.", baseVertex);
129 
130                     DAWN_TRY(
131                         mCommandBufferState.ValidateIndexBufferInRange(indexCount, firstIndex));
132 
133                     // Although we don't know actual vertex access range in CPU, we still call the
134                     // ValidateBufferInRangeForVertexBuffer in order to deal with those vertex step
135                     // mode vertex buffer with an array stride of zero.
136                     DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForVertexBuffer(0, 0));
137                     DAWN_TRY(mCommandBufferState.ValidateBufferInRangeForInstanceBuffer(
138                         instanceCount, firstInstance));
139                 }
140 
141                 DrawIndexedCmd* draw = allocator->Allocate<DrawIndexedCmd>(Command::DrawIndexed);
142                 draw->indexCount = indexCount;
143                 draw->instanceCount = instanceCount;
144                 draw->firstIndex = firstIndex;
145                 draw->baseVertex = baseVertex;
146                 draw->firstInstance = firstInstance;
147 
148                 return {};
149             },
150             "encoding %s.DrawIndexed(%u, %u, %u, %i, %u).", this, indexCount, instanceCount,
151             firstIndex, baseVertex, firstInstance);
152     }
153 
APIDrawIndirect(BufferBase * indirectBuffer,uint64_t indirectOffset)154     void RenderEncoderBase::APIDrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) {
155         mEncodingContext->TryEncode(
156             this,
157             [&](CommandAllocator* allocator) -> MaybeError {
158                 if (IsValidationEnabled()) {
159                     DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer));
160                     DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
161                     DAWN_TRY(mCommandBufferState.ValidateCanDraw());
162 
163                     DAWN_INVALID_IF(indirectOffset % 4 != 0,
164                                     "Indirect offset (%u) is not a multiple of 4.", indirectOffset);
165 
166                     DAWN_INVALID_IF(
167                         indirectOffset >= indirectBuffer->GetSize() ||
168                             kDrawIndirectSize > indirectBuffer->GetSize() - indirectOffset,
169                         "Indirect offset (%u) is out of bounds of indirect buffer %s size (%u).",
170                         indirectOffset, indirectBuffer, indirectBuffer->GetSize());
171                 }
172 
173                 DrawIndirectCmd* cmd = allocator->Allocate<DrawIndirectCmd>(Command::DrawIndirect);
174                 cmd->indirectBuffer = indirectBuffer;
175                 cmd->indirectOffset = indirectOffset;
176 
177                 mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
178 
179                 return {};
180             },
181             "encoding %s.DrawIndirect(%s, %u).", this, indirectBuffer, indirectOffset);
182     }
183 
APIDrawIndexedIndirect(BufferBase * indirectBuffer,uint64_t indirectOffset)184     void RenderEncoderBase::APIDrawIndexedIndirect(BufferBase* indirectBuffer,
185                                                    uint64_t indirectOffset) {
186         mEncodingContext->TryEncode(
187             this,
188             [&](CommandAllocator* allocator) -> MaybeError {
189                 if (IsValidationEnabled()) {
190                     DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer));
191                     DAWN_TRY(ValidateCanUseAs(indirectBuffer, wgpu::BufferUsage::Indirect));
192                     DAWN_TRY(mCommandBufferState.ValidateCanDrawIndexed());
193 
194                     DAWN_INVALID_IF(indirectOffset % 4 != 0,
195                                     "Indirect offset (%u) is not a multiple of 4.", indirectOffset);
196 
197                     DAWN_INVALID_IF(
198                         (indirectOffset >= indirectBuffer->GetSize() ||
199                          kDrawIndexedIndirectSize > indirectBuffer->GetSize() - indirectOffset),
200                         "Indirect offset (%u) is out of bounds of indirect buffer %s size (%u).",
201                         indirectOffset, indirectBuffer, indirectBuffer->GetSize());
202                 }
203 
204                 DrawIndexedIndirectCmd* cmd =
205                     allocator->Allocate<DrawIndexedIndirectCmd>(Command::DrawIndexedIndirect);
206                 if (IsValidationEnabled()) {
207                     // Later, EncodeIndirectDrawValidationCommands will allocate a scratch storage
208                     // buffer which will store the validated indirect data. The buffer and offset
209                     // will be updated to point to it.
210                     // |EncodeIndirectDrawValidationCommands| is called at the end of encoding the
211                     // render pass, while the |cmd| pointer is still valid.
212                     cmd->indirectBuffer = nullptr;
213 
214                     mIndirectDrawMetadata.AddIndexedIndirectDraw(
215                         mCommandBufferState.GetIndexFormat(),
216                         mCommandBufferState.GetIndexBufferSize(), indirectBuffer, indirectOffset,
217                         cmd);
218                 } else {
219                     cmd->indirectBuffer = indirectBuffer;
220                     cmd->indirectOffset = indirectOffset;
221                 }
222 
223                 // TODO(crbug.com/dawn/1166): Adding the indirectBuffer is needed for correct usage
224                 // validation, but it will unecessarily transition to indirectBuffer usage in the
225                 // backend.
226                 mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect);
227 
228                 return {};
229             },
230             "encoding %s.DrawIndexedIndirect(%s, %u).", this, indirectBuffer, indirectOffset);
231     }
232 
APISetPipeline(RenderPipelineBase * pipeline)233     void RenderEncoderBase::APISetPipeline(RenderPipelineBase* pipeline) {
234         mEncodingContext->TryEncode(
235             this,
236             [&](CommandAllocator* allocator) -> MaybeError {
237                 if (IsValidationEnabled()) {
238                     DAWN_TRY(GetDevice()->ValidateObject(pipeline));
239 
240                     // TODO(dawn:563): More detail about why the states are incompatible would be
241                     // nice.
242                     DAWN_INVALID_IF(
243                         pipeline->GetAttachmentState() != mAttachmentState.Get(),
244                         "Attachment state of %s is not compatible with the attachment state of %s",
245                         pipeline, this);
246 
247                     DAWN_INVALID_IF(pipeline->WritesDepth() && mDepthReadOnly,
248                                     "%s writes depth while %s's depthReadOnly is true", pipeline,
249                                     this);
250 
251                     DAWN_INVALID_IF(pipeline->WritesStencil() && mStencilReadOnly,
252                                     "%s writes stencil while %s's stencilReadOnly is true",
253                                     pipeline, this);
254                 }
255 
256                 mCommandBufferState.SetRenderPipeline(pipeline);
257 
258                 SetRenderPipelineCmd* cmd =
259                     allocator->Allocate<SetRenderPipelineCmd>(Command::SetRenderPipeline);
260                 cmd->pipeline = pipeline;
261 
262                 return {};
263             },
264             "encoding %s.SetPipeline(%s).", this, pipeline);
265     }
266 
APISetIndexBuffer(BufferBase * buffer,wgpu::IndexFormat format,uint64_t offset,uint64_t size)267     void RenderEncoderBase::APISetIndexBuffer(BufferBase* buffer,
268                                               wgpu::IndexFormat format,
269                                               uint64_t offset,
270                                               uint64_t size) {
271         mEncodingContext->TryEncode(
272             this,
273             [&](CommandAllocator* allocator) -> MaybeError {
274                 if (IsValidationEnabled()) {
275                     DAWN_TRY(GetDevice()->ValidateObject(buffer));
276                     DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Index));
277 
278                     DAWN_TRY(ValidateIndexFormat(format));
279 
280                     DAWN_INVALID_IF(format == wgpu::IndexFormat::Undefined,
281                                     "Index format must be specified");
282 
283                     DAWN_INVALID_IF(offset % uint64_t(IndexFormatSize(format)) != 0,
284                                     "Index buffer offset (%u) is not a multiple of the size (%u)"
285                                     "of %s.",
286                                     offset, IndexFormatSize(format), format);
287 
288                     uint64_t bufferSize = buffer->GetSize();
289                     DAWN_INVALID_IF(offset > bufferSize,
290                                     "Index buffer offset (%u) is larger than the size (%u) of %s.",
291                                     offset, bufferSize, buffer);
292 
293                     uint64_t remainingSize = bufferSize - offset;
294 
295                     if (size == wgpu::kWholeSize) {
296                         size = remainingSize;
297                     } else {
298                         DAWN_INVALID_IF(size > remainingSize,
299                                         "Index buffer range (offset: %u, size: %u) doesn't fit in "
300                                         "the size (%u) of "
301                                         "%s.",
302                                         offset, size, bufferSize, buffer);
303                     }
304                 } else {
305                     if (size == wgpu::kWholeSize) {
306                         DAWN_ASSERT(buffer->GetSize() >= offset);
307                         size = buffer->GetSize() - offset;
308                     }
309                 }
310 
311                 mCommandBufferState.SetIndexBuffer(format, size);
312 
313                 SetIndexBufferCmd* cmd =
314                     allocator->Allocate<SetIndexBufferCmd>(Command::SetIndexBuffer);
315                 cmd->buffer = buffer;
316                 cmd->format = format;
317                 cmd->offset = offset;
318                 cmd->size = size;
319 
320                 mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Index);
321 
322                 return {};
323             },
324             "encoding %s.SetIndexBuffer(%s, %s, %u, %u).", this, buffer, format, offset, size);
325     }
326 
APISetVertexBuffer(uint32_t slot,BufferBase * buffer,uint64_t offset,uint64_t size)327     void RenderEncoderBase::APISetVertexBuffer(uint32_t slot,
328                                                BufferBase* buffer,
329                                                uint64_t offset,
330                                                uint64_t size) {
331         mEncodingContext->TryEncode(
332             this,
333             [&](CommandAllocator* allocator) -> MaybeError {
334                 if (IsValidationEnabled()) {
335                     DAWN_TRY(GetDevice()->ValidateObject(buffer));
336                     DAWN_TRY(ValidateCanUseAs(buffer, wgpu::BufferUsage::Vertex));
337 
338                     DAWN_INVALID_IF(slot >= kMaxVertexBuffers,
339                                     "Vertex buffer slot (%u) is larger the maximum (%u)", slot,
340                                     kMaxVertexBuffers - 1);
341 
342                     DAWN_INVALID_IF(offset % 4 != 0,
343                                     "Vertex buffer offset (%u) is not a multiple of 4", offset);
344 
345                     uint64_t bufferSize = buffer->GetSize();
346                     DAWN_INVALID_IF(offset > bufferSize,
347                                     "Vertex buffer offset (%u) is larger than the size (%u) of %s.",
348                                     offset, bufferSize, buffer);
349 
350                     uint64_t remainingSize = bufferSize - offset;
351 
352                     if (size == wgpu::kWholeSize) {
353                         size = remainingSize;
354                     } else {
355                         DAWN_INVALID_IF(size > remainingSize,
356                                         "Vertex buffer range (offset: %u, size: %u) doesn't fit in "
357                                         "the size (%u) "
358                                         "of %s.",
359                                         offset, size, bufferSize, buffer);
360                     }
361                 } else {
362                     if (size == wgpu::kWholeSize) {
363                         DAWN_ASSERT(buffer->GetSize() >= offset);
364                         size = buffer->GetSize() - offset;
365                     }
366                 }
367 
368                 mCommandBufferState.SetVertexBuffer(VertexBufferSlot(uint8_t(slot)), size);
369 
370                 SetVertexBufferCmd* cmd =
371                     allocator->Allocate<SetVertexBufferCmd>(Command::SetVertexBuffer);
372                 cmd->slot = VertexBufferSlot(static_cast<uint8_t>(slot));
373                 cmd->buffer = buffer;
374                 cmd->offset = offset;
375                 cmd->size = size;
376 
377                 mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Vertex);
378 
379                 return {};
380             },
381             "encoding %s.SetVertexBuffer(%u, %s, %u, %u).", this, slot, buffer, offset, size);
382     }
383 
APISetBindGroup(uint32_t groupIndexIn,BindGroupBase * group,uint32_t dynamicOffsetCount,const uint32_t * dynamicOffsets)384     void RenderEncoderBase::APISetBindGroup(uint32_t groupIndexIn,
385                                             BindGroupBase* group,
386                                             uint32_t dynamicOffsetCount,
387                                             const uint32_t* dynamicOffsets) {
388         mEncodingContext->TryEncode(
389             this,
390             [&](CommandAllocator* allocator) -> MaybeError {
391                 BindGroupIndex groupIndex(groupIndexIn);
392 
393                 if (IsValidationEnabled()) {
394                     DAWN_TRY(ValidateSetBindGroup(groupIndex, group, dynamicOffsetCount,
395                                                   dynamicOffsets));
396                 }
397 
398                 RecordSetBindGroup(allocator, groupIndex, group, dynamicOffsetCount,
399                                    dynamicOffsets);
400                 mCommandBufferState.SetBindGroup(groupIndex, group, dynamicOffsetCount,
401                                                  dynamicOffsets);
402                 mUsageTracker.AddBindGroup(group);
403 
404                 return {};
405             },
406             "encoding %s.SetBindGroup(%u, %s, %u, ...).", this, groupIndexIn, group,
407             dynamicOffsetCount);
408     }
409 
410 }  // namespace dawn_native
411