| // Copyright 2017 The Dawn Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "dawn_native/vulkan/CommandBufferVk.h" | |
| #include "dawn_native/BindGroupAndStorageBarrierTracker.h" | |
| #include "dawn_native/CommandEncoder.h" | |
| #include "dawn_native/CommandValidation.h" | |
| #include "dawn_native/Commands.h" | |
| #include "dawn_native/RenderBundle.h" | |
| #include "dawn_native/vulkan/BindGroupVk.h" | |
| #include "dawn_native/vulkan/BufferVk.h" | |
| #include "dawn_native/vulkan/CommandRecordingContext.h" | |
| #include "dawn_native/vulkan/ComputePipelineVk.h" | |
| #include "dawn_native/vulkan/DeviceVk.h" | |
| #include "dawn_native/vulkan/FencedDeleter.h" | |
| #include "dawn_native/vulkan/PipelineLayoutVk.h" | |
| #include "dawn_native/vulkan/RenderPassCache.h" | |
| #include "dawn_native/vulkan/RenderPipelineVk.h" | |
| #include "dawn_native/vulkan/TextureVk.h" | |
| #include "dawn_native/vulkan/UtilsVulkan.h" | |
| #include "dawn_native/vulkan/VulkanError.h" | |
| namespace dawn_native { namespace vulkan { | |
| namespace { | |
| VkIndexType VulkanIndexType(wgpu::IndexFormat format) { | |
| switch (format) { | |
| case wgpu::IndexFormat::Uint16: | |
| return VK_INDEX_TYPE_UINT16; | |
| case wgpu::IndexFormat::Uint32: | |
| return VK_INDEX_TYPE_UINT32; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| bool HasSameTextureCopyExtent(const TextureCopy& srcCopy, | |
| const TextureCopy& dstCopy, | |
| const Extent3D& copySize) { | |
| Extent3D imageExtentSrc = ComputeTextureCopyExtent(srcCopy, copySize); | |
| Extent3D imageExtentDst = ComputeTextureCopyExtent(dstCopy, copySize); | |
| return imageExtentSrc.width == imageExtentDst.width && | |
| imageExtentSrc.height == imageExtentDst.height && | |
| imageExtentSrc.depth == imageExtentDst.depth; | |
| } | |
| VkImageCopy ComputeImageCopyRegion(const TextureCopy& srcCopy, | |
| const TextureCopy& dstCopy, | |
| const Extent3D& copySize) { | |
| const Texture* srcTexture = ToBackend(srcCopy.texture.Get()); | |
| const Texture* dstTexture = ToBackend(dstCopy.texture.Get()); | |
| VkImageCopy region; | |
| // TODO(jiawei.shao@intel.com): support 1D and 3D textures | |
| ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D && | |
| dstTexture->GetDimension() == wgpu::TextureDimension::e2D); | |
| region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask(); | |
| region.srcSubresource.mipLevel = srcCopy.mipLevel; | |
| region.srcSubresource.baseArrayLayer = srcCopy.origin.z; | |
| region.srcSubresource.layerCount = copySize.depth; | |
| region.srcOffset.x = srcCopy.origin.x; | |
| region.srcOffset.y = srcCopy.origin.y; | |
| region.srcOffset.z = 0; | |
| region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask(); | |
| region.dstSubresource.mipLevel = dstCopy.mipLevel; | |
| region.dstSubresource.baseArrayLayer = dstCopy.origin.z; | |
| region.dstSubresource.layerCount = copySize.depth; | |
| region.dstOffset.x = dstCopy.origin.x; | |
| region.dstOffset.y = dstCopy.origin.y; | |
| region.dstOffset.z = 0; | |
| ASSERT(HasSameTextureCopyExtent(srcCopy, dstCopy, copySize)); | |
| Extent3D imageExtent = ComputeTextureCopyExtent(dstCopy, copySize); | |
| region.extent.width = imageExtent.width; | |
| region.extent.height = imageExtent.height; | |
| region.extent.depth = 1; | |
| return region; | |
| } | |
| void ApplyDescriptorSets( | |
| Device* device, | |
| VkCommandBuffer commands, | |
| VkPipelineBindPoint bindPoint, | |
| VkPipelineLayout pipelineLayout, | |
| const BindGroupLayoutMask& bindGroupsToApply, | |
| const ityp::array<BindGroupIndex, BindGroupBase*, kMaxBindGroups>& bindGroups, | |
| const ityp::array<BindGroupIndex, uint32_t, kMaxBindGroups>& dynamicOffsetCounts, | |
| const ityp::array<BindGroupIndex, | |
| std::array<uint32_t, kMaxBindingsPerGroup>, | |
| kMaxBindGroups>& dynamicOffsets) { | |
| for (BindGroupIndex dirtyIndex : IterateBitSet(bindGroupsToApply)) { | |
| VkDescriptorSet set = ToBackend(bindGroups[dirtyIndex])->GetHandle(); | |
| const uint32_t* dynamicOffset = dynamicOffsetCounts[dirtyIndex] > 0 | |
| ? dynamicOffsets[dirtyIndex].data() | |
| : nullptr; | |
| device->fn.CmdBindDescriptorSets(commands, bindPoint, pipelineLayout, | |
| static_cast<uint32_t>(dirtyIndex), 1, &*set, | |
| dynamicOffsetCounts[dirtyIndex], dynamicOffset); | |
| } | |
| } | |
| class RenderDescriptorSetTracker : public BindGroupTrackerBase<true, uint32_t> { | |
| public: | |
| RenderDescriptorSetTracker() = default; | |
| void Apply(Device* device, | |
| CommandRecordingContext* recordingContext, | |
| VkPipelineBindPoint bindPoint) { | |
| ApplyDescriptorSets(device, recordingContext->commandBuffer, bindPoint, | |
| ToBackend(mPipelineLayout)->GetHandle(), | |
| mDirtyBindGroupsObjectChangedOrIsDynamic, mBindGroups, | |
| mDynamicOffsetCounts, mDynamicOffsets); | |
| DidApply(); | |
| } | |
| }; | |
| class ComputeDescriptorSetTracker | |
| : public BindGroupAndStorageBarrierTrackerBase<true, uint32_t> { | |
| public: | |
| ComputeDescriptorSetTracker() = default; | |
| void Apply(Device* device, | |
| CommandRecordingContext* recordingContext, | |
| VkPipelineBindPoint bindPoint) { | |
| ApplyDescriptorSets(device, recordingContext->commandBuffer, bindPoint, | |
| ToBackend(mPipelineLayout)->GetHandle(), | |
| mDirtyBindGroupsObjectChangedOrIsDynamic, mBindGroups, | |
| mDynamicOffsetCounts, mDynamicOffsets); | |
| for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) { | |
| for (BindingIndex bindingIndex : | |
| IterateBitSet(mBindingsNeedingBarrier[index])) { | |
| switch (mBindingTypes[index][bindingIndex]) { | |
| case wgpu::BindingType::StorageBuffer: | |
| static_cast<Buffer*>(mBindings[index][bindingIndex]) | |
| ->TransitionUsageNow(recordingContext, | |
| wgpu::BufferUsage::Storage); | |
| break; | |
| case wgpu::BindingType::ReadonlyStorageTexture: | |
| case wgpu::BindingType::WriteonlyStorageTexture: { | |
| TextureViewBase* view = | |
| static_cast<TextureViewBase*>(mBindings[index][bindingIndex]); | |
| ToBackend(view->GetTexture()) | |
| ->TransitionUsageNow(recordingContext, | |
| wgpu::TextureUsage::Storage, | |
| view->GetSubresourceRange()); | |
| break; | |
| } | |
| case wgpu::BindingType::StorageTexture: | |
| // Not implemented. | |
| case wgpu::BindingType::UniformBuffer: | |
| case wgpu::BindingType::ReadonlyStorageBuffer: | |
| case wgpu::BindingType::Sampler: | |
| case wgpu::BindingType::ComparisonSampler: | |
| case wgpu::BindingType::SampledTexture: | |
| // Don't require barriers. | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| } | |
| DidApply(); | |
| } | |
| }; | |
| MaybeError RecordBeginRenderPass(CommandRecordingContext* recordingContext, | |
| Device* device, | |
| BeginRenderPassCmd* renderPass) { | |
| VkCommandBuffer commands = recordingContext->commandBuffer; | |
| // Query a VkRenderPass from the cache | |
| VkRenderPass renderPassVK = VK_NULL_HANDLE; | |
| { | |
| RenderPassCacheQuery query; | |
| for (uint32_t i : | |
| IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { | |
| const auto& attachmentInfo = renderPass->colorAttachments[i]; | |
| bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr; | |
| wgpu::LoadOp loadOp = attachmentInfo.loadOp; | |
| query.SetColor(i, attachmentInfo.view->GetFormat().format, loadOp, | |
| hasResolveTarget); | |
| } | |
| if (renderPass->attachmentState->HasDepthStencilAttachment()) { | |
| const auto& attachmentInfo = renderPass->depthStencilAttachment; | |
| query.SetDepthStencil(attachmentInfo.view->GetTexture()->GetFormat().format, | |
| attachmentInfo.depthLoadOp, attachmentInfo.stencilLoadOp); | |
| } | |
| query.SetSampleCount(renderPass->attachmentState->GetSampleCount()); | |
| DAWN_TRY_ASSIGN(renderPassVK, device->GetRenderPassCache()->GetRenderPass(query)); | |
| } | |
| // Create a framebuffer that will be used once for the render pass and gather the clear | |
| // values for the attachments at the same time. | |
| std::array<VkClearValue, kMaxColorAttachments + 1> clearValues; | |
| VkFramebuffer framebuffer = VK_NULL_HANDLE; | |
| uint32_t attachmentCount = 0; | |
| { | |
| // Fill in the attachment info that will be chained in the framebuffer create info. | |
| std::array<VkImageView, kMaxColorAttachments * 2 + 1> attachments; | |
| for (uint32_t i : | |
| IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { | |
| auto& attachmentInfo = renderPass->colorAttachments[i]; | |
| TextureView* view = ToBackend(attachmentInfo.view.Get()); | |
| attachments[attachmentCount] = view->GetHandle(); | |
| clearValues[attachmentCount].color.float32[0] = attachmentInfo.clearColor.r; | |
| clearValues[attachmentCount].color.float32[1] = attachmentInfo.clearColor.g; | |
| clearValues[attachmentCount].color.float32[2] = attachmentInfo.clearColor.b; | |
| clearValues[attachmentCount].color.float32[3] = attachmentInfo.clearColor.a; | |
| attachmentCount++; | |
| } | |
| if (renderPass->attachmentState->HasDepthStencilAttachment()) { | |
| auto& attachmentInfo = renderPass->depthStencilAttachment; | |
| TextureView* view = ToBackend(attachmentInfo.view.Get()); | |
| attachments[attachmentCount] = view->GetHandle(); | |
| clearValues[attachmentCount].depthStencil.depth = attachmentInfo.clearDepth; | |
| clearValues[attachmentCount].depthStencil.stencil = attachmentInfo.clearStencil; | |
| attachmentCount++; | |
| } | |
| for (uint32_t i : | |
| IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { | |
| if (renderPass->colorAttachments[i].resolveTarget.Get() != nullptr) { | |
| TextureView* view = | |
| ToBackend(renderPass->colorAttachments[i].resolveTarget.Get()); | |
| attachments[attachmentCount] = view->GetHandle(); | |
| attachmentCount++; | |
| } | |
| } | |
| // Chain attachments and create the framebuffer | |
| VkFramebufferCreateInfo createInfo; | |
| createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; | |
| createInfo.pNext = nullptr; | |
| createInfo.flags = 0; | |
| createInfo.renderPass = renderPassVK; | |
| createInfo.attachmentCount = attachmentCount; | |
| createInfo.pAttachments = AsVkArray(attachments.data()); | |
| createInfo.width = renderPass->width; | |
| createInfo.height = renderPass->height; | |
| createInfo.layers = 1; | |
| DAWN_TRY( | |
| CheckVkSuccess(device->fn.CreateFramebuffer(device->GetVkDevice(), &createInfo, | |
| nullptr, &*framebuffer), | |
| "CreateFramebuffer")); | |
| // We don't reuse VkFramebuffers so mark the framebuffer for deletion as soon as the | |
| // commands currently being recorded are finished. | |
| device->GetFencedDeleter()->DeleteWhenUnused(framebuffer); | |
| } | |
| VkRenderPassBeginInfo beginInfo; | |
| beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; | |
| beginInfo.pNext = nullptr; | |
| beginInfo.renderPass = renderPassVK; | |
| beginInfo.framebuffer = framebuffer; | |
| beginInfo.renderArea.offset.x = 0; | |
| beginInfo.renderArea.offset.y = 0; | |
| beginInfo.renderArea.extent.width = renderPass->width; | |
| beginInfo.renderArea.extent.height = renderPass->height; | |
| beginInfo.clearValueCount = attachmentCount; | |
| beginInfo.pClearValues = clearValues.data(); | |
| device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE); | |
| return {}; | |
| } | |
| } // anonymous namespace | |
| // static | |
| CommandBuffer* CommandBuffer::Create(CommandEncoder* encoder, | |
| const CommandBufferDescriptor* descriptor) { | |
| return new CommandBuffer(encoder, descriptor); | |
| } | |
| CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) | |
| : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) { | |
| } | |
| CommandBuffer::~CommandBuffer() { | |
| FreeCommands(&mCommands); | |
| } | |
| void CommandBuffer::RecordCopyImageWithTemporaryBuffer( | |
| CommandRecordingContext* recordingContext, | |
| const TextureCopy& srcCopy, | |
| const TextureCopy& dstCopy, | |
| const Extent3D& copySize) { | |
| ASSERT(srcCopy.texture->GetFormat().format == dstCopy.texture->GetFormat().format); | |
| dawn_native::Format format = srcCopy.texture->GetFormat(); | |
| ASSERT(copySize.width % format.blockWidth == 0); | |
| ASSERT(copySize.height % format.blockHeight == 0); | |
| // Create the temporary buffer. Note that We don't need to respect WebGPU's 256 alignment | |
| // because it isn't a hard constraint in Vulkan. | |
| uint64_t tempBufferSize = | |
| (copySize.width / format.blockWidth * copySize.height / format.blockHeight) * | |
| format.blockByteSize; | |
| BufferDescriptor tempBufferDescriptor; | |
| tempBufferDescriptor.size = tempBufferSize; | |
| tempBufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| Device* device = ToBackend(GetDevice()); | |
| Ref<Buffer> tempBuffer = AcquireRef(ToBackend(device->CreateBuffer(&tempBufferDescriptor))); | |
| BufferCopy tempBufferCopy; | |
| tempBufferCopy.buffer = tempBuffer.Get(); | |
| tempBufferCopy.rowsPerImage = copySize.height; | |
| tempBufferCopy.offset = 0; | |
| tempBufferCopy.bytesPerRow = copySize.width / format.blockWidth * format.blockByteSize; | |
| VkCommandBuffer commands = recordingContext->commandBuffer; | |
| VkImage srcImage = ToBackend(srcCopy.texture)->GetHandle(); | |
| VkImage dstImage = ToBackend(dstCopy.texture)->GetHandle(); | |
| tempBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); | |
| VkBufferImageCopy srcToTempBufferRegion = | |
| ComputeBufferImageCopyRegion(tempBufferCopy, srcCopy, copySize); | |
| // The Dawn CopySrc usage is always mapped to GENERAL | |
| device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, | |
| tempBuffer->GetHandle(), 1, &srcToTempBufferRegion); | |
| tempBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); | |
| VkBufferImageCopy tempBufferToDstRegion = | |
| ComputeBufferImageCopyRegion(tempBufferCopy, dstCopy, copySize); | |
| // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the | |
| // copy command. | |
| device->fn.CmdCopyBufferToImage(commands, tempBuffer->GetHandle(), dstImage, | |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, | |
| &tempBufferToDstRegion); | |
| recordingContext->tempBuffers.emplace_back(tempBuffer); | |
| } | |
| MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* recordingContext) { | |
| Device* device = ToBackend(GetDevice()); | |
| VkCommandBuffer commands = recordingContext->commandBuffer; | |
| // Records the necessary barriers for the resource usage pre-computed by the frontend | |
| auto TransitionForPass = [](Device* device, CommandRecordingContext* recordingContext, | |
| const PassResourceUsage& usages) { | |
| std::vector<VkBufferMemoryBarrier> bufferBarriers; | |
| std::vector<VkImageMemoryBarrier> imageBarriers; | |
| VkPipelineStageFlags srcStages = 0; | |
| VkPipelineStageFlags dstStages = 0; | |
| for (size_t i = 0; i < usages.buffers.size(); ++i) { | |
| Buffer* buffer = ToBackend(usages.buffers[i]); | |
| buffer->TransitionUsageNow(recordingContext, usages.bufferUsages[i], | |
| &bufferBarriers, &srcStages, &dstStages); | |
| } | |
| for (size_t i = 0; i < usages.textures.size(); ++i) { | |
| Texture* texture = ToBackend(usages.textures[i]); | |
| // Clear textures that are not output attachments. Output attachments will be | |
| // cleared in RecordBeginRenderPass by setting the loadop to clear when the | |
| // texture subresource has not been initialized before the render pass. | |
| if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) { | |
| texture->EnsureSubresourceContentInitialized(recordingContext, | |
| texture->GetAllSubresources()); | |
| } | |
| texture->TransitionUsageForPass(recordingContext, usages.textureUsages[i], | |
| &imageBarriers, &srcStages, &dstStages); | |
| } | |
| if (bufferBarriers.size() || imageBarriers.size()) { | |
| device->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, | |
| 0, 0, nullptr, bufferBarriers.size(), | |
| bufferBarriers.data(), imageBarriers.size(), | |
| imageBarriers.data()); | |
| } | |
| }; | |
| const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass; | |
| size_t nextPassNumber = 0; | |
| Command type; | |
| while (mCommands.NextCommandId(&type)) { | |
| switch (type) { | |
| case Command::CopyBufferToBuffer: { | |
| CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>(); | |
| Buffer* srcBuffer = ToBackend(copy->source.Get()); | |
| Buffer* dstBuffer = ToBackend(copy->destination.Get()); | |
| srcBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); | |
| dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); | |
| VkBufferCopy region; | |
| region.srcOffset = copy->sourceOffset; | |
| region.dstOffset = copy->destinationOffset; | |
| region.size = copy->size; | |
| VkBuffer srcHandle = srcBuffer->GetHandle(); | |
| VkBuffer dstHandle = dstBuffer->GetHandle(); | |
| device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, ®ion); | |
| break; | |
| } | |
| case Command::CopyBufferToTexture: { | |
| CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>(); | |
| auto& src = copy->source; | |
| auto& dst = copy->destination; | |
| VkBufferImageCopy region = | |
| ComputeBufferImageCopyRegion(src, dst, copy->copySize); | |
| VkImageSubresourceLayers subresource = region.imageSubresource; | |
| ASSERT(dst.texture->GetDimension() == wgpu::TextureDimension::e2D); | |
| SubresourceRange range = {subresource.mipLevel, 1, subresource.baseArrayLayer, | |
| subresource.layerCount}; | |
| if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, | |
| subresource.mipLevel)) { | |
| // Since texture has been overwritten, it has been "initialized" | |
| dst.texture->SetIsSubresourceContentInitialized(true, range); | |
| } else { | |
| ToBackend(dst.texture) | |
| ->EnsureSubresourceContentInitialized(recordingContext, range); | |
| } | |
| ToBackend(src.buffer) | |
| ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); | |
| ToBackend(dst.texture) | |
| ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range); | |
| VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle(); | |
| VkImage dstImage = ToBackend(dst.texture)->GetHandle(); | |
| // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the | |
| // copy command. | |
| device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage, | |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, | |
| ®ion); | |
| break; | |
| } | |
| case Command::CopyTextureToBuffer: { | |
| CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>(); | |
| auto& src = copy->source; | |
| auto& dst = copy->destination; | |
| VkBufferImageCopy region = | |
| ComputeBufferImageCopyRegion(dst, src, copy->copySize); | |
| VkImageSubresourceLayers subresource = region.imageSubresource; | |
| ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D); | |
| const SubresourceRange range = {subresource.mipLevel, 1, | |
| subresource.baseArrayLayer, | |
| subresource.layerCount}; | |
| ToBackend(src.texture) | |
| ->EnsureSubresourceContentInitialized(recordingContext, range); | |
| ToBackend(src.texture) | |
| ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, range); | |
| ToBackend(dst.buffer) | |
| ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); | |
| VkImage srcImage = ToBackend(src.texture)->GetHandle(); | |
| VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle(); | |
| // The Dawn CopySrc usage is always mapped to GENERAL | |
| device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, | |
| dstBuffer, 1, ®ion); | |
| break; | |
| } | |
| case Command::CopyTextureToTexture: { | |
| CopyTextureToTextureCmd* copy = | |
| mCommands.NextCommand<CopyTextureToTextureCmd>(); | |
| TextureCopy& src = copy->source; | |
| TextureCopy& dst = copy->destination; | |
| SubresourceRange srcRange = GetSubresourcesAffectedByCopy(src, copy->copySize); | |
| SubresourceRange dstRange = GetSubresourcesAffectedByCopy(dst, copy->copySize); | |
| ToBackend(src.texture) | |
| ->EnsureSubresourceContentInitialized(recordingContext, srcRange); | |
| if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, | |
| dst.mipLevel)) { | |
| // Since destination texture has been overwritten, it has been "initialized" | |
| dst.texture->SetIsSubresourceContentInitialized(true, dstRange); | |
| } else { | |
| ToBackend(dst.texture) | |
| ->EnsureSubresourceContentInitialized(recordingContext, dstRange); | |
| } | |
| if (src.texture.Get() == dst.texture.Get() && src.mipLevel == dst.mipLevel) { | |
| // When there are overlapped subresources, the layout of the overlapped | |
| // subresources should all be GENERAL instead of what we set now. Currently | |
| // it is not allowed to copy with overlapped subresources, but we still | |
| // add the ASSERT here as a reminder for this possible misuse. | |
| ASSERT( | |
| !IsRangeOverlapped(src.origin.z, dst.origin.z, copy->copySize.depth)); | |
| } | |
| // TODO after Yunchao's CL | |
| ToBackend(src.texture) | |
| ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, | |
| srcRange); | |
| ToBackend(dst.texture) | |
| ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, | |
| dstRange); | |
| // In some situations we cannot do texture-to-texture copies with vkCmdCopyImage | |
| // because as Vulkan SPEC always validates image copies with the virtual size of | |
| // the image subresource, when the extent that fits in the copy region of one | |
| // subresource but does not fit in the one of another subresource, we will fail | |
| // to find a valid extent to satisfy the requirements on both source and | |
| // destination image subresource. For example, when the source is the first | |
| // level of a 16x16 texture in BC format, and the destination is the third level | |
| // of a 60x60 texture in the same format, neither 16x16 nor 15x15 is valid as | |
| // the extent of vkCmdCopyImage. | |
| // Our workaround for this issue is replacing the texture-to-texture copy with | |
| // one texture-to-buffer copy and one buffer-to-texture copy. | |
| bool copyUsingTemporaryBuffer = | |
| device->IsToggleEnabled( | |
| Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy) && | |
| src.texture->GetFormat().isCompressed && | |
| !HasSameTextureCopyExtent(src, dst, copy->copySize); | |
| if (!copyUsingTemporaryBuffer) { | |
| VkImage srcImage = ToBackend(src.texture)->GetHandle(); | |
| VkImage dstImage = ToBackend(dst.texture)->GetHandle(); | |
| VkImageCopy region = ComputeImageCopyRegion(src, dst, copy->copySize); | |
| // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the | |
| // copy command. | |
| device->fn.CmdCopyImage(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, | |
| dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, | |
| ®ion); | |
| } else { | |
| RecordCopyImageWithTemporaryBuffer(recordingContext, src, dst, | |
| copy->copySize); | |
| } | |
| break; | |
| } | |
| case Command::BeginRenderPass: { | |
| BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>(); | |
| TransitionForPass(device, recordingContext, passResourceUsages[nextPassNumber]); | |
| LazyClearRenderPassAttachments(cmd); | |
| DAWN_TRY(RecordRenderPass(recordingContext, cmd)); | |
| nextPassNumber++; | |
| break; | |
| } | |
| case Command::BeginComputePass: { | |
| mCommands.NextCommand<BeginComputePassCmd>(); | |
| TransitionForPass(device, recordingContext, passResourceUsages[nextPassNumber]); | |
| RecordComputePass(recordingContext); | |
| nextPassNumber++; | |
| break; | |
| } | |
| default: { | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| } | |
| return {}; | |
| } | |
| void CommandBuffer::RecordComputePass(CommandRecordingContext* recordingContext) { | |
| Device* device = ToBackend(GetDevice()); | |
| VkCommandBuffer commands = recordingContext->commandBuffer; | |
| ComputeDescriptorSetTracker descriptorSets = {}; | |
| Command type; | |
| while (mCommands.NextCommandId(&type)) { | |
| switch (type) { | |
| case Command::EndComputePass: { | |
| mCommands.NextCommand<EndComputePassCmd>(); | |
| return; | |
| } | |
| case Command::Dispatch: { | |
| DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>(); | |
| descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE); | |
| device->fn.CmdDispatch(commands, dispatch->x, dispatch->y, dispatch->z); | |
| break; | |
| } | |
| case Command::DispatchIndirect: { | |
| DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>(); | |
| VkBuffer indirectBuffer = ToBackend(dispatch->indirectBuffer)->GetHandle(); | |
| descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE); | |
| device->fn.CmdDispatchIndirect( | |
| commands, indirectBuffer, | |
| static_cast<VkDeviceSize>(dispatch->indirectOffset)); | |
| break; | |
| } | |
| case Command::SetBindGroup: { | |
| SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>(); | |
| BindGroup* bindGroup = ToBackend(cmd->group.Get()); | |
| uint32_t* dynamicOffsets = nullptr; | |
| if (cmd->dynamicOffsetCount > 0) { | |
| dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount); | |
| } | |
| descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount, | |
| dynamicOffsets); | |
| break; | |
| } | |
| case Command::SetComputePipeline: { | |
| SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>(); | |
| ComputePipeline* pipeline = ToBackend(cmd->pipeline).Get(); | |
| device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_COMPUTE, | |
| pipeline->GetHandle()); | |
| descriptorSets.OnSetPipeline(pipeline); | |
| break; | |
| } | |
| case Command::InsertDebugMarker: { | |
| if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { | |
| InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>(); | |
| const char* label = mCommands.NextData<char>(cmd->length + 1); | |
| VkDebugMarkerMarkerInfoEXT markerInfo; | |
| markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; | |
| markerInfo.pNext = nullptr; | |
| markerInfo.pMarkerName = label; | |
| // Default color to black | |
| markerInfo.color[0] = 0.0; | |
| markerInfo.color[1] = 0.0; | |
| markerInfo.color[2] = 0.0; | |
| markerInfo.color[3] = 1.0; | |
| device->fn.CmdDebugMarkerInsertEXT(commands, &markerInfo); | |
| } else { | |
| SkipCommand(&mCommands, Command::InsertDebugMarker); | |
| } | |
| break; | |
| } | |
| case Command::PopDebugGroup: { | |
| if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { | |
| mCommands.NextCommand<PopDebugGroupCmd>(); | |
| device->fn.CmdDebugMarkerEndEXT(commands); | |
| } else { | |
| SkipCommand(&mCommands, Command::PopDebugGroup); | |
| } | |
| break; | |
| } | |
| case Command::PushDebugGroup: { | |
| if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { | |
| PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>(); | |
| const char* label = mCommands.NextData<char>(cmd->length + 1); | |
| VkDebugMarkerMarkerInfoEXT markerInfo; | |
| markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; | |
| markerInfo.pNext = nullptr; | |
| markerInfo.pMarkerName = label; | |
| // Default color to black | |
| markerInfo.color[0] = 0.0; | |
| markerInfo.color[1] = 0.0; | |
| markerInfo.color[2] = 0.0; | |
| markerInfo.color[3] = 1.0; | |
| device->fn.CmdDebugMarkerBeginEXT(commands, &markerInfo); | |
| } else { | |
| SkipCommand(&mCommands, Command::PushDebugGroup); | |
| } | |
| break; | |
| } | |
| default: { | |
| UNREACHABLE(); | |
| break; | |
| } | |
| } | |
| } | |
| // EndComputePass should have been called | |
| UNREACHABLE(); | |
| } | |
| MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* recordingContext, | |
| BeginRenderPassCmd* renderPassCmd) { | |
| Device* device = ToBackend(GetDevice()); | |
| VkCommandBuffer commands = recordingContext->commandBuffer; | |
| DAWN_TRY(RecordBeginRenderPass(recordingContext, device, renderPassCmd)); | |
| // Set the default value for the dynamic state | |
| { | |
| device->fn.CmdSetLineWidth(commands, 1.0f); | |
| device->fn.CmdSetDepthBounds(commands, 0.0f, 1.0f); | |
| device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 0); | |
| float blendConstants[4] = { | |
| 0.0f, | |
| 0.0f, | |
| 0.0f, | |
| 0.0f, | |
| }; | |
| device->fn.CmdSetBlendConstants(commands, blendConstants); | |
| // The viewport and scissor default to cover all of the attachments | |
| VkViewport viewport; | |
| viewport.x = 0.0f; | |
| viewport.y = static_cast<float>(renderPassCmd->height); | |
| viewport.width = static_cast<float>(renderPassCmd->width); | |
| viewport.height = -static_cast<float>(renderPassCmd->height); | |
| viewport.minDepth = 0.0f; | |
| viewport.maxDepth = 1.0f; | |
| device->fn.CmdSetViewport(commands, 0, 1, &viewport); | |
| VkRect2D scissorRect; | |
| scissorRect.offset.x = 0; | |
| scissorRect.offset.y = 0; | |
| scissorRect.extent.width = renderPassCmd->width; | |
| scissorRect.extent.height = renderPassCmd->height; | |
| device->fn.CmdSetScissor(commands, 0, 1, &scissorRect); | |
| } | |
| RenderDescriptorSetTracker descriptorSets = {}; | |
| RenderPipeline* lastPipeline = nullptr; | |
| auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) { | |
| switch (type) { | |
| case Command::Draw: { | |
| DrawCmd* draw = iter->NextCommand<DrawCmd>(); | |
| descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); | |
| device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount, | |
| draw->firstVertex, draw->firstInstance); | |
| break; | |
| } | |
| case Command::DrawIndexed: { | |
| DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>(); | |
| descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); | |
| device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount, | |
| draw->firstIndex, draw->baseVertex, | |
| draw->firstInstance); | |
| break; | |
| } | |
| case Command::DrawIndirect: { | |
| DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>(); | |
| VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle(); | |
| descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); | |
| device->fn.CmdDrawIndirect(commands, indirectBuffer, | |
| static_cast<VkDeviceSize>(draw->indirectOffset), 1, | |
| 0); | |
| break; | |
| } | |
| case Command::DrawIndexedIndirect: { | |
| DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>(); | |
| VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle(); | |
| descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); | |
| device->fn.CmdDrawIndexedIndirect( | |
| commands, indirectBuffer, static_cast<VkDeviceSize>(draw->indirectOffset), | |
| 1, 0); | |
| break; | |
| } | |
| case Command::InsertDebugMarker: { | |
| if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { | |
| InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>(); | |
| const char* label = iter->NextData<char>(cmd->length + 1); | |
| VkDebugMarkerMarkerInfoEXT markerInfo; | |
| markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; | |
| markerInfo.pNext = nullptr; | |
| markerInfo.pMarkerName = label; | |
| // Default color to black | |
| markerInfo.color[0] = 0.0; | |
| markerInfo.color[1] = 0.0; | |
| markerInfo.color[2] = 0.0; | |
| markerInfo.color[3] = 1.0; | |
| device->fn.CmdDebugMarkerInsertEXT(commands, &markerInfo); | |
| } else { | |
| SkipCommand(iter, Command::InsertDebugMarker); | |
| } | |
| break; | |
| } | |
| case Command::PopDebugGroup: { | |
| if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { | |
| iter->NextCommand<PopDebugGroupCmd>(); | |
| device->fn.CmdDebugMarkerEndEXT(commands); | |
| } else { | |
| SkipCommand(iter, Command::PopDebugGroup); | |
| } | |
| break; | |
| } | |
| case Command::PushDebugGroup: { | |
| if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { | |
| PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>(); | |
| const char* label = iter->NextData<char>(cmd->length + 1); | |
| VkDebugMarkerMarkerInfoEXT markerInfo; | |
| markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; | |
| markerInfo.pNext = nullptr; | |
| markerInfo.pMarkerName = label; | |
| // Default color to black | |
| markerInfo.color[0] = 0.0; | |
| markerInfo.color[1] = 0.0; | |
| markerInfo.color[2] = 0.0; | |
| markerInfo.color[3] = 1.0; | |
| device->fn.CmdDebugMarkerBeginEXT(commands, &markerInfo); | |
| } else { | |
| SkipCommand(iter, Command::PushDebugGroup); | |
| } | |
| break; | |
| } | |
| case Command::SetBindGroup: { | |
| SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>(); | |
| BindGroup* bindGroup = ToBackend(cmd->group.Get()); | |
| uint32_t* dynamicOffsets = nullptr; | |
| if (cmd->dynamicOffsetCount > 0) { | |
| dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount); | |
| } | |
| descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount, | |
| dynamicOffsets); | |
| break; | |
| } | |
| case Command::SetIndexBuffer: { | |
| SetIndexBufferCmd* cmd = iter->NextCommand<SetIndexBufferCmd>(); | |
| VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle(); | |
| // TODO(cwallez@chromium.org): get the index type from the last render pipeline | |
| // and rebind if needed on pipeline change | |
| ASSERT(lastPipeline != nullptr); | |
| VkIndexType indexType = | |
| VulkanIndexType(lastPipeline->GetVertexStateDescriptor()->indexFormat); | |
| device->fn.CmdBindIndexBuffer( | |
| commands, indexBuffer, static_cast<VkDeviceSize>(cmd->offset), indexType); | |
| break; | |
| } | |
| case Command::SetRenderPipeline: { | |
| SetRenderPipelineCmd* cmd = iter->NextCommand<SetRenderPipelineCmd>(); | |
| RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); | |
| device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| pipeline->GetHandle()); | |
| lastPipeline = pipeline; | |
| descriptorSets.OnSetPipeline(pipeline); | |
| break; | |
| } | |
| case Command::SetVertexBuffer: { | |
| SetVertexBufferCmd* cmd = iter->NextCommand<SetVertexBufferCmd>(); | |
| VkBuffer buffer = ToBackend(cmd->buffer)->GetHandle(); | |
| VkDeviceSize offset = static_cast<VkDeviceSize>(cmd->offset); | |
| device->fn.CmdBindVertexBuffers(commands, cmd->slot, 1, &*buffer, &offset); | |
| break; | |
| } | |
| default: | |
| UNREACHABLE(); | |
| break; | |
| } | |
| }; | |
| Command type; | |
| while (mCommands.NextCommandId(&type)) { | |
| switch (type) { | |
| case Command::EndRenderPass: { | |
| mCommands.NextCommand<EndRenderPassCmd>(); | |
| device->fn.CmdEndRenderPass(commands); | |
| return {}; | |
| } | |
| case Command::SetBlendColor: { | |
| SetBlendColorCmd* cmd = mCommands.NextCommand<SetBlendColorCmd>(); | |
| float blendConstants[4] = { | |
| cmd->color.r, | |
| cmd->color.g, | |
| cmd->color.b, | |
| cmd->color.a, | |
| }; | |
| device->fn.CmdSetBlendConstants(commands, blendConstants); | |
| break; | |
| } | |
| case Command::SetStencilReference: { | |
| SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>(); | |
| device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, | |
| cmd->reference); | |
| break; | |
| } | |
| case Command::SetViewport: { | |
| SetViewportCmd* cmd = mCommands.NextCommand<SetViewportCmd>(); | |
| VkViewport viewport; | |
| viewport.x = cmd->x; | |
| viewport.y = cmd->y + cmd->height; | |
| viewport.width = cmd->width; | |
| viewport.height = -cmd->height; | |
| viewport.minDepth = cmd->minDepth; | |
| viewport.maxDepth = cmd->maxDepth; | |
| device->fn.CmdSetViewport(commands, 0, 1, &viewport); | |
| break; | |
| } | |
| case Command::SetScissorRect: { | |
| SetScissorRectCmd* cmd = mCommands.NextCommand<SetScissorRectCmd>(); | |
| VkRect2D rect; | |
| rect.offset.x = cmd->x; | |
| rect.offset.y = cmd->y; | |
| rect.extent.width = cmd->width; | |
| rect.extent.height = cmd->height; | |
| device->fn.CmdSetScissor(commands, 0, 1, &rect); | |
| break; | |
| } | |
| case Command::ExecuteBundles: { | |
| ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>(); | |
| auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count); | |
| for (uint32_t i = 0; i < cmd->count; ++i) { | |
| CommandIterator* iter = bundles[i]->GetCommands(); | |
| iter->Reset(); | |
| while (iter->NextCommandId(&type)) { | |
| EncodeRenderBundleCommand(iter, type); | |
| } | |
| } | |
| break; | |
| } | |
| default: { | |
| EncodeRenderBundleCommand(&mCommands, type); | |
| break; | |
| } | |
| } | |
| } | |
| // EndRenderPass should have been called | |
| UNREACHABLE(); | |
| } | |
| }} // namespace dawn_native::vulkan |