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/CommandEncoder.h" 16 17 #include "common/BitSetIterator.h" 18 #include "common/Math.h" 19 #include "dawn_native/BindGroup.h" 20 #include "dawn_native/Buffer.h" 21 #include "dawn_native/CommandBuffer.h" 22 #include "dawn_native/CommandBufferStateTracker.h" 23 #include "dawn_native/CommandValidation.h" 24 #include "dawn_native/Commands.h" 25 #include "dawn_native/ComputePassEncoder.h" 26 #include "dawn_native/Device.h" 27 #include "dawn_native/ErrorData.h" 28 #include "dawn_native/QuerySet.h" 29 #include "dawn_native/RenderPassEncoder.h" 30 #include "dawn_native/RenderPipeline.h" 31 #include "dawn_native/ValidationUtils_autogen.h" 32 #include "dawn_platform/DawnPlatform.h" 33 #include "dawn_platform/tracing/TraceEvent.h" 34 35 #include <cmath> 36 #include <map> 37 38 namespace dawn_native { 39 40 namespace { 41 ValidateB2BCopyAlignment(uint64_t dataSize,uint64_t srcOffset,uint64_t dstOffset)42 MaybeError ValidateB2BCopyAlignment(uint64_t dataSize, 43 uint64_t srcOffset, 44 uint64_t dstOffset) { 45 // Copy size must be a multiple of 4 bytes on macOS. 46 if (dataSize % 4 != 0) { 47 return DAWN_VALIDATION_ERROR("Copy size must be a multiple of 4 bytes"); 48 } 49 50 // SourceOffset and destinationOffset must be multiples of 4 bytes on macOS. 51 if (srcOffset % 4 != 0 || dstOffset % 4 != 0) { 52 return DAWN_VALIDATION_ERROR( 53 "Source offset and destination offset must be multiples of 4 bytes"); 54 } 55 56 return {}; 57 } 58 ValidateTextureSampleCountInBufferCopyCommands(const TextureBase * texture)59 MaybeError ValidateTextureSampleCountInBufferCopyCommands(const TextureBase* texture) { 60 if (texture->GetSampleCount() > 1) { 61 return DAWN_VALIDATION_ERROR( 62 "The sample count of textures must be 1 when copying between buffers and " 63 "textures"); 64 } 65 66 return {}; 67 } 68 ValidateLinearTextureCopyOffset(const TextureDataLayout & layout,const TexelBlockInfo & blockInfo)69 MaybeError ValidateLinearTextureCopyOffset(const TextureDataLayout& layout, 70 const TexelBlockInfo& blockInfo) { 71 if (layout.offset % blockInfo.byteSize != 0) { 72 return DAWN_VALIDATION_ERROR( 73 "offset must be a multiple of the texel block byte size."); 74 } 75 return {}; 76 } 77 ValidateTextureDepthStencilToBufferCopyRestrictions(const TextureCopyView & src)78 MaybeError ValidateTextureDepthStencilToBufferCopyRestrictions(const TextureCopyView& src) { 79 Aspect aspectUsed; 80 DAWN_TRY_ASSIGN(aspectUsed, SingleAspectUsedByTextureCopyView(src)); 81 if (aspectUsed == Aspect::Depth) { 82 switch (src.texture->GetFormat().format) { 83 case wgpu::TextureFormat::Depth24Plus: 84 case wgpu::TextureFormat::Depth24PlusStencil8: 85 return DAWN_VALIDATION_ERROR( 86 "The depth aspect of depth24plus texture cannot be selected in a " 87 "texture to buffer copy"); 88 break; 89 case wgpu::TextureFormat::Depth32Float: 90 break; 91 92 default: 93 UNREACHABLE(); 94 } 95 } 96 97 return {}; 98 } 99 ValidateAttachmentArrayLayersAndLevelCount(const TextureViewBase * attachment)100 MaybeError ValidateAttachmentArrayLayersAndLevelCount(const TextureViewBase* attachment) { 101 // Currently we do not support layered rendering. 102 if (attachment->GetLayerCount() > 1) { 103 return DAWN_VALIDATION_ERROR( 104 "The layer count of the texture view used as attachment cannot be greater than " 105 "1"); 106 } 107 108 if (attachment->GetLevelCount() > 1) { 109 return DAWN_VALIDATION_ERROR( 110 "The mipmap level count of the texture view used as attachment cannot be " 111 "greater than 1"); 112 } 113 114 return {}; 115 } 116 ValidateOrSetAttachmentSize(const TextureViewBase * attachment,uint32_t * width,uint32_t * height)117 MaybeError ValidateOrSetAttachmentSize(const TextureViewBase* attachment, 118 uint32_t* width, 119 uint32_t* height) { 120 const Extent3D& attachmentSize = 121 attachment->GetTexture()->GetMipLevelVirtualSize(attachment->GetBaseMipLevel()); 122 123 if (*width == 0) { 124 DAWN_ASSERT(*height == 0); 125 *width = attachmentSize.width; 126 *height = attachmentSize.height; 127 DAWN_ASSERT(*width != 0 && *height != 0); 128 } else if (*width != attachmentSize.width || *height != attachmentSize.height) { 129 return DAWN_VALIDATION_ERROR("Attachment size mismatch"); 130 } 131 132 return {}; 133 } 134 ValidateOrSetColorAttachmentSampleCount(const TextureViewBase * colorAttachment,uint32_t * sampleCount)135 MaybeError ValidateOrSetColorAttachmentSampleCount(const TextureViewBase* colorAttachment, 136 uint32_t* sampleCount) { 137 if (*sampleCount == 0) { 138 *sampleCount = colorAttachment->GetTexture()->GetSampleCount(); 139 DAWN_ASSERT(*sampleCount != 0); 140 } else if (*sampleCount != colorAttachment->GetTexture()->GetSampleCount()) { 141 return DAWN_VALIDATION_ERROR("Color attachment sample counts mismatch"); 142 } 143 144 return {}; 145 } 146 ValidateResolveTarget(const DeviceBase * device,const RenderPassColorAttachmentDescriptor & colorAttachment)147 MaybeError ValidateResolveTarget( 148 const DeviceBase* device, 149 const RenderPassColorAttachmentDescriptor& colorAttachment) { 150 if (colorAttachment.resolveTarget == nullptr) { 151 return {}; 152 } 153 154 const TextureViewBase* resolveTarget = colorAttachment.resolveTarget; 155 const TextureViewBase* attachment = colorAttachment.attachment; 156 DAWN_TRY(device->ValidateObject(colorAttachment.resolveTarget)); 157 158 if (!attachment->GetTexture()->IsMultisampledTexture()) { 159 return DAWN_VALIDATION_ERROR( 160 "Cannot set resolve target when the sample count of the color attachment is 1"); 161 } 162 163 if (resolveTarget->GetTexture()->IsMultisampledTexture()) { 164 return DAWN_VALIDATION_ERROR("Cannot use multisampled texture as resolve target"); 165 } 166 167 if (resolveTarget->GetLayerCount() > 1) { 168 return DAWN_VALIDATION_ERROR( 169 "The array layer count of the resolve target must be 1"); 170 } 171 172 if (resolveTarget->GetLevelCount() > 1) { 173 return DAWN_VALIDATION_ERROR("The mip level count of the resolve target must be 1"); 174 } 175 176 uint32_t colorAttachmentBaseMipLevel = attachment->GetBaseMipLevel(); 177 const Extent3D& colorTextureSize = attachment->GetTexture()->GetSize(); 178 uint32_t colorAttachmentWidth = colorTextureSize.width >> colorAttachmentBaseMipLevel; 179 uint32_t colorAttachmentHeight = colorTextureSize.height >> colorAttachmentBaseMipLevel; 180 181 uint32_t resolveTargetBaseMipLevel = resolveTarget->GetBaseMipLevel(); 182 const Extent3D& resolveTextureSize = resolveTarget->GetTexture()->GetSize(); 183 uint32_t resolveTargetWidth = resolveTextureSize.width >> resolveTargetBaseMipLevel; 184 uint32_t resolveTargetHeight = resolveTextureSize.height >> resolveTargetBaseMipLevel; 185 if (colorAttachmentWidth != resolveTargetWidth || 186 colorAttachmentHeight != resolveTargetHeight) { 187 return DAWN_VALIDATION_ERROR( 188 "The size of the resolve target must be the same as the color attachment"); 189 } 190 191 wgpu::TextureFormat resolveTargetFormat = resolveTarget->GetFormat().format; 192 if (resolveTargetFormat != attachment->GetFormat().format) { 193 return DAWN_VALIDATION_ERROR( 194 "The format of the resolve target must be the same as the color attachment"); 195 } 196 197 return {}; 198 } 199 ValidateRenderPassColorAttachment(const DeviceBase * device,const RenderPassColorAttachmentDescriptor & colorAttachment,uint32_t * width,uint32_t * height,uint32_t * sampleCount)200 MaybeError ValidateRenderPassColorAttachment( 201 const DeviceBase* device, 202 const RenderPassColorAttachmentDescriptor& colorAttachment, 203 uint32_t* width, 204 uint32_t* height, 205 uint32_t* sampleCount) { 206 DAWN_TRY(device->ValidateObject(colorAttachment.attachment)); 207 208 const TextureViewBase* attachment = colorAttachment.attachment; 209 if (!(attachment->GetAspects() & Aspect::Color) || 210 !attachment->GetFormat().isRenderable) { 211 return DAWN_VALIDATION_ERROR( 212 "The format of the texture view used as color attachment is not color " 213 "renderable"); 214 } 215 216 DAWN_TRY(ValidateLoadOp(colorAttachment.loadOp)); 217 DAWN_TRY(ValidateStoreOp(colorAttachment.storeOp)); 218 219 if (colorAttachment.loadOp == wgpu::LoadOp::Clear) { 220 if (std::isnan(colorAttachment.clearColor.r) || 221 std::isnan(colorAttachment.clearColor.g) || 222 std::isnan(colorAttachment.clearColor.b) || 223 std::isnan(colorAttachment.clearColor.a)) { 224 return DAWN_VALIDATION_ERROR("Color clear value cannot contain NaN"); 225 } 226 } 227 228 DAWN_TRY(ValidateOrSetColorAttachmentSampleCount(attachment, sampleCount)); 229 230 DAWN_TRY(ValidateResolveTarget(device, colorAttachment)); 231 232 DAWN_TRY(ValidateAttachmentArrayLayersAndLevelCount(attachment)); 233 DAWN_TRY(ValidateOrSetAttachmentSize(attachment, width, height)); 234 235 return {}; 236 } 237 ValidateRenderPassDepthStencilAttachment(const DeviceBase * device,const RenderPassDepthStencilAttachmentDescriptor * depthStencilAttachment,uint32_t * width,uint32_t * height,uint32_t * sampleCount)238 MaybeError ValidateRenderPassDepthStencilAttachment( 239 const DeviceBase* device, 240 const RenderPassDepthStencilAttachmentDescriptor* depthStencilAttachment, 241 uint32_t* width, 242 uint32_t* height, 243 uint32_t* sampleCount) { 244 DAWN_ASSERT(depthStencilAttachment != nullptr); 245 246 DAWN_TRY(device->ValidateObject(depthStencilAttachment->attachment)); 247 248 const TextureViewBase* attachment = depthStencilAttachment->attachment; 249 if ((attachment->GetAspects() & (Aspect::Depth | Aspect::Stencil)) == Aspect::None || 250 !attachment->GetFormat().isRenderable) { 251 return DAWN_VALIDATION_ERROR( 252 "The format of the texture view used as depth stencil attachment is not a " 253 "depth stencil format"); 254 } 255 256 DAWN_TRY(ValidateLoadOp(depthStencilAttachment->depthLoadOp)); 257 DAWN_TRY(ValidateLoadOp(depthStencilAttachment->stencilLoadOp)); 258 DAWN_TRY(ValidateStoreOp(depthStencilAttachment->depthStoreOp)); 259 DAWN_TRY(ValidateStoreOp(depthStencilAttachment->stencilStoreOp)); 260 261 if (attachment->GetAspects() == (Aspect::Depth | Aspect::Stencil) && 262 depthStencilAttachment->depthReadOnly != depthStencilAttachment->stencilReadOnly) { 263 return DAWN_VALIDATION_ERROR( 264 "depthReadOnly and stencilReadOnly must be the same when texture aspect is " 265 "'all'"); 266 } 267 268 if (depthStencilAttachment->depthReadOnly && 269 (depthStencilAttachment->depthLoadOp != wgpu::LoadOp::Load || 270 depthStencilAttachment->depthStoreOp != wgpu::StoreOp::Store)) { 271 return DAWN_VALIDATION_ERROR( 272 "depthLoadOp must be load and depthStoreOp must be store when depthReadOnly " 273 "is true."); 274 } 275 276 if (depthStencilAttachment->stencilReadOnly && 277 (depthStencilAttachment->stencilLoadOp != wgpu::LoadOp::Load || 278 depthStencilAttachment->stencilStoreOp != wgpu::StoreOp::Store)) { 279 return DAWN_VALIDATION_ERROR( 280 "stencilLoadOp must be load and stencilStoreOp must be store when " 281 "stencilReadOnly " 282 "is true."); 283 } 284 285 if (depthStencilAttachment->depthLoadOp == wgpu::LoadOp::Clear && 286 std::isnan(depthStencilAttachment->clearDepth)) { 287 return DAWN_VALIDATION_ERROR("Depth clear value cannot be NaN"); 288 } 289 290 // *sampleCount == 0 must only happen when there is no color attachment. In that case we 291 // do not need to validate the sample count of the depth stencil attachment. 292 const uint32_t depthStencilSampleCount = attachment->GetTexture()->GetSampleCount(); 293 if (*sampleCount != 0) { 294 if (depthStencilSampleCount != *sampleCount) { 295 return DAWN_VALIDATION_ERROR("Depth stencil attachment sample counts mismatch"); 296 } 297 } else { 298 *sampleCount = depthStencilSampleCount; 299 } 300 301 DAWN_TRY(ValidateAttachmentArrayLayersAndLevelCount(attachment)); 302 DAWN_TRY(ValidateOrSetAttachmentSize(attachment, width, height)); 303 304 return {}; 305 } 306 ValidateRenderPassDescriptor(const DeviceBase * device,const RenderPassDescriptor * descriptor,uint32_t * width,uint32_t * height,uint32_t * sampleCount)307 MaybeError ValidateRenderPassDescriptor(const DeviceBase* device, 308 const RenderPassDescriptor* descriptor, 309 uint32_t* width, 310 uint32_t* height, 311 uint32_t* sampleCount) { 312 if (descriptor->colorAttachmentCount > kMaxColorAttachments) { 313 return DAWN_VALIDATION_ERROR("Setting color attachments out of bounds"); 314 } 315 316 for (uint32_t i = 0; i < descriptor->colorAttachmentCount; ++i) { 317 DAWN_TRY(ValidateRenderPassColorAttachment(device, descriptor->colorAttachments[i], 318 width, height, sampleCount)); 319 } 320 321 if (descriptor->depthStencilAttachment != nullptr) { 322 DAWN_TRY(ValidateRenderPassDepthStencilAttachment( 323 device, descriptor->depthStencilAttachment, width, height, sampleCount)); 324 } 325 326 if (descriptor->occlusionQuerySet != nullptr) { 327 return DAWN_VALIDATION_ERROR("occlusionQuerySet not implemented"); 328 } 329 330 if (descriptor->colorAttachmentCount == 0 && 331 descriptor->depthStencilAttachment == nullptr) { 332 return DAWN_VALIDATION_ERROR("Cannot use render pass with no attachments."); 333 } 334 335 return {}; 336 } 337 ValidateComputePassDescriptor(const DeviceBase * device,const ComputePassDescriptor * descriptor)338 MaybeError ValidateComputePassDescriptor(const DeviceBase* device, 339 const ComputePassDescriptor* descriptor) { 340 return {}; 341 } 342 ValidateQuerySetResolve(const QuerySetBase * querySet,uint32_t firstQuery,uint32_t queryCount,const BufferBase * destination,uint64_t destinationOffset)343 MaybeError ValidateQuerySetResolve(const QuerySetBase* querySet, 344 uint32_t firstQuery, 345 uint32_t queryCount, 346 const BufferBase* destination, 347 uint64_t destinationOffset) { 348 if (firstQuery >= querySet->GetQueryCount()) { 349 return DAWN_VALIDATION_ERROR("Query index out of bounds"); 350 } 351 352 if (queryCount > querySet->GetQueryCount() - firstQuery) { 353 return DAWN_VALIDATION_ERROR( 354 "The sum of firstQuery and queryCount exceeds the number of queries in query " 355 "set"); 356 } 357 358 // TODO(hao.x.li@intel.com): Validate that the queries between [firstQuery, firstQuery + 359 // queryCount - 1] must be available(written by query operations). 360 361 // The destinationOffset must be a multiple of 8 bytes on D3D12 and Vulkan 362 if (destinationOffset % 8 != 0) { 363 return DAWN_VALIDATION_ERROR( 364 "The alignment offset into the destination buffer must be a multiple of 8 " 365 "bytes"); 366 } 367 368 uint64_t bufferSize = destination->GetSize(); 369 // The destination buffer must have enough storage, from destination offset, to contain 370 // the result of resolved queries 371 bool fitsInBuffer = destinationOffset <= bufferSize && 372 (static_cast<uint64_t>(queryCount) * sizeof(uint64_t) <= 373 (bufferSize - destinationOffset)); 374 if (!fitsInBuffer) { 375 return DAWN_VALIDATION_ERROR("The resolved query data would overflow the buffer"); 376 } 377 378 return {}; 379 } 380 381 } // namespace 382 CommandEncoder(DeviceBase * device,const CommandEncoderDescriptor *)383 CommandEncoder::CommandEncoder(DeviceBase* device, const CommandEncoderDescriptor*) 384 : ObjectBase(device), mEncodingContext(device, this) { 385 } 386 AcquireResourceUsages()387 CommandBufferResourceUsage CommandEncoder::AcquireResourceUsages() { 388 return CommandBufferResourceUsage{mEncodingContext.AcquirePassUsages(), 389 std::move(mTopLevelBuffers), std::move(mTopLevelTextures), 390 std::move(mUsedQuerySets)}; 391 } 392 AcquireCommands()393 CommandIterator CommandEncoder::AcquireCommands() { 394 return mEncodingContext.AcquireCommands(); 395 } 396 TrackUsedQuerySet(QuerySetBase * querySet)397 void CommandEncoder::TrackUsedQuerySet(QuerySetBase* querySet) { 398 mUsedQuerySets.insert(querySet); 399 } 400 TrackUsedQueryIndex(QuerySetBase * querySet,uint32_t queryIndex)401 void CommandEncoder::TrackUsedQueryIndex(QuerySetBase* querySet, uint32_t queryIndex) { 402 UsedQueryMap::iterator it = mUsedQueryIndices.find(querySet); 403 if (it != mUsedQueryIndices.end()) { 404 // Record index on existing query set 405 std::vector<bool>& queryIndices = it->second; 406 queryIndices[queryIndex] = 1; 407 } else { 408 // Record index on new query set 409 std::vector<bool> queryIndices(querySet->GetQueryCount(), 0); 410 queryIndices[queryIndex] = 1; 411 mUsedQueryIndices.insert({querySet, std::move(queryIndices)}); 412 } 413 } 414 GetUsedQueryIndices() const415 const UsedQueryMap& CommandEncoder::GetUsedQueryIndices() const { 416 return mUsedQueryIndices; 417 } 418 419 // Implementation of the API's command recording methods 420 BeginComputePass(const ComputePassDescriptor * descriptor)421 ComputePassEncoder* CommandEncoder::BeginComputePass(const ComputePassDescriptor* descriptor) { 422 DeviceBase* device = GetDevice(); 423 424 bool success = 425 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 426 DAWN_TRY(ValidateComputePassDescriptor(device, descriptor)); 427 428 allocator->Allocate<BeginComputePassCmd>(Command::BeginComputePass); 429 430 return {}; 431 }); 432 433 if (success) { 434 ComputePassEncoder* passEncoder = 435 new ComputePassEncoder(device, this, &mEncodingContext); 436 mEncodingContext.EnterPass(passEncoder); 437 return passEncoder; 438 } 439 440 return ComputePassEncoder::MakeError(device, this, &mEncodingContext); 441 } 442 BeginRenderPass(const RenderPassDescriptor * descriptor)443 RenderPassEncoder* CommandEncoder::BeginRenderPass(const RenderPassDescriptor* descriptor) { 444 DeviceBase* device = GetDevice(); 445 446 PassResourceUsageTracker usageTracker(PassType::Render); 447 448 uint32_t width = 0; 449 uint32_t height = 0; 450 bool success = 451 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 452 uint32_t sampleCount = 0; 453 454 DAWN_TRY(ValidateRenderPassDescriptor(device, descriptor, &width, &height, 455 &sampleCount)); 456 457 ASSERT(width > 0 && height > 0 && sampleCount > 0); 458 459 BeginRenderPassCmd* cmd = 460 allocator->Allocate<BeginRenderPassCmd>(Command::BeginRenderPass); 461 462 cmd->attachmentState = device->GetOrCreateAttachmentState(descriptor); 463 464 for (ColorAttachmentIndex index : 465 IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) { 466 uint8_t i = static_cast<uint8_t>(index); 467 TextureViewBase* view = descriptor->colorAttachments[i].attachment; 468 TextureViewBase* resolveTarget = descriptor->colorAttachments[i].resolveTarget; 469 470 cmd->colorAttachments[index].view = view; 471 cmd->colorAttachments[index].resolveTarget = resolveTarget; 472 cmd->colorAttachments[index].loadOp = descriptor->colorAttachments[i].loadOp; 473 cmd->colorAttachments[index].storeOp = descriptor->colorAttachments[i].storeOp; 474 cmd->colorAttachments[index].clearColor = 475 descriptor->colorAttachments[i].clearColor; 476 477 usageTracker.TextureViewUsedAs(view, wgpu::TextureUsage::RenderAttachment); 478 479 if (resolveTarget != nullptr) { 480 usageTracker.TextureViewUsedAs(resolveTarget, 481 wgpu::TextureUsage::RenderAttachment); 482 } 483 } 484 485 if (cmd->attachmentState->HasDepthStencilAttachment()) { 486 TextureViewBase* view = descriptor->depthStencilAttachment->attachment; 487 488 cmd->depthStencilAttachment.view = view; 489 cmd->depthStencilAttachment.clearDepth = 490 descriptor->depthStencilAttachment->clearDepth; 491 cmd->depthStencilAttachment.clearStencil = 492 descriptor->depthStencilAttachment->clearStencil; 493 cmd->depthStencilAttachment.depthLoadOp = 494 descriptor->depthStencilAttachment->depthLoadOp; 495 cmd->depthStencilAttachment.depthStoreOp = 496 descriptor->depthStencilAttachment->depthStoreOp; 497 cmd->depthStencilAttachment.stencilLoadOp = 498 descriptor->depthStencilAttachment->stencilLoadOp; 499 cmd->depthStencilAttachment.stencilStoreOp = 500 descriptor->depthStencilAttachment->stencilStoreOp; 501 502 usageTracker.TextureViewUsedAs(view, wgpu::TextureUsage::RenderAttachment); 503 } 504 505 cmd->width = width; 506 cmd->height = height; 507 508 return {}; 509 }); 510 511 if (success) { 512 RenderPassEncoder* passEncoder = new RenderPassEncoder( 513 device, this, &mEncodingContext, std::move(usageTracker), width, height); 514 mEncodingContext.EnterPass(passEncoder); 515 return passEncoder; 516 } 517 518 return RenderPassEncoder::MakeError(device, this, &mEncodingContext); 519 } 520 CopyBufferToBuffer(BufferBase * source,uint64_t sourceOffset,BufferBase * destination,uint64_t destinationOffset,uint64_t size)521 void CommandEncoder::CopyBufferToBuffer(BufferBase* source, 522 uint64_t sourceOffset, 523 BufferBase* destination, 524 uint64_t destinationOffset, 525 uint64_t size) { 526 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 527 if (GetDevice()->IsValidationEnabled()) { 528 DAWN_TRY(GetDevice()->ValidateObject(source)); 529 DAWN_TRY(GetDevice()->ValidateObject(destination)); 530 531 if (source == destination) { 532 return DAWN_VALIDATION_ERROR( 533 "Source and destination cannot be the same buffer."); 534 } 535 536 DAWN_TRY(ValidateCopySizeFitsInBuffer(source, sourceOffset, size)); 537 DAWN_TRY(ValidateCopySizeFitsInBuffer(destination, destinationOffset, size)); 538 DAWN_TRY(ValidateB2BCopyAlignment(size, sourceOffset, destinationOffset)); 539 540 DAWN_TRY(ValidateCanUseAs(source, wgpu::BufferUsage::CopySrc)); 541 DAWN_TRY(ValidateCanUseAs(destination, wgpu::BufferUsage::CopyDst)); 542 543 mTopLevelBuffers.insert(source); 544 mTopLevelBuffers.insert(destination); 545 } 546 547 // Skip noop copies. Some backends validation rules disallow them. 548 if (size != 0) { 549 CopyBufferToBufferCmd* copy = 550 allocator->Allocate<CopyBufferToBufferCmd>(Command::CopyBufferToBuffer); 551 copy->source = source; 552 copy->sourceOffset = sourceOffset; 553 copy->destination = destination; 554 copy->destinationOffset = destinationOffset; 555 copy->size = size; 556 } 557 558 return {}; 559 }); 560 } 561 CopyBufferToTexture(const BufferCopyView * source,const TextureCopyView * destination,const Extent3D * copySize)562 void CommandEncoder::CopyBufferToTexture(const BufferCopyView* source, 563 const TextureCopyView* destination, 564 const Extent3D* copySize) { 565 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 566 if (GetDevice()->IsValidationEnabled()) { 567 DAWN_TRY(ValidateBufferCopyView(GetDevice(), *source)); 568 DAWN_TRY(ValidateCanUseAs(source->buffer, wgpu::BufferUsage::CopySrc)); 569 570 DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination, *copySize)); 571 DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst)); 572 DAWN_TRY(ValidateTextureSampleCountInBufferCopyCommands(destination->texture)); 573 574 DAWN_TRY(ValidateLinearToDepthStencilCopyRestrictions(*destination)); 575 // We validate texture copy range before validating linear texture data, 576 // because in the latter we divide copyExtent.width by blockWidth and 577 // copyExtent.height by blockHeight while the divisibility conditions are 578 // checked in validating texture copy range. 579 DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize)); 580 } 581 const TexelBlockInfo& blockInfo = 582 destination->texture->GetFormat().GetAspectInfo(destination->aspect).block; 583 TextureDataLayout srcLayout = FixUpDeprecatedTextureDataLayoutOptions( 584 GetDevice(), source->layout, blockInfo, *copySize); 585 if (GetDevice()->IsValidationEnabled()) { 586 DAWN_TRY(ValidateLinearTextureCopyOffset(srcLayout, blockInfo)); 587 DAWN_TRY(ValidateLinearTextureData(srcLayout, source->buffer->GetSize(), blockInfo, 588 *copySize)); 589 590 mTopLevelBuffers.insert(source->buffer); 591 mTopLevelTextures.insert(destination->texture); 592 } 593 594 ApplyDefaultTextureDataLayoutOptions(&srcLayout, blockInfo, *copySize); 595 596 // Skip noop copies. 597 if (copySize->width != 0 && copySize->height != 0 && copySize->depth != 0) { 598 // Record the copy command. 599 CopyBufferToTextureCmd* copy = 600 allocator->Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture); 601 copy->source.buffer = source->buffer; 602 copy->source.offset = srcLayout.offset; 603 copy->source.bytesPerRow = srcLayout.bytesPerRow; 604 copy->source.rowsPerImage = srcLayout.rowsPerImage; 605 copy->destination.texture = destination->texture; 606 copy->destination.origin = destination->origin; 607 copy->destination.mipLevel = destination->mipLevel; 608 copy->destination.aspect = 609 ConvertAspect(destination->texture->GetFormat(), destination->aspect); 610 copy->copySize = *copySize; 611 } 612 613 return {}; 614 }); 615 } 616 CopyTextureToBuffer(const TextureCopyView * source,const BufferCopyView * destination,const Extent3D * copySize)617 void CommandEncoder::CopyTextureToBuffer(const TextureCopyView* source, 618 const BufferCopyView* destination, 619 const Extent3D* copySize) { 620 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 621 if (GetDevice()->IsValidationEnabled()) { 622 DAWN_TRY(ValidateTextureCopyView(GetDevice(), *source, *copySize)); 623 DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc)); 624 DAWN_TRY(ValidateTextureSampleCountInBufferCopyCommands(source->texture)); 625 DAWN_TRY(ValidateTextureDepthStencilToBufferCopyRestrictions(*source)); 626 627 DAWN_TRY(ValidateBufferCopyView(GetDevice(), *destination)); 628 DAWN_TRY(ValidateCanUseAs(destination->buffer, wgpu::BufferUsage::CopyDst)); 629 630 // We validate texture copy range before validating linear texture data, 631 // because in the latter we divide copyExtent.width by blockWidth and 632 // copyExtent.height by blockHeight while the divisibility conditions are 633 // checked in validating texture copy range. 634 DAWN_TRY(ValidateTextureCopyRange(*source, *copySize)); 635 } 636 const TexelBlockInfo& blockInfo = 637 source->texture->GetFormat().GetAspectInfo(source->aspect).block; 638 TextureDataLayout dstLayout = FixUpDeprecatedTextureDataLayoutOptions( 639 GetDevice(), destination->layout, blockInfo, *copySize); 640 if (GetDevice()->IsValidationEnabled()) { 641 DAWN_TRY(ValidateLinearTextureCopyOffset(dstLayout, blockInfo)); 642 DAWN_TRY(ValidateLinearTextureData(dstLayout, destination->buffer->GetSize(), 643 blockInfo, *copySize)); 644 645 mTopLevelTextures.insert(source->texture); 646 mTopLevelBuffers.insert(destination->buffer); 647 } 648 649 ApplyDefaultTextureDataLayoutOptions(&dstLayout, blockInfo, *copySize); 650 651 // Skip noop copies. 652 if (copySize->width != 0 && copySize->height != 0 && copySize->depth != 0) { 653 // Record the copy command. 654 CopyTextureToBufferCmd* copy = 655 allocator->Allocate<CopyTextureToBufferCmd>(Command::CopyTextureToBuffer); 656 copy->source.texture = source->texture; 657 copy->source.origin = source->origin; 658 copy->source.mipLevel = source->mipLevel; 659 copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); 660 copy->destination.buffer = destination->buffer; 661 copy->destination.offset = dstLayout.offset; 662 copy->destination.bytesPerRow = dstLayout.bytesPerRow; 663 copy->destination.rowsPerImage = dstLayout.rowsPerImage; 664 copy->copySize = *copySize; 665 } 666 667 return {}; 668 }); 669 } 670 CopyTextureToTexture(const TextureCopyView * source,const TextureCopyView * destination,const Extent3D * copySize)671 void CommandEncoder::CopyTextureToTexture(const TextureCopyView* source, 672 const TextureCopyView* destination, 673 const Extent3D* copySize) { 674 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 675 if (GetDevice()->IsValidationEnabled()) { 676 DAWN_TRY(GetDevice()->ValidateObject(source->texture)); 677 DAWN_TRY(GetDevice()->ValidateObject(destination->texture)); 678 679 DAWN_TRY(ValidateTextureCopyView(GetDevice(), *source, *copySize)); 680 DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination, *copySize)); 681 682 DAWN_TRY( 683 ValidateTextureToTextureCopyRestrictions(*source, *destination, *copySize)); 684 685 DAWN_TRY(ValidateTextureCopyRange(*source, *copySize)); 686 DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize)); 687 688 DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc)); 689 DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst)); 690 691 mTopLevelTextures.insert(source->texture); 692 mTopLevelTextures.insert(destination->texture); 693 } 694 695 // Skip noop copies. 696 if (copySize->width != 0 && copySize->height != 0 && copySize->depth != 0) { 697 CopyTextureToTextureCmd* copy = 698 allocator->Allocate<CopyTextureToTextureCmd>(Command::CopyTextureToTexture); 699 copy->source.texture = source->texture; 700 copy->source.origin = source->origin; 701 copy->source.mipLevel = source->mipLevel; 702 copy->source.aspect = ConvertAspect(source->texture->GetFormat(), source->aspect); 703 copy->destination.texture = destination->texture; 704 copy->destination.origin = destination->origin; 705 copy->destination.mipLevel = destination->mipLevel; 706 copy->destination.aspect = 707 ConvertAspect(destination->texture->GetFormat(), destination->aspect); 708 copy->copySize = *copySize; 709 } 710 711 return {}; 712 }); 713 } 714 InsertDebugMarker(const char * groupLabel)715 void CommandEncoder::InsertDebugMarker(const char* groupLabel) { 716 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 717 InsertDebugMarkerCmd* cmd = 718 allocator->Allocate<InsertDebugMarkerCmd>(Command::InsertDebugMarker); 719 cmd->length = strlen(groupLabel); 720 721 char* label = allocator->AllocateData<char>(cmd->length + 1); 722 memcpy(label, groupLabel, cmd->length + 1); 723 724 return {}; 725 }); 726 } 727 PopDebugGroup()728 void CommandEncoder::PopDebugGroup() { 729 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 730 allocator->Allocate<PopDebugGroupCmd>(Command::PopDebugGroup); 731 732 return {}; 733 }); 734 } 735 PushDebugGroup(const char * groupLabel)736 void CommandEncoder::PushDebugGroup(const char* groupLabel) { 737 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 738 PushDebugGroupCmd* cmd = 739 allocator->Allocate<PushDebugGroupCmd>(Command::PushDebugGroup); 740 cmd->length = strlen(groupLabel); 741 742 char* label = allocator->AllocateData<char>(cmd->length + 1); 743 memcpy(label, groupLabel, cmd->length + 1); 744 745 return {}; 746 }); 747 } 748 ResolveQuerySet(QuerySetBase * querySet,uint32_t firstQuery,uint32_t queryCount,BufferBase * destination,uint64_t destinationOffset)749 void CommandEncoder::ResolveQuerySet(QuerySetBase* querySet, 750 uint32_t firstQuery, 751 uint32_t queryCount, 752 BufferBase* destination, 753 uint64_t destinationOffset) { 754 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 755 if (GetDevice()->IsValidationEnabled()) { 756 DAWN_TRY(GetDevice()->ValidateObject(querySet)); 757 DAWN_TRY(GetDevice()->ValidateObject(destination)); 758 759 DAWN_TRY(ValidateQuerySetResolve(querySet, firstQuery, queryCount, destination, 760 destinationOffset)); 761 762 DAWN_TRY(ValidateCanUseAs(destination, wgpu::BufferUsage::QueryResolve)); 763 764 TrackUsedQuerySet(querySet); 765 mTopLevelBuffers.insert(destination); 766 } 767 768 ResolveQuerySetCmd* cmd = 769 allocator->Allocate<ResolveQuerySetCmd>(Command::ResolveQuerySet); 770 cmd->querySet = querySet; 771 cmd->firstQuery = firstQuery; 772 cmd->queryCount = queryCount; 773 cmd->destination = destination; 774 cmd->destinationOffset = destinationOffset; 775 776 return {}; 777 }); 778 } 779 WriteTimestamp(QuerySetBase * querySet,uint32_t queryIndex)780 void CommandEncoder::WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) { 781 mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { 782 if (GetDevice()->IsValidationEnabled()) { 783 DAWN_TRY(GetDevice()->ValidateObject(querySet)); 784 DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex, GetUsedQueryIndices())); 785 TrackUsedQuerySet(querySet); 786 } 787 788 TrackUsedQueryIndex(querySet, queryIndex); 789 790 WriteTimestampCmd* cmd = 791 allocator->Allocate<WriteTimestampCmd>(Command::WriteTimestamp); 792 cmd->querySet = querySet; 793 cmd->queryIndex = queryIndex; 794 795 return {}; 796 }); 797 } 798 Finish(const CommandBufferDescriptor * descriptor)799 CommandBufferBase* CommandEncoder::Finish(const CommandBufferDescriptor* descriptor) { 800 DeviceBase* device = GetDevice(); 801 // Even if mEncodingContext.Finish() validation fails, calling it will mutate the internal 802 // state of the encoding context. The internal state is set to finished, and subsequent 803 // calls to encode commands will generate errors. 804 if (device->ConsumedError(mEncodingContext.Finish()) || 805 device->ConsumedError(device->ValidateIsAlive()) || 806 (device->IsValidationEnabled() && 807 device->ConsumedError(ValidateFinish(mEncodingContext.GetIterator(), 808 mEncodingContext.GetPassUsages())))) { 809 return CommandBufferBase::MakeError(device); 810 } 811 ASSERT(!IsError()); 812 return device->CreateCommandBuffer(this, descriptor); 813 } 814 815 // Implementation of the command buffer validation that can be precomputed before submit ValidateFinish(CommandIterator * commands,const PerPassUsages & perPassUsages) const816 MaybeError CommandEncoder::ValidateFinish(CommandIterator* commands, 817 const PerPassUsages& perPassUsages) const { 818 TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "CommandEncoder::ValidateFinish"); 819 DAWN_TRY(GetDevice()->ValidateObject(this)); 820 821 for (const PassResourceUsage& passUsage : perPassUsages) { 822 DAWN_TRY(ValidatePassResourceUsage(passUsage)); 823 } 824 825 uint64_t debugGroupStackSize = 0; 826 827 commands->Reset(); 828 Command type; 829 while (commands->NextCommandId(&type)) { 830 switch (type) { 831 case Command::BeginComputePass: { 832 commands->NextCommand<BeginComputePassCmd>(); 833 DAWN_TRY(ValidateComputePass(commands)); 834 break; 835 } 836 837 case Command::BeginRenderPass: { 838 const BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>(); 839 DAWN_TRY(ValidateRenderPass(commands, cmd)); 840 break; 841 } 842 843 case Command::CopyBufferToBuffer: { 844 commands->NextCommand<CopyBufferToBufferCmd>(); 845 break; 846 } 847 848 case Command::CopyBufferToTexture: { 849 commands->NextCommand<CopyBufferToTextureCmd>(); 850 break; 851 } 852 853 case Command::CopyTextureToBuffer: { 854 commands->NextCommand<CopyTextureToBufferCmd>(); 855 break; 856 } 857 858 case Command::CopyTextureToTexture: { 859 commands->NextCommand<CopyTextureToTextureCmd>(); 860 break; 861 } 862 863 case Command::InsertDebugMarker: { 864 const InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>(); 865 commands->NextData<char>(cmd->length + 1); 866 break; 867 } 868 869 case Command::PopDebugGroup: { 870 commands->NextCommand<PopDebugGroupCmd>(); 871 DAWN_TRY(ValidateCanPopDebugGroup(debugGroupStackSize)); 872 debugGroupStackSize--; 873 break; 874 } 875 876 case Command::PushDebugGroup: { 877 const PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>(); 878 commands->NextData<char>(cmd->length + 1); 879 debugGroupStackSize++; 880 break; 881 } 882 883 case Command::ResolveQuerySet: { 884 commands->NextCommand<ResolveQuerySetCmd>(); 885 break; 886 } 887 888 case Command::WriteTimestamp: { 889 commands->NextCommand<WriteTimestampCmd>(); 890 break; 891 } 892 default: 893 return DAWN_VALIDATION_ERROR("Command disallowed outside of a pass"); 894 } 895 } 896 897 DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); 898 899 return {}; 900 } 901 902 } // namespace dawn_native 903