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