1 // Copyright 2017 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/vulkan/CommandBufferVk.h" 16 17 #include "dawn_native/BindGroupTracker.h" 18 #include "dawn_native/CommandEncoder.h" 19 #include "dawn_native/CommandValidation.h" 20 #include "dawn_native/Commands.h" 21 #include "dawn_native/DynamicUploader.h" 22 #include "dawn_native/EnumMaskIterator.h" 23 #include "dawn_native/RenderBundle.h" 24 #include "dawn_native/vulkan/BindGroupVk.h" 25 #include "dawn_native/vulkan/BufferVk.h" 26 #include "dawn_native/vulkan/CommandRecordingContext.h" 27 #include "dawn_native/vulkan/ComputePipelineVk.h" 28 #include "dawn_native/vulkan/DeviceVk.h" 29 #include "dawn_native/vulkan/FencedDeleter.h" 30 #include "dawn_native/vulkan/PipelineLayoutVk.h" 31 #include "dawn_native/vulkan/QuerySetVk.h" 32 #include "dawn_native/vulkan/RenderPassCache.h" 33 #include "dawn_native/vulkan/RenderPipelineVk.h" 34 #include "dawn_native/vulkan/StagingBufferVk.h" 35 #include "dawn_native/vulkan/TextureVk.h" 36 #include "dawn_native/vulkan/UtilsVulkan.h" 37 #include "dawn_native/vulkan/VulkanError.h" 38 39 #include <algorithm> 40 41 namespace dawn_native { namespace vulkan { 42 43 namespace { 44 VulkanIndexType(wgpu::IndexFormat format)45 VkIndexType VulkanIndexType(wgpu::IndexFormat format) { 46 switch (format) { 47 case wgpu::IndexFormat::Uint16: 48 return VK_INDEX_TYPE_UINT16; 49 case wgpu::IndexFormat::Uint32: 50 return VK_INDEX_TYPE_UINT32; 51 case wgpu::IndexFormat::Undefined: 52 break; 53 } 54 UNREACHABLE(); 55 } 56 HasSameTextureCopyExtent(const TextureCopy& srcCopy, const TextureCopy& dstCopy, const Extent3D& copySize)57 bool HasSameTextureCopyExtent(const TextureCopy& srcCopy, 58 const TextureCopy& dstCopy, 59 const Extent3D& copySize) { 60 Extent3D imageExtentSrc = ComputeTextureCopyExtent(srcCopy, copySize); 61 Extent3D imageExtentDst = ComputeTextureCopyExtent(dstCopy, copySize); 62 return imageExtentSrc.width == imageExtentDst.width && 63 imageExtentSrc.height == imageExtentDst.height && 64 imageExtentSrc.depthOrArrayLayers == imageExtentDst.depthOrArrayLayers; 65 } 66 ComputeImageCopyRegion(const TextureCopy& srcCopy, const TextureCopy& dstCopy, const Extent3D& copySize, Aspect aspect)67 VkImageCopy ComputeImageCopyRegion(const TextureCopy& srcCopy, 68 const TextureCopy& dstCopy, 69 const Extent3D& copySize, 70 Aspect aspect) { 71 const Texture* srcTexture = ToBackend(srcCopy.texture.Get()); 72 const Texture* dstTexture = ToBackend(dstCopy.texture.Get()); 73 74 VkImageCopy region; 75 region.srcSubresource.aspectMask = VulkanAspectMask(aspect); 76 region.srcSubresource.mipLevel = srcCopy.mipLevel; 77 region.dstSubresource.aspectMask = VulkanAspectMask(aspect); 78 region.dstSubresource.mipLevel = dstCopy.mipLevel; 79 80 bool has3DTextureInCopy = false; 81 82 region.srcOffset.x = srcCopy.origin.x; 83 region.srcOffset.y = srcCopy.origin.y; 84 switch (srcTexture->GetDimension()) { 85 case wgpu::TextureDimension::e2D: 86 region.srcSubresource.baseArrayLayer = srcCopy.origin.z; 87 region.srcSubresource.layerCount = copySize.depthOrArrayLayers; 88 region.srcOffset.z = 0; 89 break; 90 case wgpu::TextureDimension::e3D: 91 has3DTextureInCopy = true; 92 region.srcSubresource.baseArrayLayer = 0; 93 region.srcSubresource.layerCount = 1; 94 region.srcOffset.z = srcCopy.origin.z; 95 break; 96 case wgpu::TextureDimension::e1D: 97 // TODO(crbug.com/dawn/814): support 1D textures 98 UNREACHABLE(); 99 } 100 101 region.dstOffset.x = dstCopy.origin.x; 102 region.dstOffset.y = dstCopy.origin.y; 103 switch (dstTexture->GetDimension()) { 104 case wgpu::TextureDimension::e2D: 105 region.dstSubresource.baseArrayLayer = dstCopy.origin.z; 106 region.dstSubresource.layerCount = copySize.depthOrArrayLayers; 107 region.dstOffset.z = 0; 108 break; 109 case wgpu::TextureDimension::e3D: 110 has3DTextureInCopy = true; 111 region.dstSubresource.baseArrayLayer = 0; 112 region.dstSubresource.layerCount = 1; 113 region.dstOffset.z = dstCopy.origin.z; 114 break; 115 case wgpu::TextureDimension::e1D: 116 // TODO(crbug.com/dawn/814): support 1D textures 117 UNREACHABLE(); 118 } 119 120 ASSERT(HasSameTextureCopyExtent(srcCopy, dstCopy, copySize)); 121 Extent3D imageExtent = ComputeTextureCopyExtent(dstCopy, copySize); 122 region.extent.width = imageExtent.width; 123 region.extent.height = imageExtent.height; 124 region.extent.depth = has3DTextureInCopy ? copySize.depthOrArrayLayers : 1; 125 126 return region; 127 } 128 129 class DescriptorSetTracker : public BindGroupTrackerBase<true, uint32_t> { 130 public: 131 DescriptorSetTracker() = default; 132 Apply(Device* device, CommandRecordingContext* recordingContext, VkPipelineBindPoint bindPoint)133 void Apply(Device* device, 134 CommandRecordingContext* recordingContext, 135 VkPipelineBindPoint bindPoint) { 136 BeforeApply(); 137 for (BindGroupIndex dirtyIndex : 138 IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) { 139 VkDescriptorSet set = ToBackend(mBindGroups[dirtyIndex])->GetHandle(); 140 const uint32_t* dynamicOffset = mDynamicOffsetCounts[dirtyIndex] > 0 141 ? mDynamicOffsets[dirtyIndex].data() 142 : nullptr; 143 device->fn.CmdBindDescriptorSets( 144 recordingContext->commandBuffer, bindPoint, 145 ToBackend(mPipelineLayout)->GetHandle(), static_cast<uint32_t>(dirtyIndex), 146 1, &*set, mDynamicOffsetCounts[dirtyIndex], dynamicOffset); 147 } 148 AfterApply(); 149 } 150 }; 151 152 // Records the necessary barriers for a synchronization scope using the resource usage 153 // data pre-computed in the frontend. Also performs lazy initialization if required. TransitionAndClearForSyncScope(Device* device, CommandRecordingContext* recordingContext, const SyncScopeResourceUsage& scope)154 void TransitionAndClearForSyncScope(Device* device, 155 CommandRecordingContext* recordingContext, 156 const SyncScopeResourceUsage& scope) { 157 std::vector<VkBufferMemoryBarrier> bufferBarriers; 158 std::vector<VkImageMemoryBarrier> imageBarriers; 159 VkPipelineStageFlags srcStages = 0; 160 VkPipelineStageFlags dstStages = 0; 161 162 for (size_t i = 0; i < scope.buffers.size(); ++i) { 163 Buffer* buffer = ToBackend(scope.buffers[i]); 164 buffer->EnsureDataInitialized(recordingContext); 165 166 VkBufferMemoryBarrier bufferBarrier; 167 if (buffer->TransitionUsageAndGetResourceBarrier( 168 scope.bufferUsages[i], &bufferBarrier, &srcStages, &dstStages)) { 169 bufferBarriers.push_back(bufferBarrier); 170 } 171 } 172 173 for (size_t i = 0; i < scope.textures.size(); ++i) { 174 Texture* texture = ToBackend(scope.textures[i]); 175 176 // Clear subresources that are not render attachments. Render attachments will be 177 // cleared in RecordBeginRenderPass by setting the loadop to clear when the texture 178 // subresource has not been initialized before the render pass. 179 scope.textureUsages[i].Iterate( 180 [&](const SubresourceRange& range, wgpu::TextureUsage usage) { 181 if (usage & ~wgpu::TextureUsage::RenderAttachment) { 182 texture->EnsureSubresourceContentInitialized(recordingContext, range); 183 } 184 }); 185 texture->TransitionUsageForPass(recordingContext, scope.textureUsages[i], 186 &imageBarriers, &srcStages, &dstStages); 187 } 188 189 if (bufferBarriers.size() || imageBarriers.size()) { 190 device->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 191 0, 0, nullptr, bufferBarriers.size(), 192 bufferBarriers.data(), imageBarriers.size(), 193 imageBarriers.data()); 194 } 195 } 196 RecordBeginRenderPass(CommandRecordingContext* recordingContext, Device* device, BeginRenderPassCmd* renderPass)197 MaybeError RecordBeginRenderPass(CommandRecordingContext* recordingContext, 198 Device* device, 199 BeginRenderPassCmd* renderPass) { 200 VkCommandBuffer commands = recordingContext->commandBuffer; 201 202 // Query a VkRenderPass from the cache 203 VkRenderPass renderPassVK = VK_NULL_HANDLE; 204 { 205 RenderPassCacheQuery query; 206 207 for (ColorAttachmentIndex i : 208 IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { 209 const auto& attachmentInfo = renderPass->colorAttachments[i]; 210 211 bool hasResolveTarget = attachmentInfo.resolveTarget != nullptr; 212 213 query.SetColor(i, attachmentInfo.view->GetFormat().format, 214 attachmentInfo.loadOp, attachmentInfo.storeOp, hasResolveTarget); 215 } 216 217 if (renderPass->attachmentState->HasDepthStencilAttachment()) { 218 const auto& attachmentInfo = renderPass->depthStencilAttachment; 219 220 query.SetDepthStencil( 221 attachmentInfo.view->GetTexture()->GetFormat().format, 222 attachmentInfo.depthLoadOp, attachmentInfo.depthStoreOp, 223 attachmentInfo.stencilLoadOp, attachmentInfo.stencilStoreOp, 224 attachmentInfo.depthReadOnly || attachmentInfo.stencilReadOnly); 225 } 226 227 query.SetSampleCount(renderPass->attachmentState->GetSampleCount()); 228 229 DAWN_TRY_ASSIGN(renderPassVK, device->GetRenderPassCache()->GetRenderPass(query)); 230 } 231 232 // Create a framebuffer that will be used once for the render pass and gather the clear 233 // values for the attachments at the same time. 234 std::array<VkClearValue, kMaxColorAttachments + 1> clearValues; 235 VkFramebuffer framebuffer = VK_NULL_HANDLE; 236 uint32_t attachmentCount = 0; 237 { 238 // Fill in the attachment info that will be chained in the framebuffer create info. 239 std::array<VkImageView, kMaxColorAttachments * 2 + 1> attachments; 240 241 for (ColorAttachmentIndex i : 242 IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { 243 auto& attachmentInfo = renderPass->colorAttachments[i]; 244 TextureView* view = ToBackend(attachmentInfo.view.Get()); 245 246 attachments[attachmentCount] = view->GetHandle(); 247 248 switch (view->GetFormat().GetAspectInfo(Aspect::Color).baseType) { 249 case wgpu::TextureComponentType::Float: { 250 const std::array<float, 4> appliedClearColor = 251 ConvertToFloatColor(attachmentInfo.clearColor); 252 for (uint32_t i = 0; i < 4; ++i) { 253 clearValues[attachmentCount].color.float32[i] = 254 appliedClearColor[i]; 255 } 256 break; 257 } 258 case wgpu::TextureComponentType::Uint: { 259 const std::array<uint32_t, 4> appliedClearColor = 260 ConvertToUnsignedIntegerColor(attachmentInfo.clearColor); 261 for (uint32_t i = 0; i < 4; ++i) { 262 clearValues[attachmentCount].color.uint32[i] = appliedClearColor[i]; 263 } 264 break; 265 } 266 case wgpu::TextureComponentType::Sint: { 267 const std::array<int32_t, 4> appliedClearColor = 268 ConvertToSignedIntegerColor(attachmentInfo.clearColor); 269 for (uint32_t i = 0; i < 4; ++i) { 270 clearValues[attachmentCount].color.int32[i] = appliedClearColor[i]; 271 } 272 break; 273 } 274 275 case wgpu::TextureComponentType::DepthComparison: 276 UNREACHABLE(); 277 } 278 attachmentCount++; 279 } 280 281 if (renderPass->attachmentState->HasDepthStencilAttachment()) { 282 auto& attachmentInfo = renderPass->depthStencilAttachment; 283 TextureView* view = ToBackend(attachmentInfo.view.Get()); 284 285 attachments[attachmentCount] = view->GetHandle(); 286 287 clearValues[attachmentCount].depthStencil.depth = attachmentInfo.clearDepth; 288 clearValues[attachmentCount].depthStencil.stencil = attachmentInfo.clearStencil; 289 290 attachmentCount++; 291 } 292 293 for (ColorAttachmentIndex i : 294 IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { 295 if (renderPass->colorAttachments[i].resolveTarget != nullptr) { 296 TextureView* view = 297 ToBackend(renderPass->colorAttachments[i].resolveTarget.Get()); 298 299 attachments[attachmentCount] = view->GetHandle(); 300 301 attachmentCount++; 302 } 303 } 304 305 // Chain attachments and create the framebuffer 306 VkFramebufferCreateInfo createInfo; 307 createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; 308 createInfo.pNext = nullptr; 309 createInfo.flags = 0; 310 createInfo.renderPass = renderPassVK; 311 createInfo.attachmentCount = attachmentCount; 312 createInfo.pAttachments = AsVkArray(attachments.data()); 313 createInfo.width = renderPass->width; 314 createInfo.height = renderPass->height; 315 createInfo.layers = 1; 316 317 DAWN_TRY( 318 CheckVkSuccess(device->fn.CreateFramebuffer(device->GetVkDevice(), &createInfo, 319 nullptr, &*framebuffer), 320 "CreateFramebuffer")); 321 322 // We don't reuse VkFramebuffers so mark the framebuffer for deletion as soon as the 323 // commands currently being recorded are finished. 324 device->GetFencedDeleter()->DeleteWhenUnused(framebuffer); 325 } 326 327 VkRenderPassBeginInfo beginInfo; 328 beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; 329 beginInfo.pNext = nullptr; 330 beginInfo.renderPass = renderPassVK; 331 beginInfo.framebuffer = framebuffer; 332 beginInfo.renderArea.offset.x = 0; 333 beginInfo.renderArea.offset.y = 0; 334 beginInfo.renderArea.extent.width = renderPass->width; 335 beginInfo.renderArea.extent.height = renderPass->height; 336 beginInfo.clearValueCount = attachmentCount; 337 beginInfo.pClearValues = clearValues.data(); 338 339 device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE); 340 341 return {}; 342 } 343 344 // Reset the query sets used on render pass because the reset command must be called outside 345 // render pass. ResetUsedQuerySetsOnRenderPass(Device* device, VkCommandBuffer commands, QuerySetBase* querySet, const std::vector<bool>& availability)346 void ResetUsedQuerySetsOnRenderPass(Device* device, 347 VkCommandBuffer commands, 348 QuerySetBase* querySet, 349 const std::vector<bool>& availability) { 350 ASSERT(availability.size() == querySet->GetQueryAvailability().size()); 351 352 auto currentIt = availability.begin(); 353 auto lastIt = availability.end(); 354 // Traverse the used queries which availability are true. 355 while (currentIt != lastIt) { 356 auto firstTrueIt = std::find(currentIt, lastIt, true); 357 // No used queries need to be reset 358 if (firstTrueIt == lastIt) { 359 break; 360 } 361 362 auto nextFalseIt = std::find(firstTrueIt, lastIt, false); 363 364 uint32_t queryIndex = std::distance(availability.begin(), firstTrueIt); 365 uint32_t queryCount = std::distance(firstTrueIt, nextFalseIt); 366 367 // Reset the queries between firstTrueIt and nextFalseIt (which is at most 368 // lastIt) 369 device->fn.CmdResetQueryPool(commands, ToBackend(querySet)->GetHandle(), queryIndex, 370 queryCount); 371 372 // Set current iterator to next false 373 currentIt = nextFalseIt; 374 } 375 } 376 RecordWriteTimestampCmd(CommandRecordingContext* recordingContext, Device* device, WriteTimestampCmd* cmd)377 void RecordWriteTimestampCmd(CommandRecordingContext* recordingContext, 378 Device* device, 379 WriteTimestampCmd* cmd) { 380 VkCommandBuffer commands = recordingContext->commandBuffer; 381 QuerySet* querySet = ToBackend(cmd->querySet.Get()); 382 383 device->fn.CmdWriteTimestamp(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 384 querySet->GetHandle(), cmd->queryIndex); 385 } 386 RecordResolveQuerySetCmd(VkCommandBuffer commands, Device* device, QuerySet* querySet, uint32_t firstQuery, uint32_t queryCount, Buffer* destination, uint64_t destinationOffset)387 void RecordResolveQuerySetCmd(VkCommandBuffer commands, 388 Device* device, 389 QuerySet* querySet, 390 uint32_t firstQuery, 391 uint32_t queryCount, 392 Buffer* destination, 393 uint64_t destinationOffset) { 394 const std::vector<bool>& availability = querySet->GetQueryAvailability(); 395 396 auto currentIt = availability.begin() + firstQuery; 397 auto lastIt = availability.begin() + firstQuery + queryCount; 398 399 // Traverse available queries in the range of [firstQuery, firstQuery + queryCount - 1] 400 while (currentIt != lastIt) { 401 auto firstTrueIt = std::find(currentIt, lastIt, true); 402 // No available query found for resolving 403 if (firstTrueIt == lastIt) { 404 break; 405 } 406 auto nextFalseIt = std::find(firstTrueIt, lastIt, false); 407 408 // The query index of firstTrueIt where the resolving starts 409 uint32_t resolveQueryIndex = std::distance(availability.begin(), firstTrueIt); 410 // The queries count between firstTrueIt and nextFalseIt need to be resolved 411 uint32_t resolveQueryCount = std::distance(firstTrueIt, nextFalseIt); 412 413 // Calculate destinationOffset based on the current resolveQueryIndex and firstQuery 414 uint32_t resolveDestinationOffset = 415 destinationOffset + (resolveQueryIndex - firstQuery) * sizeof(uint64_t); 416 417 // Resolve the queries between firstTrueIt and nextFalseIt (which is at most lastIt) 418 device->fn.CmdCopyQueryPoolResults( 419 commands, querySet->GetHandle(), resolveQueryIndex, resolveQueryCount, 420 destination->GetHandle(), resolveDestinationOffset, sizeof(uint64_t), 421 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); 422 423 // Set current iterator to next false 424 currentIt = nextFalseIt; 425 } 426 } 427 428 } // anonymous namespace 429 430 // static Create(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)431 Ref<CommandBuffer> CommandBuffer::Create(CommandEncoder* encoder, 432 const CommandBufferDescriptor* descriptor) { 433 return AcquireRef(new CommandBuffer(encoder, descriptor)); 434 } 435 CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)436 CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) 437 : CommandBufferBase(encoder, descriptor) { 438 } 439 RecordCopyImageWithTemporaryBuffer( CommandRecordingContext* recordingContext, const TextureCopy& srcCopy, const TextureCopy& dstCopy, const Extent3D& copySize)440 void CommandBuffer::RecordCopyImageWithTemporaryBuffer( 441 CommandRecordingContext* recordingContext, 442 const TextureCopy& srcCopy, 443 const TextureCopy& dstCopy, 444 const Extent3D& copySize) { 445 ASSERT(srcCopy.texture->GetFormat().format == dstCopy.texture->GetFormat().format); 446 ASSERT(srcCopy.aspect == dstCopy.aspect); 447 dawn_native::Format format = srcCopy.texture->GetFormat(); 448 const TexelBlockInfo& blockInfo = format.GetAspectInfo(srcCopy.aspect).block; 449 ASSERT(copySize.width % blockInfo.width == 0); 450 uint32_t widthInBlocks = copySize.width / blockInfo.width; 451 ASSERT(copySize.height % blockInfo.height == 0); 452 uint32_t heightInBlocks = copySize.height / blockInfo.height; 453 454 // Create the temporary buffer. Note that We don't need to respect WebGPU's 256 alignment 455 // because it isn't a hard constraint in Vulkan. 456 uint64_t tempBufferSize = 457 widthInBlocks * heightInBlocks * copySize.depthOrArrayLayers * blockInfo.byteSize; 458 BufferDescriptor tempBufferDescriptor; 459 tempBufferDescriptor.size = tempBufferSize; 460 tempBufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; 461 462 Device* device = ToBackend(GetDevice()); 463 // TODO(dawn:723): change to not use AcquireRef for reentrant object creation. 464 Ref<Buffer> tempBuffer = 465 AcquireRef(ToBackend(device->APICreateBuffer(&tempBufferDescriptor))); 466 467 BufferCopy tempBufferCopy; 468 tempBufferCopy.buffer = tempBuffer.Get(); 469 tempBufferCopy.rowsPerImage = heightInBlocks; 470 tempBufferCopy.offset = 0; 471 tempBufferCopy.bytesPerRow = copySize.width / blockInfo.width * blockInfo.byteSize; 472 473 VkCommandBuffer commands = recordingContext->commandBuffer; 474 VkImage srcImage = ToBackend(srcCopy.texture)->GetHandle(); 475 VkImage dstImage = ToBackend(dstCopy.texture)->GetHandle(); 476 477 tempBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); 478 VkBufferImageCopy srcToTempBufferRegion = 479 ComputeBufferImageCopyRegion(tempBufferCopy, srcCopy, copySize); 480 481 // The Dawn CopySrc usage is always mapped to GENERAL 482 device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, 483 tempBuffer->GetHandle(), 1, &srcToTempBufferRegion); 484 485 tempBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); 486 VkBufferImageCopy tempBufferToDstRegion = 487 ComputeBufferImageCopyRegion(tempBufferCopy, dstCopy, copySize); 488 489 // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the 490 // copy command. 491 device->fn.CmdCopyBufferToImage(commands, tempBuffer->GetHandle(), dstImage, 492 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, 493 &tempBufferToDstRegion); 494 495 recordingContext->tempBuffers.emplace_back(tempBuffer); 496 } 497 RecordCommands(CommandRecordingContext* recordingContext)498 MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* recordingContext) { 499 Device* device = ToBackend(GetDevice()); 500 VkCommandBuffer commands = recordingContext->commandBuffer; 501 502 // Records the necessary barriers for the resource usage pre-computed by the frontend. 503 // And resets the used query sets which are rewritten on the render pass. 504 auto PrepareResourcesForRenderPass = [](Device* device, 505 CommandRecordingContext* recordingContext, 506 const RenderPassResourceUsage& usages) { 507 TransitionAndClearForSyncScope(device, recordingContext, usages); 508 509 // Reset all query set used on current render pass together before beginning render pass 510 // because the reset command must be called outside render pass 511 for (size_t i = 0; i < usages.querySets.size(); ++i) { 512 ResetUsedQuerySetsOnRenderPass(device, recordingContext->commandBuffer, 513 usages.querySets[i], usages.queryAvailabilities[i]); 514 } 515 }; 516 517 size_t nextComputePassNumber = 0; 518 size_t nextRenderPassNumber = 0; 519 520 Command type; 521 while (mCommands.NextCommandId(&type)) { 522 switch (type) { 523 case Command::CopyBufferToBuffer: { 524 CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>(); 525 if (copy->size == 0) { 526 // Skip no-op copies. 527 break; 528 } 529 530 Buffer* srcBuffer = ToBackend(copy->source.Get()); 531 Buffer* dstBuffer = ToBackend(copy->destination.Get()); 532 533 srcBuffer->EnsureDataInitialized(recordingContext); 534 dstBuffer->EnsureDataInitializedAsDestination( 535 recordingContext, copy->destinationOffset, copy->size); 536 537 srcBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); 538 dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); 539 540 VkBufferCopy region; 541 region.srcOffset = copy->sourceOffset; 542 region.dstOffset = copy->destinationOffset; 543 region.size = copy->size; 544 545 VkBuffer srcHandle = srcBuffer->GetHandle(); 546 VkBuffer dstHandle = dstBuffer->GetHandle(); 547 device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, ®ion); 548 break; 549 } 550 551 case Command::CopyBufferToTexture: { 552 CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>(); 553 if (copy->copySize.width == 0 || copy->copySize.height == 0 || 554 copy->copySize.depthOrArrayLayers == 0) { 555 // Skip no-op copies. 556 continue; 557 } 558 auto& src = copy->source; 559 auto& dst = copy->destination; 560 561 ToBackend(src.buffer)->EnsureDataInitialized(recordingContext); 562 563 VkBufferImageCopy region = 564 ComputeBufferImageCopyRegion(src, dst, copy->copySize); 565 VkImageSubresourceLayers subresource = region.imageSubresource; 566 567 ASSERT(dst.texture->GetDimension() != wgpu::TextureDimension::e1D); 568 SubresourceRange range = 569 GetSubresourcesAffectedByCopy(copy->destination, copy->copySize); 570 571 if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, 572 subresource.mipLevel)) { 573 // Since texture has been overwritten, it has been "initialized" 574 dst.texture->SetIsSubresourceContentInitialized(true, range); 575 } else { 576 ToBackend(dst.texture) 577 ->EnsureSubresourceContentInitialized(recordingContext, range); 578 } 579 ToBackend(src.buffer) 580 ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); 581 ToBackend(dst.texture) 582 ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range); 583 VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle(); 584 VkImage dstImage = ToBackend(dst.texture)->GetHandle(); 585 586 // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the 587 // copy command. 588 device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage, 589 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, 590 ®ion); 591 break; 592 } 593 594 case Command::CopyTextureToBuffer: { 595 CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>(); 596 if (copy->copySize.width == 0 || copy->copySize.height == 0 || 597 copy->copySize.depthOrArrayLayers == 0) { 598 // Skip no-op copies. 599 continue; 600 } 601 auto& src = copy->source; 602 auto& dst = copy->destination; 603 604 ToBackend(dst.buffer) 605 ->EnsureDataInitializedAsDestination(recordingContext, copy); 606 607 VkBufferImageCopy region = 608 ComputeBufferImageCopyRegion(dst, src, copy->copySize); 609 610 ASSERT(src.texture->GetDimension() != wgpu::TextureDimension::e1D); 611 SubresourceRange range = 612 GetSubresourcesAffectedByCopy(copy->source, copy->copySize); 613 614 ToBackend(src.texture) 615 ->EnsureSubresourceContentInitialized(recordingContext, range); 616 617 ToBackend(src.texture) 618 ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, range); 619 ToBackend(dst.buffer) 620 ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); 621 622 VkImage srcImage = ToBackend(src.texture)->GetHandle(); 623 VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle(); 624 // The Dawn CopySrc usage is always mapped to GENERAL 625 device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, 626 dstBuffer, 1, ®ion); 627 break; 628 } 629 630 case Command::CopyTextureToTexture: { 631 CopyTextureToTextureCmd* copy = 632 mCommands.NextCommand<CopyTextureToTextureCmd>(); 633 if (copy->copySize.width == 0 || copy->copySize.height == 0 || 634 copy->copySize.depthOrArrayLayers == 0) { 635 // Skip no-op copies. 636 continue; 637 } 638 TextureCopy& src = copy->source; 639 TextureCopy& dst = copy->destination; 640 SubresourceRange srcRange = GetSubresourcesAffectedByCopy(src, copy->copySize); 641 SubresourceRange dstRange = GetSubresourcesAffectedByCopy(dst, copy->copySize); 642 643 ToBackend(src.texture) 644 ->EnsureSubresourceContentInitialized(recordingContext, srcRange); 645 if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, 646 dst.mipLevel)) { 647 // Since destination texture has been overwritten, it has been "initialized" 648 dst.texture->SetIsSubresourceContentInitialized(true, dstRange); 649 } else { 650 ToBackend(dst.texture) 651 ->EnsureSubresourceContentInitialized(recordingContext, dstRange); 652 } 653 654 if (src.texture.Get() == dst.texture.Get() && src.mipLevel == dst.mipLevel) { 655 // When there are overlapped subresources, the layout of the overlapped 656 // subresources should all be GENERAL instead of what we set now. Currently 657 // it is not allowed to copy with overlapped subresources, but we still 658 // add the ASSERT here as a reminder for this possible misuse. 659 ASSERT(!IsRangeOverlapped(src.origin.z, dst.origin.z, 660 copy->copySize.depthOrArrayLayers)); 661 } 662 663 // TODO after Yunchao's CL 664 ToBackend(src.texture) 665 ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, 666 srcRange); 667 ToBackend(dst.texture) 668 ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, 669 dstRange); 670 671 // In some situations we cannot do texture-to-texture copies with vkCmdCopyImage 672 // because as Vulkan SPEC always validates image copies with the virtual size of 673 // the image subresource, when the extent that fits in the copy region of one 674 // subresource but does not fit in the one of another subresource, we will fail 675 // to find a valid extent to satisfy the requirements on both source and 676 // destination image subresource. For example, when the source is the first 677 // level of a 16x16 texture in BC format, and the destination is the third level 678 // of a 60x60 texture in the same format, neither 16x16 nor 15x15 is valid as 679 // the extent of vkCmdCopyImage. 680 // Our workaround for this issue is replacing the texture-to-texture copy with 681 // one texture-to-buffer copy and one buffer-to-texture copy. 682 bool copyUsingTemporaryBuffer = 683 device->IsToggleEnabled( 684 Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy) && 685 src.texture->GetFormat().isCompressed && 686 !HasSameTextureCopyExtent(src, dst, copy->copySize); 687 688 if (!copyUsingTemporaryBuffer) { 689 VkImage srcImage = ToBackend(src.texture)->GetHandle(); 690 VkImage dstImage = ToBackend(dst.texture)->GetHandle(); 691 692 for (Aspect aspect : IterateEnumMask(src.texture->GetFormat().aspects)) { 693 ASSERT(dst.texture->GetFormat().aspects & aspect); 694 VkImageCopy region = 695 ComputeImageCopyRegion(src, dst, copy->copySize, aspect); 696 697 // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after 698 // the copy command. 699 device->fn.CmdCopyImage(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, 700 dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 701 1, ®ion); 702 } 703 } else { 704 RecordCopyImageWithTemporaryBuffer(recordingContext, src, dst, 705 copy->copySize); 706 } 707 708 break; 709 } 710 711 case Command::ClearBuffer: { 712 ClearBufferCmd* cmd = mCommands.NextCommand<ClearBufferCmd>(); 713 if (cmd->size == 0) { 714 // Skip no-op fills. 715 break; 716 } 717 718 Buffer* dstBuffer = ToBackend(cmd->buffer.Get()); 719 bool clearedToZero = dstBuffer->EnsureDataInitializedAsDestination( 720 recordingContext, cmd->offset, cmd->size); 721 722 if (!clearedToZero) { 723 dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); 724 device->fn.CmdFillBuffer(recordingContext->commandBuffer, 725 dstBuffer->GetHandle(), cmd->offset, cmd->size, 726 0u); 727 } 728 729 break; 730 } 731 732 case Command::BeginRenderPass: { 733 BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>(); 734 735 PrepareResourcesForRenderPass( 736 device, recordingContext, 737 GetResourceUsages().renderPasses[nextRenderPassNumber]); 738 739 LazyClearRenderPassAttachments(cmd); 740 DAWN_TRY(RecordRenderPass(recordingContext, cmd)); 741 742 nextRenderPassNumber++; 743 break; 744 } 745 746 case Command::BeginComputePass: { 747 mCommands.NextCommand<BeginComputePassCmd>(); 748 749 DAWN_TRY(RecordComputePass( 750 recordingContext, 751 GetResourceUsages().computePasses[nextComputePassNumber])); 752 753 nextComputePassNumber++; 754 break; 755 } 756 757 case Command::ResolveQuerySet: { 758 ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>(); 759 QuerySet* querySet = ToBackend(cmd->querySet.Get()); 760 Buffer* destination = ToBackend(cmd->destination.Get()); 761 762 destination->EnsureDataInitializedAsDestination( 763 recordingContext, cmd->destinationOffset, 764 cmd->queryCount * sizeof(uint64_t)); 765 766 // vkCmdCopyQueryPoolResults only can retrieve available queries because 767 // VK_QUERY_RESULT_WAIT_BIT is set. In order to resolve the unavailable queries 768 // as 0s, we need to clear the resolving region of the destination buffer to 0s. 769 auto startIt = querySet->GetQueryAvailability().begin() + cmd->firstQuery; 770 auto endIt = querySet->GetQueryAvailability().begin() + cmd->firstQuery + 771 cmd->queryCount; 772 bool hasUnavailableQueries = std::find(startIt, endIt, false) != endIt; 773 if (hasUnavailableQueries) { 774 destination->TransitionUsageNow(recordingContext, 775 wgpu::BufferUsage::CopyDst); 776 device->fn.CmdFillBuffer(commands, destination->GetHandle(), 777 cmd->destinationOffset, 778 cmd->queryCount * sizeof(uint64_t), 0u); 779 } 780 781 destination->TransitionUsageNow(recordingContext, 782 wgpu::BufferUsage::QueryResolve); 783 784 RecordResolveQuerySetCmd(commands, device, querySet, cmd->firstQuery, 785 cmd->queryCount, destination, cmd->destinationOffset); 786 787 break; 788 } 789 790 case Command::WriteTimestamp: { 791 WriteTimestampCmd* cmd = mCommands.NextCommand<WriteTimestampCmd>(); 792 793 // The query must be reset between uses. 794 device->fn.CmdResetQueryPool(commands, ToBackend(cmd->querySet)->GetHandle(), 795 cmd->queryIndex, 1); 796 797 RecordWriteTimestampCmd(recordingContext, device, cmd); 798 break; 799 } 800 801 case Command::InsertDebugMarker: { 802 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 803 InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>(); 804 const char* label = mCommands.NextData<char>(cmd->length + 1); 805 VkDebugUtilsLabelEXT utilsLabel; 806 utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; 807 utilsLabel.pNext = nullptr; 808 utilsLabel.pLabelName = label; 809 // Default color to black 810 utilsLabel.color[0] = 0.0; 811 utilsLabel.color[1] = 0.0; 812 utilsLabel.color[2] = 0.0; 813 utilsLabel.color[3] = 1.0; 814 device->fn.CmdInsertDebugUtilsLabelEXT(commands, &utilsLabel); 815 } else { 816 SkipCommand(&mCommands, Command::InsertDebugMarker); 817 } 818 break; 819 } 820 821 case Command::PopDebugGroup: { 822 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 823 mCommands.NextCommand<PopDebugGroupCmd>(); 824 device->fn.CmdEndDebugUtilsLabelEXT(commands); 825 } else { 826 SkipCommand(&mCommands, Command::PopDebugGroup); 827 } 828 break; 829 } 830 831 case Command::PushDebugGroup: { 832 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 833 PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>(); 834 const char* label = mCommands.NextData<char>(cmd->length + 1); 835 VkDebugUtilsLabelEXT utilsLabel; 836 utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; 837 utilsLabel.pNext = nullptr; 838 utilsLabel.pLabelName = label; 839 // Default color to black 840 utilsLabel.color[0] = 0.0; 841 utilsLabel.color[1] = 0.0; 842 utilsLabel.color[2] = 0.0; 843 utilsLabel.color[3] = 1.0; 844 device->fn.CmdBeginDebugUtilsLabelEXT(commands, &utilsLabel); 845 } else { 846 SkipCommand(&mCommands, Command::PushDebugGroup); 847 } 848 break; 849 } 850 851 case Command::WriteBuffer: { 852 WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>(); 853 const uint64_t offset = write->offset; 854 const uint64_t size = write->size; 855 if (size == 0) { 856 continue; 857 } 858 859 Buffer* dstBuffer = ToBackend(write->buffer.Get()); 860 uint8_t* data = mCommands.NextData<uint8_t>(size); 861 Device* device = ToBackend(GetDevice()); 862 863 UploadHandle uploadHandle; 864 DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( 865 size, device->GetPendingCommandSerial(), 866 kCopyBufferToBufferOffsetAlignment)); 867 ASSERT(uploadHandle.mappedBuffer != nullptr); 868 memcpy(uploadHandle.mappedBuffer, data, size); 869 870 dstBuffer->EnsureDataInitializedAsDestination(recordingContext, offset, size); 871 872 dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); 873 874 VkBufferCopy copy; 875 copy.srcOffset = uploadHandle.startOffset; 876 copy.dstOffset = offset; 877 copy.size = size; 878 879 device->fn.CmdCopyBuffer( 880 commands, ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle(), 881 dstBuffer->GetHandle(), 1, ©); 882 break; 883 } 884 885 default: 886 break; 887 } 888 } 889 890 return {}; 891 } 892 RecordComputePass(CommandRecordingContext* recordingContext, const ComputePassResourceUsage& resourceUsages)893 MaybeError CommandBuffer::RecordComputePass(CommandRecordingContext* recordingContext, 894 const ComputePassResourceUsage& resourceUsages) { 895 Device* device = ToBackend(GetDevice()); 896 VkCommandBuffer commands = recordingContext->commandBuffer; 897 898 uint64_t currentDispatch = 0; 899 DescriptorSetTracker descriptorSets = {}; 900 901 Command type; 902 while (mCommands.NextCommandId(&type)) { 903 switch (type) { 904 case Command::EndComputePass: { 905 mCommands.NextCommand<EndComputePassCmd>(); 906 return {}; 907 } 908 909 case Command::Dispatch: { 910 DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>(); 911 912 TransitionAndClearForSyncScope(device, recordingContext, 913 resourceUsages.dispatchUsages[currentDispatch]); 914 descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE); 915 916 device->fn.CmdDispatch(commands, dispatch->x, dispatch->y, dispatch->z); 917 currentDispatch++; 918 break; 919 } 920 921 case Command::DispatchIndirect: { 922 DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>(); 923 VkBuffer indirectBuffer = ToBackend(dispatch->indirectBuffer)->GetHandle(); 924 925 TransitionAndClearForSyncScope(device, recordingContext, 926 resourceUsages.dispatchUsages[currentDispatch]); 927 descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE); 928 929 device->fn.CmdDispatchIndirect( 930 commands, indirectBuffer, 931 static_cast<VkDeviceSize>(dispatch->indirectOffset)); 932 currentDispatch++; 933 break; 934 } 935 936 case Command::SetBindGroup: { 937 SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>(); 938 939 BindGroup* bindGroup = ToBackend(cmd->group.Get()); 940 uint32_t* dynamicOffsets = nullptr; 941 if (cmd->dynamicOffsetCount > 0) { 942 dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount); 943 } 944 945 descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount, 946 dynamicOffsets); 947 break; 948 } 949 950 case Command::SetComputePipeline: { 951 SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>(); 952 ComputePipeline* pipeline = ToBackend(cmd->pipeline).Get(); 953 954 device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_COMPUTE, 955 pipeline->GetHandle()); 956 descriptorSets.OnSetPipeline(pipeline); 957 break; 958 } 959 960 case Command::InsertDebugMarker: { 961 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 962 InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>(); 963 const char* label = mCommands.NextData<char>(cmd->length + 1); 964 VkDebugUtilsLabelEXT utilsLabel; 965 utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; 966 utilsLabel.pNext = nullptr; 967 utilsLabel.pLabelName = label; 968 // Default color to black 969 utilsLabel.color[0] = 0.0; 970 utilsLabel.color[1] = 0.0; 971 utilsLabel.color[2] = 0.0; 972 utilsLabel.color[3] = 1.0; 973 device->fn.CmdInsertDebugUtilsLabelEXT(commands, &utilsLabel); 974 } else { 975 SkipCommand(&mCommands, Command::InsertDebugMarker); 976 } 977 break; 978 } 979 980 case Command::PopDebugGroup: { 981 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 982 mCommands.NextCommand<PopDebugGroupCmd>(); 983 device->fn.CmdEndDebugUtilsLabelEXT(commands); 984 } else { 985 SkipCommand(&mCommands, Command::PopDebugGroup); 986 } 987 break; 988 } 989 990 case Command::PushDebugGroup: { 991 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 992 PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>(); 993 const char* label = mCommands.NextData<char>(cmd->length + 1); 994 VkDebugUtilsLabelEXT utilsLabel; 995 utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; 996 utilsLabel.pNext = nullptr; 997 utilsLabel.pLabelName = label; 998 // Default color to black 999 utilsLabel.color[0] = 0.0; 1000 utilsLabel.color[1] = 0.0; 1001 utilsLabel.color[2] = 0.0; 1002 utilsLabel.color[3] = 1.0; 1003 device->fn.CmdBeginDebugUtilsLabelEXT(commands, &utilsLabel); 1004 } else { 1005 SkipCommand(&mCommands, Command::PushDebugGroup); 1006 } 1007 break; 1008 } 1009 1010 case Command::WriteTimestamp: { 1011 WriteTimestampCmd* cmd = mCommands.NextCommand<WriteTimestampCmd>(); 1012 1013 // The query must be reset between uses. 1014 device->fn.CmdResetQueryPool(commands, ToBackend(cmd->querySet)->GetHandle(), 1015 cmd->queryIndex, 1); 1016 1017 RecordWriteTimestampCmd(recordingContext, device, cmd); 1018 break; 1019 } 1020 1021 default: 1022 UNREACHABLE(); 1023 } 1024 } 1025 1026 // EndComputePass should have been called 1027 UNREACHABLE(); 1028 } 1029 RecordRenderPass(CommandRecordingContext* recordingContext, BeginRenderPassCmd* renderPassCmd)1030 MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* recordingContext, 1031 BeginRenderPassCmd* renderPassCmd) { 1032 Device* device = ToBackend(GetDevice()); 1033 VkCommandBuffer commands = recordingContext->commandBuffer; 1034 1035 DAWN_TRY(RecordBeginRenderPass(recordingContext, device, renderPassCmd)); 1036 1037 // Set the default value for the dynamic state 1038 { 1039 device->fn.CmdSetLineWidth(commands, 1.0f); 1040 device->fn.CmdSetDepthBounds(commands, 0.0f, 1.0f); 1041 1042 device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 0); 1043 1044 float blendConstants[4] = { 1045 0.0f, 1046 0.0f, 1047 0.0f, 1048 0.0f, 1049 }; 1050 device->fn.CmdSetBlendConstants(commands, blendConstants); 1051 1052 // The viewport and scissor default to cover all of the attachments 1053 VkViewport viewport; 1054 viewport.x = 0.0f; 1055 viewport.y = static_cast<float>(renderPassCmd->height); 1056 viewport.width = static_cast<float>(renderPassCmd->width); 1057 viewport.height = -static_cast<float>(renderPassCmd->height); 1058 viewport.minDepth = 0.0f; 1059 viewport.maxDepth = 1.0f; 1060 device->fn.CmdSetViewport(commands, 0, 1, &viewport); 1061 1062 VkRect2D scissorRect; 1063 scissorRect.offset.x = 0; 1064 scissorRect.offset.y = 0; 1065 scissorRect.extent.width = renderPassCmd->width; 1066 scissorRect.extent.height = renderPassCmd->height; 1067 device->fn.CmdSetScissor(commands, 0, 1, &scissorRect); 1068 } 1069 1070 DescriptorSetTracker descriptorSets = {}; 1071 RenderPipeline* lastPipeline = nullptr; 1072 1073 auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) { 1074 switch (type) { 1075 case Command::Draw: { 1076 DrawCmd* draw = iter->NextCommand<DrawCmd>(); 1077 1078 descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); 1079 device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount, 1080 draw->firstVertex, draw->firstInstance); 1081 break; 1082 } 1083 1084 case Command::DrawIndexed: { 1085 DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>(); 1086 1087 descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); 1088 device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount, 1089 draw->firstIndex, draw->baseVertex, 1090 draw->firstInstance); 1091 break; 1092 } 1093 1094 case Command::DrawIndirect: { 1095 DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>(); 1096 Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); 1097 1098 descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); 1099 device->fn.CmdDrawIndirect(commands, buffer->GetHandle(), 1100 static_cast<VkDeviceSize>(draw->indirectOffset), 1, 1101 0); 1102 break; 1103 } 1104 1105 case Command::DrawIndexedIndirect: { 1106 DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>(); 1107 Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); 1108 ASSERT(buffer != nullptr); 1109 1110 descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); 1111 device->fn.CmdDrawIndexedIndirect( 1112 commands, buffer->GetHandle(), 1113 static_cast<VkDeviceSize>(draw->indirectOffset), 1, 0); 1114 break; 1115 } 1116 1117 case Command::InsertDebugMarker: { 1118 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 1119 InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>(); 1120 const char* label = iter->NextData<char>(cmd->length + 1); 1121 VkDebugUtilsLabelEXT utilsLabel; 1122 utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; 1123 utilsLabel.pNext = nullptr; 1124 utilsLabel.pLabelName = label; 1125 // Default color to black 1126 utilsLabel.color[0] = 0.0; 1127 utilsLabel.color[1] = 0.0; 1128 utilsLabel.color[2] = 0.0; 1129 utilsLabel.color[3] = 1.0; 1130 device->fn.CmdInsertDebugUtilsLabelEXT(commands, &utilsLabel); 1131 } else { 1132 SkipCommand(iter, Command::InsertDebugMarker); 1133 } 1134 break; 1135 } 1136 1137 case Command::PopDebugGroup: { 1138 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 1139 iter->NextCommand<PopDebugGroupCmd>(); 1140 device->fn.CmdEndDebugUtilsLabelEXT(commands); 1141 } else { 1142 SkipCommand(iter, Command::PopDebugGroup); 1143 } 1144 break; 1145 } 1146 1147 case Command::PushDebugGroup: { 1148 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) { 1149 PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>(); 1150 const char* label = iter->NextData<char>(cmd->length + 1); 1151 VkDebugUtilsLabelEXT utilsLabel; 1152 utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; 1153 utilsLabel.pNext = nullptr; 1154 utilsLabel.pLabelName = label; 1155 // Default color to black 1156 utilsLabel.color[0] = 0.0; 1157 utilsLabel.color[1] = 0.0; 1158 utilsLabel.color[2] = 0.0; 1159 utilsLabel.color[3] = 1.0; 1160 device->fn.CmdBeginDebugUtilsLabelEXT(commands, &utilsLabel); 1161 } else { 1162 SkipCommand(iter, Command::PushDebugGroup); 1163 } 1164 break; 1165 } 1166 1167 case Command::SetBindGroup: { 1168 SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>(); 1169 BindGroup* bindGroup = ToBackend(cmd->group.Get()); 1170 uint32_t* dynamicOffsets = nullptr; 1171 if (cmd->dynamicOffsetCount > 0) { 1172 dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount); 1173 } 1174 1175 descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount, 1176 dynamicOffsets); 1177 break; 1178 } 1179 1180 case Command::SetIndexBuffer: { 1181 SetIndexBufferCmd* cmd = iter->NextCommand<SetIndexBufferCmd>(); 1182 VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle(); 1183 1184 device->fn.CmdBindIndexBuffer(commands, indexBuffer, cmd->offset, 1185 VulkanIndexType(cmd->format)); 1186 break; 1187 } 1188 1189 case Command::SetRenderPipeline: { 1190 SetRenderPipelineCmd* cmd = iter->NextCommand<SetRenderPipelineCmd>(); 1191 RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); 1192 1193 device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, 1194 pipeline->GetHandle()); 1195 lastPipeline = pipeline; 1196 1197 descriptorSets.OnSetPipeline(pipeline); 1198 break; 1199 } 1200 1201 case Command::SetVertexBuffer: { 1202 SetVertexBufferCmd* cmd = iter->NextCommand<SetVertexBufferCmd>(); 1203 VkBuffer buffer = ToBackend(cmd->buffer)->GetHandle(); 1204 VkDeviceSize offset = static_cast<VkDeviceSize>(cmd->offset); 1205 1206 device->fn.CmdBindVertexBuffers(commands, static_cast<uint8_t>(cmd->slot), 1, 1207 &*buffer, &offset); 1208 break; 1209 } 1210 1211 default: 1212 UNREACHABLE(); 1213 break; 1214 } 1215 }; 1216 1217 Command type; 1218 while (mCommands.NextCommandId(&type)) { 1219 switch (type) { 1220 case Command::EndRenderPass: { 1221 mCommands.NextCommand<EndRenderPassCmd>(); 1222 device->fn.CmdEndRenderPass(commands); 1223 return {}; 1224 } 1225 1226 case Command::SetBlendConstant: { 1227 SetBlendConstantCmd* cmd = mCommands.NextCommand<SetBlendConstantCmd>(); 1228 const std::array<float, 4> blendConstants = ConvertToFloatColor(cmd->color); 1229 device->fn.CmdSetBlendConstants(commands, blendConstants.data()); 1230 break; 1231 } 1232 1233 case Command::SetStencilReference: { 1234 SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>(); 1235 device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 1236 cmd->reference); 1237 break; 1238 } 1239 1240 case Command::SetViewport: { 1241 SetViewportCmd* cmd = mCommands.NextCommand<SetViewportCmd>(); 1242 VkViewport viewport; 1243 viewport.x = cmd->x; 1244 viewport.y = cmd->y + cmd->height; 1245 viewport.width = cmd->width; 1246 viewport.height = -cmd->height; 1247 viewport.minDepth = cmd->minDepth; 1248 viewport.maxDepth = cmd->maxDepth; 1249 1250 // Vulkan disallows width = 0, but VK_KHR_maintenance1 which we require allows 1251 // height = 0 so use that to do an empty viewport. 1252 if (viewport.width == 0) { 1253 viewport.height = 0; 1254 1255 // Set the viewport x range to a range that's always valid. 1256 viewport.x = 0; 1257 viewport.width = 1; 1258 } 1259 1260 device->fn.CmdSetViewport(commands, 0, 1, &viewport); 1261 break; 1262 } 1263 1264 case Command::SetScissorRect: { 1265 SetScissorRectCmd* cmd = mCommands.NextCommand<SetScissorRectCmd>(); 1266 VkRect2D rect; 1267 rect.offset.x = cmd->x; 1268 rect.offset.y = cmd->y; 1269 rect.extent.width = cmd->width; 1270 rect.extent.height = cmd->height; 1271 1272 device->fn.CmdSetScissor(commands, 0, 1, &rect); 1273 break; 1274 } 1275 1276 case Command::ExecuteBundles: { 1277 ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>(); 1278 auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count); 1279 1280 for (uint32_t i = 0; i < cmd->count; ++i) { 1281 CommandIterator* iter = bundles[i]->GetCommands(); 1282 iter->Reset(); 1283 while (iter->NextCommandId(&type)) { 1284 EncodeRenderBundleCommand(iter, type); 1285 } 1286 } 1287 break; 1288 } 1289 1290 case Command::BeginOcclusionQuery: { 1291 BeginOcclusionQueryCmd* cmd = mCommands.NextCommand<BeginOcclusionQueryCmd>(); 1292 1293 device->fn.CmdBeginQuery(commands, ToBackend(cmd->querySet.Get())->GetHandle(), 1294 cmd->queryIndex, 0); 1295 break; 1296 } 1297 1298 case Command::EndOcclusionQuery: { 1299 EndOcclusionQueryCmd* cmd = mCommands.NextCommand<EndOcclusionQueryCmd>(); 1300 1301 device->fn.CmdEndQuery(commands, ToBackend(cmd->querySet.Get())->GetHandle(), 1302 cmd->queryIndex); 1303 break; 1304 } 1305 1306 case Command::WriteTimestamp: { 1307 WriteTimestampCmd* cmd = mCommands.NextCommand<WriteTimestampCmd>(); 1308 1309 RecordWriteTimestampCmd(recordingContext, device, cmd); 1310 break; 1311 } 1312 1313 default: { 1314 EncodeRenderBundleCommand(&mCommands, type); 1315 break; 1316 } 1317 } 1318 } 1319 1320 // EndRenderPass should have been called 1321 UNREACHABLE(); 1322 } 1323 1324 }} // namespace dawn_native::vulkan 1325