// // Copyright 2019 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // SecondaryCommandBuffer: // CPU-side storage of commands to delay GPU-side allocation until commands are submitted. // #include "libANGLE/renderer/vulkan/SecondaryCommandBuffer.h" #include "common/debug.h" #include "libANGLE/renderer/vulkan/vk_utils.h" #include "libANGLE/trace.h" namespace rx { namespace vk { namespace priv { namespace { const char *GetCommandString(CommandID id) { switch (id) { case CommandID::Invalid: return "--Invalid--"; case CommandID::BeginDebugUtilsLabel: return "BeginDebugUtilsLabel"; case CommandID::BeginQuery: return "BeginQuery"; case CommandID::BeginTransformFeedback: return "BeginTransformFeedback"; case CommandID::BindComputePipeline: return "BindComputePipeline"; case CommandID::BindDescriptorSets: return "BindDescriptorSets"; case CommandID::BindGraphicsPipeline: return "BindGraphicsPipeline"; case CommandID::BindIndexBuffer: return "BindIndexBuffer"; case CommandID::BindTransformFeedbackBuffers: return "BindTransformFeedbackBuffers"; case CommandID::BindVertexBuffers: return "BindVertexBuffers"; case CommandID::BlitImage: return "BlitImage"; case CommandID::BufferBarrier: return "BufferBarrier"; case CommandID::ClearAttachments: return "ClearAttachments"; case CommandID::ClearColorImage: return "ClearColorImage"; case CommandID::ClearDepthStencilImage: return "ClearDepthStencilImage"; case CommandID::CopyBuffer: return "CopyBuffer"; case CommandID::CopyBufferToImage: return "CopyBufferToImage"; case CommandID::CopyImage: return "CopyImage"; case CommandID::CopyImageToBuffer: return "CopyImageToBuffer"; case CommandID::Dispatch: return "Dispatch"; case CommandID::DispatchIndirect: return "DispatchIndirect"; case CommandID::Draw: return "Draw"; case CommandID::DrawIndexed: return "DrawIndexed"; case CommandID::DrawIndexedBaseVertex: return "DrawIndexedBaseVertex"; case CommandID::DrawIndexedIndirect: return "DrawIndexedIndirect"; case CommandID::DrawIndexedInstanced: return "DrawIndexedInstanced"; case CommandID::DrawIndexedInstancedBaseVertex: return "DrawIndexedInstancedBaseVertex"; case CommandID::DrawIndexedInstancedBaseVertexBaseInstance: return "DrawIndexedInstancedBaseVertexBaseInstance"; case CommandID::DrawIndirect: return "DrawIndirect"; case CommandID::DrawInstanced: return "DrawInstanced"; case CommandID::DrawInstancedBaseInstance: return "DrawInstancedBaseInstance"; case CommandID::EndDebugUtilsLabel: return "EndDebugUtilsLabel"; case CommandID::EndQuery: return "EndQuery"; case CommandID::EndTransformFeedback: return "EndTransformFeedback"; case CommandID::ExecutionBarrier: return "ExecutionBarrier"; case CommandID::FillBuffer: return "FillBuffer"; case CommandID::ImageBarrier: return "ImageBarrier"; case CommandID::InsertDebugUtilsLabel: return "InsertDebugUtilsLabel"; case CommandID::MemoryBarrier: return "MemoryBarrier"; case CommandID::NextSubpass: return "NextSubpass"; case CommandID::PipelineBarrier: return "PipelineBarrier"; case CommandID::PushConstants: return "PushConstants"; case CommandID::ResetEvent: return "ResetEvent"; case CommandID::ResetQueryPool: return "ResetQueryPool"; case CommandID::ResolveImage: return "ResolveImage"; case CommandID::SetEvent: return "SetEvent"; case CommandID::SetScissor: return "SetScissor"; case CommandID::SetViewport: return "SetViewport"; case CommandID::WaitEvents: return "WaitEvents"; case CommandID::WriteTimestamp: return "WriteTimestamp"; default: // Need this to work around MSVC warning 4715. UNREACHABLE(); return "--unreachable--"; } } } // namespace ANGLE_INLINE const CommandHeader *NextCommand(const CommandHeader *command) { return reinterpret_cast(reinterpret_cast(command) + command->size); } // Parse the cmds in this cmd buffer into given primary cmd buffer void SecondaryCommandBuffer::executeCommands(VkCommandBuffer cmdBuffer) { ANGLE_TRACE_EVENT0("gpu.angle", "SecondaryCommandBuffer::executeCommands"); for (const CommandHeader *command : mCommands) { for (const CommandHeader *currentCommand = command; currentCommand->id != CommandID::Invalid; currentCommand = NextCommand(currentCommand)) { switch (currentCommand->id) { case CommandID::BeginDebugUtilsLabel: { const DebugUtilsLabelParams *params = getParamPtr(currentCommand); const char *pLabelName = Offset(params, sizeof(DebugUtilsLabelParams)); const VkDebugUtilsLabelEXT label = { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, nullptr, pLabelName, {params->color[0], params->color[1], params->color[2], params->color[3]}}; ASSERT(vkCmdBeginDebugUtilsLabelEXT); vkCmdBeginDebugUtilsLabelEXT(cmdBuffer, &label); break; } case CommandID::BeginQuery: { const BeginQueryParams *params = getParamPtr(currentCommand); vkCmdBeginQuery(cmdBuffer, params->queryPool, params->query, params->flags); break; } case CommandID::BeginTransformFeedback: { const BeginTransformFeedbackParams *params = getParamPtr(currentCommand); const VkBuffer *counterBuffers = Offset(params, sizeof(BeginTransformFeedbackParams)); // Workaround for AMD driver bug where it expects the offsets array to be // non-null gl::TransformFeedbackBuffersArray offsets; offsets.fill(0); vkCmdBeginTransformFeedbackEXT(cmdBuffer, 0, params->bufferCount, counterBuffers, offsets.data()); break; } case CommandID::BindComputePipeline: { const BindPipelineParams *params = getParamPtr(currentCommand); vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, params->pipeline); break; } case CommandID::BindDescriptorSets: { const BindDescriptorSetParams *params = getParamPtr(currentCommand); const VkDescriptorSet *descriptorSets = Offset(params, sizeof(BindDescriptorSetParams)); const uint32_t *dynamicOffsets = Offset( descriptorSets, sizeof(VkDescriptorSet) * params->descriptorSetCount); vkCmdBindDescriptorSets(cmdBuffer, params->pipelineBindPoint, params->layout, params->firstSet, params->descriptorSetCount, descriptorSets, params->dynamicOffsetCount, dynamicOffsets); break; } case CommandID::BindGraphicsPipeline: { const BindPipelineParams *params = getParamPtr(currentCommand); vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, params->pipeline); break; } case CommandID::BindIndexBuffer: { const BindIndexBufferParams *params = getParamPtr(currentCommand); vkCmdBindIndexBuffer(cmdBuffer, params->buffer, params->offset, params->indexType); break; } case CommandID::BindTransformFeedbackBuffers: { const BindTransformFeedbackBuffersParams *params = getParamPtr(currentCommand); const VkBuffer *buffers = Offset(params, sizeof(BindTransformFeedbackBuffersParams)); const VkDeviceSize *offsets = Offset(buffers, sizeof(VkBuffer) * params->bindingCount); const VkDeviceSize *sizes = Offset(offsets, sizeof(VkDeviceSize) * params->bindingCount); vkCmdBindTransformFeedbackBuffersEXT(cmdBuffer, 0, params->bindingCount, buffers, offsets, sizes); break; } case CommandID::BindVertexBuffers: { const BindVertexBuffersParams *params = getParamPtr(currentCommand); const VkBuffer *buffers = Offset(params, sizeof(BindVertexBuffersParams)); const VkDeviceSize *offsets = Offset(buffers, sizeof(VkBuffer) * params->bindingCount); vkCmdBindVertexBuffers(cmdBuffer, 0, params->bindingCount, buffers, offsets); break; } case CommandID::BlitImage: { const BlitImageParams *params = getParamPtr(currentCommand); vkCmdBlitImage(cmdBuffer, params->srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, params->dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ¶ms->region, params->filter); break; } case CommandID::BufferBarrier: { const BufferBarrierParams *params = getParamPtr(currentCommand); vkCmdPipelineBarrier(cmdBuffer, params->srcStageMask, params->dstStageMask, 0, 0, nullptr, 1, ¶ms->bufferMemoryBarrier, 0, nullptr); break; } case CommandID::ClearAttachments: { const ClearAttachmentsParams *params = getParamPtr(currentCommand); const VkClearAttachment *attachments = Offset(params, sizeof(ClearAttachmentsParams)); vkCmdClearAttachments(cmdBuffer, params->attachmentCount, attachments, 1, ¶ms->rect); break; } case CommandID::ClearColorImage: { const ClearColorImageParams *params = getParamPtr(currentCommand); vkCmdClearColorImage(cmdBuffer, params->image, params->imageLayout, ¶ms->color, 1, ¶ms->range); break; } case CommandID::ClearDepthStencilImage: { const ClearDepthStencilImageParams *params = getParamPtr(currentCommand); vkCmdClearDepthStencilImage(cmdBuffer, params->image, params->imageLayout, ¶ms->depthStencil, 1, ¶ms->range); break; } case CommandID::CopyBuffer: { const CopyBufferParams *params = getParamPtr(currentCommand); const VkBufferCopy *regions = Offset(params, sizeof(CopyBufferParams)); vkCmdCopyBuffer(cmdBuffer, params->srcBuffer, params->destBuffer, params->regionCount, regions); break; } case CommandID::CopyBufferToImage: { const CopyBufferToImageParams *params = getParamPtr(currentCommand); vkCmdCopyBufferToImage(cmdBuffer, params->srcBuffer, params->dstImage, params->dstImageLayout, 1, ¶ms->region); break; } case CommandID::CopyImage: { const CopyImageParams *params = getParamPtr(currentCommand); vkCmdCopyImage(cmdBuffer, params->srcImage, params->srcImageLayout, params->dstImage, params->dstImageLayout, 1, ¶ms->region); break; } case CommandID::CopyImageToBuffer: { const CopyImageToBufferParams *params = getParamPtr(currentCommand); vkCmdCopyImageToBuffer(cmdBuffer, params->srcImage, params->srcImageLayout, params->dstBuffer, 1, ¶ms->region); break; } case CommandID::Dispatch: { const DispatchParams *params = getParamPtr(currentCommand); vkCmdDispatch(cmdBuffer, params->groupCountX, params->groupCountY, params->groupCountZ); break; } case CommandID::DispatchIndirect: { const DispatchIndirectParams *params = getParamPtr(currentCommand); vkCmdDispatchIndirect(cmdBuffer, params->buffer, params->offset); break; } case CommandID::Draw: { const DrawParams *params = getParamPtr(currentCommand); vkCmdDraw(cmdBuffer, params->vertexCount, 1, params->firstVertex, 0); break; } case CommandID::DrawIndexed: { const DrawIndexedParams *params = getParamPtr(currentCommand); vkCmdDrawIndexed(cmdBuffer, params->indexCount, 1, 0, 0, 0); break; } case CommandID::DrawIndexedBaseVertex: { const DrawIndexedBaseVertexParams *params = getParamPtr(currentCommand); vkCmdDrawIndexed(cmdBuffer, params->indexCount, 1, 0, params->vertexOffset, 0); break; } case CommandID::DrawIndexedIndirect: { const DrawIndexedIndirectParams *params = getParamPtr(currentCommand); vkCmdDrawIndexedIndirect(cmdBuffer, params->buffer, params->offset, 1, 0); break; } case CommandID::DrawIndexedInstanced: { const DrawIndexedInstancedParams *params = getParamPtr(currentCommand); vkCmdDrawIndexed(cmdBuffer, params->indexCount, params->instanceCount, 0, 0, 0); break; } case CommandID::DrawIndexedInstancedBaseVertex: { const DrawIndexedInstancedBaseVertexParams *params = getParamPtr(currentCommand); vkCmdDrawIndexed(cmdBuffer, params->indexCount, params->instanceCount, 0, params->vertexOffset, 0); break; } case CommandID::DrawIndexedInstancedBaseVertexBaseInstance: { const DrawIndexedInstancedBaseVertexBaseInstanceParams *params = getParamPtr( currentCommand); vkCmdDrawIndexed(cmdBuffer, params->indexCount, params->instanceCount, params->firstIndex, params->vertexOffset, params->firstInstance); break; } case CommandID::DrawIndirect: { const DrawIndirectParams *params = getParamPtr(currentCommand); vkCmdDrawIndirect(cmdBuffer, params->buffer, params->offset, 1, 0); break; } case CommandID::DrawInstanced: { const DrawInstancedParams *params = getParamPtr(currentCommand); vkCmdDraw(cmdBuffer, params->vertexCount, params->instanceCount, params->firstVertex, 0); break; } case CommandID::DrawInstancedBaseInstance: { const DrawInstancedBaseInstanceParams *params = getParamPtr(currentCommand); vkCmdDraw(cmdBuffer, params->vertexCount, params->instanceCount, params->firstVertex, params->firstInstance); break; } case CommandID::EndDebugUtilsLabel: { ASSERT(vkCmdEndDebugUtilsLabelEXT); vkCmdEndDebugUtilsLabelEXT(cmdBuffer); break; } case CommandID::EndQuery: { const EndQueryParams *params = getParamPtr(currentCommand); vkCmdEndQuery(cmdBuffer, params->queryPool, params->query); break; } case CommandID::EndTransformFeedback: { const EndTransformFeedbackParams *params = getParamPtr(currentCommand); const VkBuffer *counterBuffers = Offset(params, sizeof(EndTransformFeedbackParams)); // Workaround for AMD driver bug where it expects the offsets array to be // non-null gl::TransformFeedbackBuffersArray offsets; offsets.fill(0); vkCmdEndTransformFeedbackEXT(cmdBuffer, 0, params->bufferCount, counterBuffers, offsets.data()); break; } case CommandID::ExecutionBarrier: { const ExecutionBarrierParams *params = getParamPtr(currentCommand); vkCmdPipelineBarrier(cmdBuffer, params->stageMask, params->stageMask, 0, 0, nullptr, 0, nullptr, 0, nullptr); break; } case CommandID::FillBuffer: { const FillBufferParams *params = getParamPtr(currentCommand); vkCmdFillBuffer(cmdBuffer, params->dstBuffer, params->dstOffset, params->size, params->data); break; } case CommandID::ImageBarrier: { const ImageBarrierParams *params = getParamPtr(currentCommand); vkCmdPipelineBarrier(cmdBuffer, params->srcStageMask, params->dstStageMask, 0, 0, nullptr, 0, nullptr, 1, ¶ms->imageMemoryBarrier); break; } case CommandID::InsertDebugUtilsLabel: { const DebugUtilsLabelParams *params = getParamPtr(currentCommand); const char *pLabelName = Offset(params, sizeof(DebugUtilsLabelParams)); const VkDebugUtilsLabelEXT label = { VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, nullptr, pLabelName, {params->color[0], params->color[1], params->color[2], params->color[3]}}; ASSERT(vkCmdInsertDebugUtilsLabelEXT); vkCmdInsertDebugUtilsLabelEXT(cmdBuffer, &label); break; } case CommandID::MemoryBarrier: { const MemoryBarrierParams *params = getParamPtr(currentCommand); vkCmdPipelineBarrier(cmdBuffer, params->srcStageMask, params->dstStageMask, 0, 1, ¶ms->memoryBarrier, 0, nullptr, 0, nullptr); break; } case CommandID::NextSubpass: { const NextSubpassParams *params = getParamPtr(currentCommand); vkCmdNextSubpass(cmdBuffer, params->subpassContents); break; } case CommandID::PipelineBarrier: { const PipelineBarrierParams *params = getParamPtr(currentCommand); const VkMemoryBarrier *memoryBarriers = Offset(params, sizeof(PipelineBarrierParams)); const VkBufferMemoryBarrier *bufferMemoryBarriers = Offset( memoryBarriers, params->memoryBarrierCount * sizeof(VkMemoryBarrier)); const VkImageMemoryBarrier *imageMemoryBarriers = Offset( bufferMemoryBarriers, params->bufferMemoryBarrierCount * sizeof(VkBufferMemoryBarrier)); vkCmdPipelineBarrier(cmdBuffer, params->srcStageMask, params->dstStageMask, params->dependencyFlags, params->memoryBarrierCount, memoryBarriers, params->bufferMemoryBarrierCount, bufferMemoryBarriers, params->imageMemoryBarrierCount, imageMemoryBarriers); break; } case CommandID::PushConstants: { const PushConstantsParams *params = getParamPtr(currentCommand); const void *data = Offset(params, sizeof(PushConstantsParams)); vkCmdPushConstants(cmdBuffer, params->layout, params->flag, params->offset, params->size, data); break; } case CommandID::ResetEvent: { const ResetEventParams *params = getParamPtr(currentCommand); vkCmdResetEvent(cmdBuffer, params->event, params->stageMask); break; } case CommandID::ResetQueryPool: { const ResetQueryPoolParams *params = getParamPtr(currentCommand); vkCmdResetQueryPool(cmdBuffer, params->queryPool, params->firstQuery, params->queryCount); break; } case CommandID::ResolveImage: { const ResolveImageParams *params = getParamPtr(currentCommand); vkCmdResolveImage(cmdBuffer, params->srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, params->dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ¶ms->region); break; } case CommandID::SetEvent: { const SetEventParams *params = getParamPtr(currentCommand); vkCmdSetEvent(cmdBuffer, params->event, params->stageMask); break; } case CommandID::SetScissor: { const SetScissorParams *params = getParamPtr(currentCommand); vkCmdSetScissor(cmdBuffer, 0, 1, ¶ms->scissor); break; } case CommandID::SetViewport: { const SetViewportParams *params = getParamPtr(currentCommand); vkCmdSetViewport(cmdBuffer, 0, 1, ¶ms->viewport); break; } case CommandID::WaitEvents: { const WaitEventsParams *params = getParamPtr(currentCommand); const VkEvent *events = Offset(params, sizeof(WaitEventsParams)); const VkMemoryBarrier *memoryBarriers = Offset(events, params->eventCount * sizeof(VkEvent)); const VkBufferMemoryBarrier *bufferMemoryBarriers = Offset( memoryBarriers, params->memoryBarrierCount * sizeof(VkMemoryBarrier)); const VkImageMemoryBarrier *imageMemoryBarriers = Offset( bufferMemoryBarriers, params->bufferMemoryBarrierCount * sizeof(VkBufferMemoryBarrier)); vkCmdWaitEvents(cmdBuffer, params->eventCount, events, params->srcStageMask, params->dstStageMask, params->memoryBarrierCount, memoryBarriers, params->bufferMemoryBarrierCount, bufferMemoryBarriers, params->imageMemoryBarrierCount, imageMemoryBarriers); break; } case CommandID::WriteTimestamp: { const WriteTimestampParams *params = getParamPtr(currentCommand); vkCmdWriteTimestamp(cmdBuffer, params->pipelineStage, params->queryPool, params->query); break; } default: { UNREACHABLE(); break; } } } } } void SecondaryCommandBuffer::getMemoryUsageStats(size_t *usedMemoryOut, size_t *allocatedMemoryOut) const { *allocatedMemoryOut = kBlockSize * mCommands.size(); *usedMemoryOut = 0; for (const CommandHeader *command : mCommands) { const CommandHeader *commandEnd = command; while (commandEnd->id != CommandID::Invalid) { commandEnd = NextCommand(commandEnd); } *usedMemoryOut += reinterpret_cast(commandEnd) - reinterpret_cast(command) + sizeof(CommandHeader::id); } ASSERT(*usedMemoryOut <= *allocatedMemoryOut); } std::string SecondaryCommandBuffer::dumpCommands(const char *separator) const { std::stringstream result; for (const CommandHeader *command : mCommands) { for (const CommandHeader *currentCommand = command; currentCommand->id != CommandID::Invalid; currentCommand = NextCommand(currentCommand)) { result << GetCommandString(currentCommand->id) << separator; } } return result.str(); } } // namespace priv } // namespace vk } // namespace rx