// Copyright 2018 The SwiftShader Authors. All Rights Reserved. // // 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 "VkCommandBuffer.hpp" #include "VkBuffer.hpp" #include "VkConfig.hpp" #include "VkDevice.hpp" #include "VkEvent.hpp" #include "VkFence.hpp" #include "VkFramebuffer.hpp" #include "VkImage.hpp" #include "VkImageView.hpp" #include "VkPipeline.hpp" #include "VkPipelineLayout.hpp" #include "VkQueryPool.hpp" #include "VkRenderPass.hpp" #include "Device/Renderer.hpp" #include "./Debug/Context.hpp" #include "./Debug/File.hpp" #include "./Debug/Thread.hpp" #include "marl/defer.h" #include #include namespace { class CmdBeginRenderPass : public vk::CommandBuffer::Command { public: CmdBeginRenderPass(vk::RenderPass *renderPass, vk::Framebuffer *framebuffer, VkRect2D renderArea, uint32_t clearValueCount, const VkClearValue *pClearValues, const VkRenderPassAttachmentBeginInfo *attachmentInfo) : renderPass(renderPass) , framebuffer(framebuffer) , renderArea(renderArea) , clearValueCount(clearValueCount) , attachmentCount(attachmentInfo ? attachmentInfo->attachmentCount : 0) , attachments(nullptr) { // FIXME(b/119409619): use an allocator here so we can control all memory allocations clearValues = new VkClearValue[clearValueCount]; memcpy(clearValues, pClearValues, clearValueCount * sizeof(VkClearValue)); if(attachmentCount > 0) { attachments = new vk::ImageView *[attachmentCount]; for(uint32_t i = 0; i < attachmentCount; i++) { attachments[i] = vk::Cast(attachmentInfo->pAttachments[i]); } } } ~CmdBeginRenderPass() override { delete[] clearValues; delete[] attachments; } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.renderPass = renderPass; executionState.renderPassFramebuffer = framebuffer; executionState.subpassIndex = 0; for(uint32_t i = 0; i < attachmentCount; i++) { framebuffer->setAttachment(attachments[i], i); } // Vulkan specifies that the attachments' `loadOp` gets executed "at the beginning of the subpass where it is first used." // Since we don't discard any contents between subpasses, this is equivalent to executing it at the start of the renderpass. framebuffer->executeLoadOp(executionState.renderPass, clearValueCount, clearValues, renderArea); } std::string description() override { return "vkCmdBeginRenderPass()"; } private: vk::RenderPass *const renderPass; vk::Framebuffer *const framebuffer; const VkRect2D renderArea; const uint32_t clearValueCount; VkClearValue *clearValues; uint32_t attachmentCount; vk::ImageView **attachments; }; class CmdNextSubpass : public vk::CommandBuffer::Command { public: void execute(vk::CommandBuffer::ExecutionState &executionState) override { bool hasResolveAttachments = (executionState.renderPass->getSubpass(executionState.subpassIndex).pResolveAttachments != nullptr); if(hasResolveAttachments) { // TODO(b/197691918): Avoid halt-the-world synchronization. executionState.renderer->synchronize(); // TODO(b/197691917): Eliminate redundant resolve operations. executionState.renderPassFramebuffer->resolve(executionState.renderPass, executionState.subpassIndex); } executionState.subpassIndex++; } std::string description() override { return "vkCmdNextSubpass()"; } }; class CmdEndRenderPass : public vk::CommandBuffer::Command { public: void execute(vk::CommandBuffer::ExecutionState &executionState) override { // Execute (implicit or explicit) VkSubpassDependency to VK_SUBPASS_EXTERNAL. // TODO(b/197691918): Avoid halt-the-world synchronization. executionState.renderer->synchronize(); // TODO(b/197691917): Eliminate redundant resolve operations. executionState.renderPassFramebuffer->resolve(executionState.renderPass, executionState.subpassIndex); executionState.renderPass = nullptr; executionState.renderPassFramebuffer = nullptr; } std::string description() override { return "vkCmdEndRenderPass()"; } }; class CmdBeginRendering : public vk::CommandBuffer::Command { public: CmdBeginRendering(const VkRenderingInfo *pRenderingInfo) : dynamicRendering(pRenderingInfo) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicRendering = &dynamicRendering; if(!executionState.dynamicRendering->resume()) { VkClearRect rect = {}; rect.rect = executionState.dynamicRendering->getRenderArea(); rect.layerCount = executionState.dynamicRendering->getLayerCount(); uint32_t viewMask = executionState.dynamicRendering->getViewMask(); // Vulkan specifies that the attachments' `loadOp` gets executed "at the beginning of the subpass where it is first used." // Since we don't discard any contents between subpasses, this is equivalent to executing it at the start of the renderpass. for(uint32_t i = 0; i < dynamicRendering.getColorAttachmentCount(); i++) { const VkRenderingAttachmentInfo *colorAttachment = dynamicRendering.getColorAttachment(i); if(colorAttachment->loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) { vk::ImageView *imageView = vk::Cast(colorAttachment->imageView); if(imageView) { imageView->clear(colorAttachment->clearValue, VK_IMAGE_ASPECT_COLOR_BIT, rect, viewMask); } } } const VkRenderingAttachmentInfo &stencilAttachment = dynamicRendering.getStencilAttachment(); if(stencilAttachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) { vk::ImageView *imageView = vk::Cast(stencilAttachment.imageView); if(imageView) { imageView->clear(stencilAttachment.clearValue, VK_IMAGE_ASPECT_STENCIL_BIT, rect, viewMask); } } const VkRenderingAttachmentInfo &depthAttachment = dynamicRendering.getDepthAttachment(); if(depthAttachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) { vk::ImageView *imageView = vk::Cast(depthAttachment.imageView); if(imageView) { imageView->clear(depthAttachment.clearValue, VK_IMAGE_ASPECT_DEPTH_BIT, rect, viewMask); } } } } std::string description() override { return "vkCmdBeginRendering()"; } private: vk::DynamicRendering dynamicRendering; }; class CmdEndRendering : public vk::CommandBuffer::Command { public: void execute(vk::CommandBuffer::ExecutionState &executionState) override { // TODO(b/197691918): Avoid halt-the-world synchronization. executionState.renderer->synchronize(); if(!executionState.dynamicRendering->suspend()) { uint32_t viewMask = executionState.dynamicRendering->getViewMask(); // TODO(b/197691917): Eliminate redundant resolve operations. uint32_t colorAttachmentCount = executionState.dynamicRendering->getColorAttachmentCount(); for(uint32_t i = 0; i < colorAttachmentCount; i++) { const VkRenderingAttachmentInfo *colorAttachment = executionState.dynamicRendering->getColorAttachment(i); if(colorAttachment && colorAttachment->resolveMode != VK_RESOLVE_MODE_NONE) { vk::ImageView *imageView = vk::Cast(colorAttachment->imageView); vk::ImageView *resolveImageView = vk::Cast(colorAttachment->resolveImageView); imageView->resolve(resolveImageView, viewMask); } } const VkRenderingAttachmentInfo &depthAttachment = executionState.dynamicRendering->getDepthAttachment(); if(depthAttachment.resolveMode != VK_RESOLVE_MODE_NONE) { vk::ImageView *imageView = vk::Cast(depthAttachment.imageView); vk::ImageView *resolveImageView = vk::Cast(depthAttachment.resolveImageView); imageView->resolveDepthStencil(resolveImageView, depthAttachment.resolveMode, VK_RESOLVE_MODE_NONE); } const VkRenderingAttachmentInfo &stencilAttachment = executionState.dynamicRendering->getStencilAttachment(); if(stencilAttachment.resolveMode != VK_RESOLVE_MODE_NONE) { vk::ImageView *imageView = vk::Cast(stencilAttachment.imageView); vk::ImageView *resolveImageView = vk::Cast(stencilAttachment.resolveImageView); imageView->resolveDepthStencil(resolveImageView, VK_RESOLVE_MODE_NONE, stencilAttachment.resolveMode); } } executionState.dynamicRendering = nullptr; } std::string description() override { return "vkCmdEndRendering()"; } }; class CmdExecuteCommands : public vk::CommandBuffer::Command { public: CmdExecuteCommands(const vk::CommandBuffer *commandBuffer) : commandBuffer(commandBuffer) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { commandBuffer->submitSecondary(executionState); } std::string description() override { return "vkCmdExecuteCommands()"; } private: const vk::CommandBuffer *const commandBuffer; }; class CmdPipelineBind : public vk::CommandBuffer::Command { public: CmdPipelineBind(VkPipelineBindPoint pipelineBindPoint, vk::Pipeline *pipeline) : pipelineBindPoint(pipelineBindPoint) , pipeline(pipeline) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.pipelineState[pipelineBindPoint].pipeline = pipeline; } std::string description() override { return "vkCmdPipelineBind()"; } private: const VkPipelineBindPoint pipelineBindPoint; vk::Pipeline *const pipeline; }; class CmdDispatch : public vk::CommandBuffer::Command { public: CmdDispatch(uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) : baseGroupX(baseGroupX) , baseGroupY(baseGroupY) , baseGroupZ(baseGroupZ) , groupCountX(groupCountX) , groupCountY(groupCountY) , groupCountZ(groupCountZ) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { const auto &pipelineState = executionState.pipelineState[VK_PIPELINE_BIND_POINT_COMPUTE]; vk::ComputePipeline *pipeline = static_cast(pipelineState.pipeline); pipeline->run(baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ, pipelineState.descriptorSetObjects, pipelineState.descriptorSets, pipelineState.descriptorDynamicOffsets, executionState.pushConstants); } std::string description() override { return "vkCmdDispatch()"; } private: const uint32_t baseGroupX; const uint32_t baseGroupY; const uint32_t baseGroupZ; const uint32_t groupCountX; const uint32_t groupCountY; const uint32_t groupCountZ; }; class CmdDispatchIndirect : public vk::CommandBuffer::Command { public: CmdDispatchIndirect(vk::Buffer *buffer, VkDeviceSize offset) : buffer(buffer) , offset(offset) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { const auto *cmd = reinterpret_cast(buffer->getOffsetPointer(offset)); const auto &pipelineState = executionState.pipelineState[VK_PIPELINE_BIND_POINT_COMPUTE]; auto *pipeline = static_cast(pipelineState.pipeline); pipeline->run(0, 0, 0, cmd->x, cmd->y, cmd->z, pipelineState.descriptorSetObjects, pipelineState.descriptorSets, pipelineState.descriptorDynamicOffsets, executionState.pushConstants); } std::string description() override { return "vkCmdDispatchIndirect()"; } private: const vk::Buffer *const buffer; const VkDeviceSize offset; }; class CmdVertexBufferBind : public vk::CommandBuffer::Command { public: CmdVertexBufferBind(uint32_t binding, vk::Buffer *buffer, const VkDeviceSize offset, const VkDeviceSize size, const VkDeviceSize stride, bool hasStride) : binding(binding) , buffer(buffer) , offset(offset) , size(size) , stride(stride) , hasStride(hasStride) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.vertexInputBindings[binding] = { buffer, offset, size }; if(hasStride) { executionState.dynamicState.vertexInputBindings[binding].stride = stride; } } std::string description() override { return "vkCmdVertexBufferBind()"; } private: const uint32_t binding; vk::Buffer *const buffer; const VkDeviceSize offset; const VkDeviceSize size; const VkDeviceSize stride; const bool hasStride; }; class CmdIndexBufferBind : public vk::CommandBuffer::Command { public: CmdIndexBufferBind(vk::Buffer *buffer, const VkDeviceSize offset, const VkIndexType indexType) : buffer(buffer) , offset(offset) , indexType(indexType) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.indexBufferBinding = { buffer, offset, 0 }; executionState.indexType = indexType; } std::string description() override { return "vkCmdIndexBufferBind()"; } private: vk::Buffer *const buffer; const VkDeviceSize offset; const VkIndexType indexType; }; class CmdSetViewport : public vk::CommandBuffer::Command { public: CmdSetViewport(const VkViewport &viewport, uint32_t viewportID) : viewport(viewport) , viewportID(viewportID) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.viewport = viewport; } std::string description() override { return "vkCmdSetViewport()"; } private: const VkViewport viewport; const uint32_t viewportID; }; class CmdSetScissor : public vk::CommandBuffer::Command { public: CmdSetScissor(const VkRect2D &scissor, uint32_t scissorID) : scissor(scissor) , scissorID(scissorID) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.scissor = scissor; } std::string description() override { return "vkCmdSetScissor()"; } private: const VkRect2D scissor; const uint32_t scissorID; }; class CmdSetLineWidth : public vk::CommandBuffer::Command { public: CmdSetLineWidth(float lineWidth) : lineWidth(lineWidth) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.lineWidth = lineWidth; } std::string description() override { return "vkCmdSetLineWidth()"; } private: const float lineWidth; }; class CmdSetDepthBias : public vk::CommandBuffer::Command { public: CmdSetDepthBias(float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor) : depthBiasConstantFactor(depthBiasConstantFactor) , depthBiasClamp(depthBiasClamp) , depthBiasSlopeFactor(depthBiasSlopeFactor) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.depthBiasConstantFactor = depthBiasConstantFactor; executionState.dynamicState.depthBiasClamp = depthBiasClamp; executionState.dynamicState.depthBiasSlopeFactor = depthBiasSlopeFactor; } std::string description() override { return "vkCmdSetDepthBias()"; } private: const float depthBiasConstantFactor; const float depthBiasClamp; const float depthBiasSlopeFactor; }; class CmdSetBlendConstants : public vk::CommandBuffer::Command { public: CmdSetBlendConstants(const float blendConstants[4]) { memcpy(this->blendConstants, blendConstants, sizeof(this->blendConstants)); } void execute(vk::CommandBuffer::ExecutionState &executionState) override { memcpy(&(executionState.dynamicState.blendConstants[0]), blendConstants, sizeof(blendConstants)); } std::string description() override { return "vkCmdSetBlendConstants()"; } private: float blendConstants[4]; }; class CmdSetDepthBounds : public vk::CommandBuffer::Command { public: CmdSetDepthBounds(float minDepthBounds, float maxDepthBounds) : minDepthBounds(minDepthBounds) , maxDepthBounds(maxDepthBounds) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.minDepthBounds = minDepthBounds; executionState.dynamicState.maxDepthBounds = maxDepthBounds; } std::string description() override { return "vkCmdSetDepthBounds()"; } private: const float minDepthBounds; const float maxDepthBounds; }; class CmdSetStencilCompareMask : public vk::CommandBuffer::Command { public: CmdSetStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t compareMask) : faceMask(faceMask) , compareMask(compareMask) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { if(faceMask & VK_STENCIL_FACE_FRONT_BIT) { executionState.dynamicState.frontStencil.compareMask = compareMask; } if(faceMask & VK_STENCIL_FACE_BACK_BIT) { executionState.dynamicState.backStencil.compareMask = compareMask; } } std::string description() override { return "vkCmdSetStencilCompareMask()"; } private: const VkStencilFaceFlags faceMask; const uint32_t compareMask; }; class CmdSetStencilWriteMask : public vk::CommandBuffer::Command { public: CmdSetStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t writeMask) : faceMask(faceMask) , writeMask(writeMask) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { if(faceMask & VK_STENCIL_FACE_FRONT_BIT) { executionState.dynamicState.frontStencil.writeMask = writeMask; } if(faceMask & VK_STENCIL_FACE_BACK_BIT) { executionState.dynamicState.backStencil.writeMask = writeMask; } } std::string description() override { return "vkCmdSetStencilWriteMask()"; } private: const VkStencilFaceFlags faceMask; const uint32_t writeMask; }; class CmdSetStencilReference : public vk::CommandBuffer::Command { public: CmdSetStencilReference(VkStencilFaceFlags faceMask, uint32_t reference) : faceMask(faceMask) , reference(reference) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { if(faceMask & VK_STENCIL_FACE_FRONT_BIT) { executionState.dynamicState.frontStencil.reference = reference; } if(faceMask & VK_STENCIL_FACE_BACK_BIT) { executionState.dynamicState.backStencil.reference = reference; } } std::string description() override { return "vkCmdSetStencilReference()"; } private: const VkStencilFaceFlags faceMask; const uint32_t reference; }; class CmdSetCullMode : public vk::CommandBuffer::Command { public: CmdSetCullMode(VkCullModeFlags cullMode) : cullMode(cullMode) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.cullMode = cullMode; } std::string description() override { return "vkCmdSetCullModeEXT()"; } private: const VkCullModeFlags cullMode; }; class CmdSetDepthBoundsTestEnable : public vk::CommandBuffer::Command { public: CmdSetDepthBoundsTestEnable(VkBool32 depthBoundsTestEnable) : depthBoundsTestEnable(depthBoundsTestEnable) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.depthBoundsTestEnable = depthBoundsTestEnable; } std::string description() override { return "vkCmdSetDepthBoundsTestEnableEXT()"; } private: const VkBool32 depthBoundsTestEnable; }; class CmdSetDepthCompareOp : public vk::CommandBuffer::Command { public: CmdSetDepthCompareOp(VkCompareOp depthCompareOp) : depthCompareOp(depthCompareOp) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.depthCompareOp = depthCompareOp; } std::string description() override { return "vkCmdSetDepthCompareOpEXT()"; } private: const VkCompareOp depthCompareOp; }; class CmdSetDepthTestEnable : public vk::CommandBuffer::Command { public: CmdSetDepthTestEnable(VkBool32 depthTestEnable) : depthTestEnable(depthTestEnable) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.depthTestEnable = depthTestEnable; } std::string description() override { return "vkCmdSetDepthTestEnableEXT()"; } private: const VkBool32 depthTestEnable; }; class CmdSetDepthWriteEnable : public vk::CommandBuffer::Command { public: CmdSetDepthWriteEnable(VkBool32 depthWriteEnable) : depthWriteEnable(depthWriteEnable) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.depthWriteEnable = depthWriteEnable; } std::string description() override { return "vkCmdSetDepthWriteEnableEXT()"; } private: const VkBool32 depthWriteEnable; }; class CmdSetFrontFace : public vk::CommandBuffer::Command { public: CmdSetFrontFace(VkFrontFace frontFace) : frontFace(frontFace) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.frontFace = frontFace; } std::string description() override { return "vkCmdSetFrontFaceEXT()"; } private: const VkFrontFace frontFace; }; class CmdSetPrimitiveTopology : public vk::CommandBuffer::Command { public: CmdSetPrimitiveTopology(VkPrimitiveTopology primitiveTopology) : primitiveTopology(primitiveTopology) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.primitiveTopology = primitiveTopology; } std::string description() override { return "vkCmdSetPrimitiveTopologyEXT()"; } private: const VkPrimitiveTopology primitiveTopology; }; class CmdSetScissorWithCount : public vk::CommandBuffer::Command { public: CmdSetScissorWithCount(uint32_t scissorCount, const VkRect2D *pScissors) : scissorCount(scissorCount) { memcpy(&(scissors[0]), pScissors, scissorCount * sizeof(VkRect2D)); } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.scissorCount = scissorCount; for(uint32_t i = 0; i < scissorCount; i++) { executionState.dynamicState.scissors[i] = scissors[i]; } } std::string description() override { return "vkCmdSetScissorWithCountEXT()"; } private: const uint32_t scissorCount; VkRect2D scissors[vk::MAX_VIEWPORTS]; }; class CmdSetStencilOp : public vk::CommandBuffer::Command { public: CmdSetStencilOp(VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) : faceMask(faceMask) , failOp(failOp) , passOp(passOp) , depthFailOp(depthFailOp) , compareOp(compareOp) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.faceMask |= faceMask; if(faceMask & VK_STENCIL_FACE_FRONT_BIT) { executionState.dynamicState.frontStencil.failOp = failOp; executionState.dynamicState.frontStencil.passOp = passOp; executionState.dynamicState.frontStencil.depthFailOp = depthFailOp; executionState.dynamicState.frontStencil.compareOp = compareOp; } if(faceMask & VK_STENCIL_FACE_BACK_BIT) { executionState.dynamicState.backStencil.failOp = failOp; executionState.dynamicState.backStencil.passOp = passOp; executionState.dynamicState.backStencil.depthFailOp = depthFailOp; executionState.dynamicState.backStencil.compareOp = compareOp; } } std::string description() override { return "vkCmdSetStencilOpEXT()"; } private: const VkStencilFaceFlags faceMask; const VkStencilOp failOp; const VkStencilOp passOp; const VkStencilOp depthFailOp; const VkCompareOp compareOp; }; class CmdSetStencilTestEnable : public vk::CommandBuffer::Command { public: CmdSetStencilTestEnable(VkBool32 stencilTestEnable) : stencilTestEnable(stencilTestEnable) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.stencilTestEnable = stencilTestEnable; } std::string description() override { return "vkCmdSetStencilTestEnableEXT()"; } private: const VkBool32 stencilTestEnable; }; class CmdSetViewportWithCount : public vk::CommandBuffer::Command { public: CmdSetViewportWithCount(uint32_t viewportCount, const VkViewport *pViewports) : viewportCount(viewportCount) { memcpy(viewports, pViewports, sizeof(VkViewport) * viewportCount); } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.viewportCount = viewportCount; for(uint32_t i = 0; i < viewportCount; i++) { executionState.dynamicState.viewports[i] = viewports[i]; } } std::string description() override { return "vkCmdSetViewportWithCountEXT()"; } private: const uint32_t viewportCount; VkViewport viewports[vk::MAX_VIEWPORTS]; }; class CmdSetRasterizerDiscardEnable : public vk::CommandBuffer::Command { public: CmdSetRasterizerDiscardEnable(VkBool32 rasterizerDiscardEnable) : rasterizerDiscardEnable(rasterizerDiscardEnable) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.rasterizerDiscardEnable = rasterizerDiscardEnable; } std::string description() override { return "vkCmdSetRasterizerDiscardEnable()"; } private: const VkBool32 rasterizerDiscardEnable; }; class CmdSetDepthBiasEnable : public vk::CommandBuffer::Command { public: CmdSetDepthBiasEnable(VkBool32 depthBiasEnable) : depthBiasEnable(depthBiasEnable) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.depthBiasEnable = depthBiasEnable; } std::string description() override { return "vkCmdSetDepthBiasEnable()"; } private: const VkBool32 depthBiasEnable; }; class CmdSetPrimitiveRestartEnable : public vk::CommandBuffer::Command { public: CmdSetPrimitiveRestartEnable(VkBool32 primitiveRestartEnable) : primitiveRestartEnable(primitiveRestartEnable) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.dynamicState.primitiveRestartEnable = primitiveRestartEnable; } std::string description() override { return "vkCmdSetPrimitiveRestartEnable()"; } private: const VkBool32 primitiveRestartEnable; }; class CmdSetVertexInput : public vk::CommandBuffer::Command { public: CmdSetVertexInput(uint32_t vertexBindingDescriptionCount, const VkVertexInputBindingDescription2EXT *pVertexBindingDescriptions, uint32_t vertexAttributeDescriptionCount, const VkVertexInputAttributeDescription2EXT *pVertexAttributeDescriptions) : // Note: the pNext values are unused, so this copy is currently safe. vertexBindingDescriptions(pVertexBindingDescriptions, pVertexBindingDescriptions + vertexBindingDescriptionCount) , vertexAttributeDescriptions(pVertexAttributeDescriptions, pVertexAttributeDescriptions + vertexAttributeDescriptionCount) {} void execute(vk::CommandBuffer::ExecutionState &executionState) override { for(const auto &desc : vertexBindingDescriptions) { vk::DynamicVertexInputBindingState &state = executionState.dynamicState.vertexInputBindings[desc.binding]; state.inputRate = desc.inputRate; state.stride = desc.stride; state.divisor = desc.divisor; } for(const auto &desc : vertexAttributeDescriptions) { vk::DynamicVertexInputAttributeState &state = executionState.dynamicState.vertexInputAttributes[desc.location]; state.format = desc.format; state.offset = desc.offset; state.binding = desc.binding; } } std::string description() override { return "vkCmdSetVertexInputEXT()"; } private: const std::vector vertexBindingDescriptions; const std::vector vertexAttributeDescriptions; }; class CmdDrawBase : public vk::CommandBuffer::Command { public: void draw(vk::CommandBuffer::ExecutionState &executionState, bool indexed, uint32_t count, uint32_t instanceCount, uint32_t first, int32_t vertexOffset, uint32_t firstInstance) { const auto &pipelineState = executionState.pipelineState[VK_PIPELINE_BIND_POINT_GRAPHICS]; auto *pipeline = static_cast(pipelineState.pipeline); vk::Attachments &attachments = pipeline->getAttachments(); executionState.bindAttachments(&attachments); vk::Inputs &inputs = pipeline->getInputs(); inputs.updateDescriptorSets(pipelineState.descriptorSetObjects, pipelineState.descriptorSets, pipelineState.descriptorDynamicOffsets); inputs.setVertexInputBinding(executionState.vertexInputBindings, executionState.dynamicState); inputs.bindVertexInputs(firstInstance); if(indexed) { vk::IndexBuffer &indexBuffer = pipeline->getIndexBuffer(); indexBuffer.setIndexBufferBinding(executionState.indexBufferBinding, executionState.indexType); } std::vector> indexBuffers; pipeline->getIndexBuffers(executionState.dynamicState, count, first, indexed, &indexBuffers); VkRect2D renderArea = executionState.getRenderArea(); for(uint32_t instance = firstInstance; instance != firstInstance + instanceCount; instance++) { // FIXME: reconsider instances/views nesting. auto layerMask = executionState.getLayerMask(); while(layerMask) { int layer = sw::log2i(layerMask); layerMask &= ~(1 << layer); for(auto indexBuffer : indexBuffers) { executionState.renderer->draw(pipeline, executionState.dynamicState, indexBuffer.first, vertexOffset, executionState.events, instance, layer, indexBuffer.second, renderArea, executionState.pushConstants); } } if(instanceCount > 1) { UNOPTIMIZED("Optimize instancing to use a single draw call."); // TODO(b/137740918) inputs.advanceInstanceAttributes(); } } } }; class CmdDraw : public CmdDrawBase { public: CmdDraw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) : vertexCount(vertexCount) , instanceCount(instanceCount) , firstVertex(firstVertex) , firstInstance(firstInstance) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { draw(executionState, false, vertexCount, instanceCount, 0, firstVertex, firstInstance); } std::string description() override { return "vkCmdDraw()"; } private: const uint32_t vertexCount; const uint32_t instanceCount; const uint32_t firstVertex; const uint32_t firstInstance; }; class CmdDrawIndexed : public CmdDrawBase { public: CmdDrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) : indexCount(indexCount) , instanceCount(instanceCount) , firstIndex(firstIndex) , vertexOffset(vertexOffset) , firstInstance(firstInstance) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { draw(executionState, true, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); } std::string description() override { return "vkCmdDrawIndexed()"; } private: const uint32_t indexCount; const uint32_t instanceCount; const uint32_t firstIndex; const int32_t vertexOffset; const uint32_t firstInstance; }; class CmdDrawIndirect : public CmdDrawBase { public: CmdDrawIndirect(vk::Buffer *buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) : buffer(buffer) , offset(offset) , drawCount(drawCount) , stride(stride) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { for(auto drawId = 0u; drawId < drawCount; drawId++) { const auto *cmd = reinterpret_cast(buffer->getOffsetPointer(offset + drawId * stride)); draw(executionState, false, cmd->vertexCount, cmd->instanceCount, 0, cmd->firstVertex, cmd->firstInstance); } } std::string description() override { return "vkCmdDrawIndirect()"; } private: const vk::Buffer *const buffer; const VkDeviceSize offset; const uint32_t drawCount; const uint32_t stride; }; class CmdDrawIndexedIndirect : public CmdDrawBase { public: CmdDrawIndexedIndirect(vk::Buffer *buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) : buffer(buffer) , offset(offset) , drawCount(drawCount) , stride(stride) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { for(auto drawId = 0u; drawId < drawCount; drawId++) { const auto *cmd = reinterpret_cast(buffer->getOffsetPointer(offset + drawId * stride)); draw(executionState, true, cmd->indexCount, cmd->instanceCount, cmd->firstIndex, cmd->vertexOffset, cmd->firstInstance); } } std::string description() override { return "vkCmdDrawIndexedIndirect()"; } private: const vk::Buffer *const buffer; const VkDeviceSize offset; const uint32_t drawCount; const uint32_t stride; }; class CmdCopyImage : public vk::CommandBuffer::Command { public: CmdCopyImage(const vk::Image *srcImage, vk::Image *dstImage, const VkImageCopy2 ®ion) : srcImage(srcImage) , dstImage(dstImage) , region(region) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { srcImage->copyTo(dstImage, region); } std::string description() override { return "vkCmdCopyImage()"; } private: const vk::Image *const srcImage; vk::Image *const dstImage; const VkImageCopy2 region; }; class CmdCopyBuffer : public vk::CommandBuffer::Command { public: CmdCopyBuffer(const vk::Buffer *srcBuffer, vk::Buffer *dstBuffer, const VkBufferCopy2 ®ion) : srcBuffer(srcBuffer) , dstBuffer(dstBuffer) , region(region) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { srcBuffer->copyTo(dstBuffer, region); } std::string description() override { return "vkCmdCopyBuffer()"; } private: const vk::Buffer *const srcBuffer; vk::Buffer *const dstBuffer; const VkBufferCopy2 region; }; class CmdCopyImageToBuffer : public vk::CommandBuffer::Command { public: CmdCopyImageToBuffer(vk::Image *srcImage, vk::Buffer *dstBuffer, const VkBufferImageCopy2 ®ion) : srcImage(srcImage) , dstBuffer(dstBuffer) , region(region) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { srcImage->copyTo(dstBuffer, region); } std::string description() override { return "vkCmdCopyImageToBuffer()"; } private: vk::Image *const srcImage; vk::Buffer *const dstBuffer; const VkBufferImageCopy2 region; }; class CmdCopyBufferToImage : public vk::CommandBuffer::Command { public: CmdCopyBufferToImage(vk::Buffer *srcBuffer, vk::Image *dstImage, const VkBufferImageCopy2 ®ion) : srcBuffer(srcBuffer) , dstImage(dstImage) , region(region) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { dstImage->copyFrom(srcBuffer, region); } std::string description() override { return "vkCmdCopyBufferToImage()"; } private: vk::Buffer *const srcBuffer; vk::Image *const dstImage; const VkBufferImageCopy2 region; }; class CmdFillBuffer : public vk::CommandBuffer::Command { public: CmdFillBuffer(vk::Buffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data) : dstBuffer(dstBuffer) , dstOffset(dstOffset) , size(size) , data(data) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { dstBuffer->fill(dstOffset, size, data); } std::string description() override { return "vkCmdFillBuffer()"; } private: vk::Buffer *const dstBuffer; const VkDeviceSize dstOffset; const VkDeviceSize size; const uint32_t data; }; class CmdUpdateBuffer : public vk::CommandBuffer::Command { public: CmdUpdateBuffer(vk::Buffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint8_t *pData) : dstBuffer(dstBuffer) , dstOffset(dstOffset) , data(pData, &pData[dataSize]) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { dstBuffer->update(dstOffset, data.size(), data.data()); } std::string description() override { return "vkCmdUpdateBuffer()"; } private: vk::Buffer *const dstBuffer; const VkDeviceSize dstOffset; const std::vector data; // FIXME(b/119409619): replace this vector by an allocator so we can control all memory allocations }; class CmdClearColorImage : public vk::CommandBuffer::Command { public: CmdClearColorImage(vk::Image *image, const VkClearColorValue &color, const VkImageSubresourceRange &range) : image(image) , color(color) , range(range) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { image->clear(color, range); } std::string description() override { return "vkCmdClearColorImage()"; } private: vk::Image *const image; const VkClearColorValue color; const VkImageSubresourceRange range; }; class CmdClearDepthStencilImage : public vk::CommandBuffer::Command { public: CmdClearDepthStencilImage(vk::Image *image, const VkClearDepthStencilValue &depthStencil, const VkImageSubresourceRange &range) : image(image) , depthStencil(depthStencil) , range(range) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { image->clear(depthStencil, range); } std::string description() override { return "vkCmdClearDepthStencilImage()"; } private: vk::Image *const image; const VkClearDepthStencilValue depthStencil; const VkImageSubresourceRange range; }; class CmdClearAttachment : public vk::CommandBuffer::Command { public: CmdClearAttachment(const VkClearAttachment &attachment, const VkClearRect &rect) : attachment(attachment) , rect(rect) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { // attachment clears are drawing operations, and so have rasterization-order guarantees. // however, we don't do the clear through the rasterizer, so need to ensure prior drawing // has completed first. executionState.renderer->synchronize(); if(executionState.renderPassFramebuffer) { executionState.renderPassFramebuffer->clearAttachment(executionState.renderPass, executionState.subpassIndex, attachment, rect); } else if(executionState.dynamicRendering) { uint32_t viewMask = executionState.dynamicRendering->getViewMask(); if(attachment.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) { ASSERT(attachment.colorAttachment < executionState.dynamicRendering->getColorAttachmentCount()); const VkRenderingAttachmentInfo *colorAttachment = executionState.dynamicRendering->getColorAttachment(attachment.colorAttachment); if(colorAttachment) { vk::ImageView *imageView = vk::Cast(colorAttachment->imageView); if(imageView) { imageView->clear(attachment.clearValue, VK_IMAGE_ASPECT_COLOR_BIT, rect, viewMask); } } } if(attachment.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) { const VkRenderingAttachmentInfo &depthAttachment = executionState.dynamicRendering->getDepthAttachment(); vk::ImageView *imageView = vk::Cast(depthAttachment.imageView); if(imageView) { imageView->clear(attachment.clearValue, VK_IMAGE_ASPECT_DEPTH_BIT, rect, viewMask); } } if(attachment.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) { const VkRenderingAttachmentInfo &stencilAttachment = executionState.dynamicRendering->getStencilAttachment(); vk::ImageView *imageView = vk::Cast(stencilAttachment.imageView); if(imageView) { imageView->clear(attachment.clearValue, VK_IMAGE_ASPECT_STENCIL_BIT, rect, viewMask); } } } } std::string description() override { return "vkCmdClearAttachment()"; } private: const VkClearAttachment attachment; const VkClearRect rect; }; class CmdBlitImage : public vk::CommandBuffer::Command { public: CmdBlitImage(const vk::Image *srcImage, vk::Image *dstImage, const VkImageBlit2 ®ion, VkFilter filter) : srcImage(srcImage) , dstImage(dstImage) , region(region) , filter(filter) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { srcImage->blitTo(dstImage, region, filter); } std::string description() override { return "vkCmdBlitImage()"; } private: const vk::Image *const srcImage; vk::Image *const dstImage; const VkImageBlit2 region; const VkFilter filter; }; class CmdResolveImage : public vk::CommandBuffer::Command { public: CmdResolveImage(const vk::Image *srcImage, vk::Image *dstImage, const VkImageResolve2 ®ion) : srcImage(srcImage) , dstImage(dstImage) , region(region) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { srcImage->resolveTo(dstImage, region); } std::string description() override { return "vkCmdBlitImage()"; } private: const vk::Image *const srcImage; vk::Image *const dstImage; const VkImageResolve2 region; }; class CmdPipelineBarrier : public vk::CommandBuffer::Command { public: void execute(vk::CommandBuffer::ExecutionState &executionState) override { // This is a very simple implementation that simply calls sw::Renderer::synchronize(), // since the driver is free to move the source stage towards the bottom of the pipe // and the target stage towards the top, so a full pipeline sync is spec compliant. executionState.renderer->synchronize(); // Right now all buffers are read-only in drawcalls but a similar mechanism will be required once we support SSBOs. // Also note that this would be a good moment to update cube map borders or decompress compressed textures, if necessary. } std::string description() override { return "vkCmdPipelineBarrier()"; } }; class CmdSignalEvent : public vk::CommandBuffer::Command { public: CmdSignalEvent(vk::Event *ev) : ev(ev) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.renderer->synchronize(); ev->signal(); } std::string description() override { return "vkCmdSignalEvent()"; } private: vk::Event *const ev; }; class CmdResetEvent : public vk::CommandBuffer::Command { public: CmdResetEvent(vk::Event *ev, VkPipelineStageFlags2 stageMask) : ev(ev) , stageMask(stageMask) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { ev->reset(); } std::string description() override { return "vkCmdResetEvent()"; } private: vk::Event *const ev; const VkPipelineStageFlags2 stageMask; // FIXME(b/117835459): We currently ignore the flags and reset the event at the last stage }; class CmdWaitEvent : public vk::CommandBuffer::Command { public: CmdWaitEvent(vk::Event *ev) : ev(ev) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { executionState.renderer->synchronize(); ev->wait(); } std::string description() override { return "vkCmdWaitEvent()"; } private: vk::Event *const ev; }; class CmdBindDescriptorSets : public vk::CommandBuffer::Command { public: CmdBindDescriptorSets(VkPipelineBindPoint pipelineBindPoint, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets, uint32_t firstDynamicOffset, uint32_t dynamicOffsetCount, const uint32_t *pDynamicOffsets) : pipelineBindPoint(pipelineBindPoint) , firstSet(firstSet) , descriptorSetCount(descriptorSetCount) , firstDynamicOffset(firstDynamicOffset) , dynamicOffsetCount(dynamicOffsetCount) { for(uint32_t i = 0; i < descriptorSetCount; i++) { // We need both a descriptor set object for updates and a descriptor set data pointer for routines descriptorSetObjects[firstSet + i] = vk::Cast(pDescriptorSets[i]); descriptorSets[firstSet + i] = vk::Cast(pDescriptorSets[i])->getDataAddress(); } for(uint32_t i = 0; i < dynamicOffsetCount; i++) { dynamicOffsets[firstDynamicOffset + i] = pDynamicOffsets[i]; } } void execute(vk::CommandBuffer::ExecutionState &executionState) override { ASSERT((size_t)pipelineBindPoint < executionState.pipelineState.size()); ASSERT(firstSet + descriptorSetCount <= vk::MAX_BOUND_DESCRIPTOR_SETS); ASSERT(firstDynamicOffset + dynamicOffsetCount <= vk::MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC); auto &pipelineState = executionState.pipelineState[pipelineBindPoint]; for(uint32_t i = firstSet; i < firstSet + descriptorSetCount; i++) { pipelineState.descriptorSetObjects[i] = descriptorSetObjects[i]; pipelineState.descriptorSets[i] = descriptorSets[i]; } for(uint32_t i = firstDynamicOffset; i < firstDynamicOffset + dynamicOffsetCount; i++) { pipelineState.descriptorDynamicOffsets[i] = dynamicOffsets[i]; } } std::string description() override { return "vkCmdBindDescriptorSets()"; } private: const VkPipelineBindPoint pipelineBindPoint; const uint32_t firstSet; const uint32_t descriptorSetCount; const uint32_t firstDynamicOffset; const uint32_t dynamicOffsetCount; vk::DescriptorSet::Array descriptorSetObjects; vk::DescriptorSet::Bindings descriptorSets; vk::DescriptorSet::DynamicOffsets dynamicOffsets; }; class CmdSetPushConstants : public vk::CommandBuffer::Command { public: CmdSetPushConstants(uint32_t offset, uint32_t size, const void *pValues) : offset(offset) , size(size) { ASSERT(offset < vk::MAX_PUSH_CONSTANT_SIZE); ASSERT(offset + size <= vk::MAX_PUSH_CONSTANT_SIZE); memcpy(data, pValues, size); } void execute(vk::CommandBuffer::ExecutionState &executionState) override { memcpy(&executionState.pushConstants.data[offset], data, size); } std::string description() override { return "vkCmdSetPushConstants()"; } private: const uint32_t offset; const uint32_t size; unsigned char data[vk::MAX_PUSH_CONSTANT_SIZE]; }; class CmdBeginQuery : public vk::CommandBuffer::Command { public: CmdBeginQuery(vk::QueryPool *queryPool, uint32_t query, VkQueryControlFlags flags) : queryPool(queryPool) , query(query) , flags(flags) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { // "If queries are used while executing a render pass instance that has multiview enabled, the query uses // N consecutive query indices in the query pool (starting at `query`)" for(uint32_t i = 0; i < executionState.viewCount(); i++) { queryPool->begin(query + i, flags); } // The renderer accumulates the result into a single query. ASSERT(queryPool->getType() == VK_QUERY_TYPE_OCCLUSION); executionState.renderer->addQuery(queryPool->getQuery(query)); } std::string description() override { return "vkCmdBeginQuery()"; } private: vk::QueryPool *const queryPool; const uint32_t query; const VkQueryControlFlags flags; }; class CmdEndQuery : public vk::CommandBuffer::Command { public: CmdEndQuery(vk::QueryPool *queryPool, uint32_t query) : queryPool(queryPool) , query(query) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { // The renderer accumulates the result into a single query. ASSERT(queryPool->getType() == VK_QUERY_TYPE_OCCLUSION); executionState.renderer->removeQuery(queryPool->getQuery(query)); // "implementations may write the total result to the first query and write zero to the other queries." for(uint32_t i = 1; i < executionState.viewCount(); i++) { queryPool->getQuery(query + i)->set(0); } for(uint32_t i = 0; i < executionState.viewCount(); i++) { queryPool->end(query + i); } } std::string description() override { return "vkCmdEndQuery()"; } private: vk::QueryPool *const queryPool; const uint32_t query; }; class CmdResetQueryPool : public vk::CommandBuffer::Command { public: CmdResetQueryPool(vk::QueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount) : queryPool(queryPool) , firstQuery(firstQuery) , queryCount(queryCount) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { queryPool->reset(firstQuery, queryCount); } std::string description() override { return "vkCmdResetQueryPool()"; } private: vk::QueryPool *const queryPool; const uint32_t firstQuery; const uint32_t queryCount; }; class CmdWriteTimeStamp : public vk::CommandBuffer::Command { public: CmdWriteTimeStamp(vk::QueryPool *queryPool, uint32_t query, VkPipelineStageFlagBits2 stage) : queryPool(queryPool) , query(query) , stage(stage) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { if(stage & ~(VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT)) { // The `top of pipe` and `draw indirect` stages are handled in command buffer processing so a timestamp write // done in those stages can just be done here without any additional synchronization. // Everything else is deferred to the Renderer; we will treat those stages all as if they were // `bottom of pipe`. // // FIXME(chrisforbes): once Marl is integrated, do this in a task so we don't have to stall here. executionState.renderer->synchronize(); } // "the timestamp uses N consecutive query indices in the query pool (starting at `query`) where // N is the number of bits set in the view mask of the subpass the command is executed in." for(uint32_t i = 0; i < executionState.viewCount(); i++) { queryPool->writeTimestamp(query + i); } } std::string description() override { return "vkCmdWriteTimeStamp()"; } private: vk::QueryPool *const queryPool; const uint32_t query; const VkPipelineStageFlagBits2 stage; }; class CmdCopyQueryPoolResults : public vk::CommandBuffer::Command { public: CmdCopyQueryPoolResults(const vk::QueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount, vk::Buffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) : queryPool(queryPool) , firstQuery(firstQuery) , queryCount(queryCount) , dstBuffer(dstBuffer) , dstOffset(dstOffset) , stride(stride) , flags(flags) { } void execute(vk::CommandBuffer::ExecutionState &executionState) override { queryPool->getResults(firstQuery, queryCount, dstBuffer->getSize() - dstOffset, dstBuffer->getOffsetPointer(dstOffset), stride, flags); } std::string description() override { return "vkCmdCopyQueryPoolResults()"; } private: const vk::QueryPool *const queryPool; const uint32_t firstQuery; const uint32_t queryCount; vk::Buffer *const dstBuffer; const VkDeviceSize dstOffset; const VkDeviceSize stride; const VkQueryResultFlags flags; }; } // anonymous namespace namespace vk { DynamicRendering::DynamicRendering(const VkRenderingInfo *pRenderingInfo) : renderArea(pRenderingInfo->renderArea) , layerCount(pRenderingInfo->layerCount) , viewMask(pRenderingInfo->viewMask) , colorAttachmentCount(pRenderingInfo->colorAttachmentCount) , flags(pRenderingInfo->flags) { if(colorAttachmentCount > 0) { for(uint32_t i = 0; i < colorAttachmentCount; ++i) { colorAttachments[i] = pRenderingInfo->pColorAttachments[i]; } } if(pRenderingInfo->pDepthAttachment) { depthAttachment = *pRenderingInfo->pDepthAttachment; } if(pRenderingInfo->pStencilAttachment) { stencilAttachment = *pRenderingInfo->pStencilAttachment; } } void DynamicRendering::getAttachments(Attachments *attachments) const { for(uint32_t i = 0; i < sw::MAX_COLOR_BUFFERS; ++i) { attachments->colorBuffer[i] = nullptr; } for(uint32_t i = 0; i < sw::MAX_COLOR_BUFFERS; ++i) { const uint32_t location = attachments->indexToLocation[i]; if(i < colorAttachmentCount && location != VK_ATTACHMENT_UNUSED) { attachments->colorBuffer[location] = vk::Cast(colorAttachments[i].imageView); } } attachments->depthBuffer = vk::Cast(depthAttachment.imageView); attachments->stencilBuffer = vk::Cast(stencilAttachment.imageView); } CommandBuffer::CommandBuffer(Device *device, VkCommandBufferLevel pLevel) : device(device) , level(pLevel) { } void CommandBuffer::destroy(const VkAllocationCallbacks *pAllocator) { } void CommandBuffer::resetState() { // FIXME (b/119409619): replace this vector by an allocator so we can control all memory allocations commands.clear(); state = INITIAL; } VkResult CommandBuffer::begin(VkCommandBufferUsageFlags flags, const VkCommandBufferInheritanceInfo *pInheritanceInfo) { ASSERT((state != RECORDING) && (state != PENDING)); // Nothing interesting to do based on flags. We don't have any optimizations // to apply for ONE_TIME_SUBMIT or (lack of) SIMULTANEOUS_USE. RENDER_PASS_CONTINUE // must also provide a non-null pInheritanceInfo, which we don't implement yet, but is caught below. (void)flags; // pInheritanceInfo merely contains optimization hints, so we currently ignore it // "pInheritanceInfo is a pointer to a VkCommandBufferInheritanceInfo structure, used if commandBuffer is a // secondary command buffer. If this is a primary command buffer, then this value is ignored." if(level == VK_COMMAND_BUFFER_LEVEL_SECONDARY) { if(pInheritanceInfo->queryFlags != 0) { // "If the inherited queries feature is not enabled, queryFlags must be 0" UNSUPPORTED("VkPhysicalDeviceFeatures::inheritedQueries"); } } if(state != INITIAL) { // Implicit reset resetState(); } state = RECORDING; return VK_SUCCESS; } VkResult CommandBuffer::end() { ASSERT(state == RECORDING); state = EXECUTABLE; return VK_SUCCESS; } VkResult CommandBuffer::reset(VkCommandPoolResetFlags flags) { ASSERT(state != PENDING); resetState(); return VK_SUCCESS; } template void CommandBuffer::addCommand(Args &&...args) { // FIXME (b/119409619): use an allocator here so we can control all memory allocations commands.push_back(std::make_unique(std::forward(args)...)); } void CommandBuffer::beginRenderPass(RenderPass *renderPass, Framebuffer *framebuffer, VkRect2D renderArea, uint32_t clearValueCount, const VkClearValue *clearValues, VkSubpassContents contents, const VkRenderPassAttachmentBeginInfo *attachmentInfo) { ASSERT(state == RECORDING); addCommand<::CmdBeginRenderPass>(renderPass, framebuffer, renderArea, clearValueCount, clearValues, attachmentInfo); } void CommandBuffer::nextSubpass(VkSubpassContents contents) { ASSERT(state == RECORDING); addCommand<::CmdNextSubpass>(); } void CommandBuffer::endRenderPass() { addCommand<::CmdEndRenderPass>(); } void CommandBuffer::executeCommands(uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers) { ASSERT(state == RECORDING); for(uint32_t i = 0; i < commandBufferCount; ++i) { addCommand<::CmdExecuteCommands>(vk::Cast(pCommandBuffers[i])); } } void CommandBuffer::beginRendering(const VkRenderingInfo *pRenderingInfo) { ASSERT(state == RECORDING); addCommand<::CmdBeginRendering>(pRenderingInfo); } void CommandBuffer::endRendering() { ASSERT(state == RECORDING); addCommand<::CmdEndRendering>(); } void CommandBuffer::setDeviceMask(uint32_t deviceMask) { // SwiftShader only has one device, so we ignore the device mask } void CommandBuffer::dispatchBase(uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { addCommand<::CmdDispatch>(baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ); } void CommandBuffer::pipelineBarrier(const VkDependencyInfo &pDependencyInfo) { addCommand<::CmdPipelineBarrier>(); } void CommandBuffer::bindPipeline(VkPipelineBindPoint pipelineBindPoint, Pipeline *pipeline) { switch(pipelineBindPoint) { case VK_PIPELINE_BIND_POINT_COMPUTE: case VK_PIPELINE_BIND_POINT_GRAPHICS: addCommand<::CmdPipelineBind>(pipelineBindPoint, pipeline); break; default: UNSUPPORTED("VkPipelineBindPoint %d", int(pipelineBindPoint)); } } void CommandBuffer::bindVertexBuffers(uint32_t firstBinding, uint32_t bindingCount, const VkBuffer *pBuffers, const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes, const VkDeviceSize *pStrides) { for(uint32_t i = 0; i < bindingCount; ++i) { addCommand<::CmdVertexBufferBind>(i + firstBinding, vk::Cast(pBuffers[i]), pOffsets[i], pSizes ? pSizes[i] : 0, pStrides ? pStrides[i] : 0, pStrides); } } void CommandBuffer::beginQuery(QueryPool *queryPool, uint32_t query, VkQueryControlFlags flags) { addCommand<::CmdBeginQuery>(queryPool, query, flags); } void CommandBuffer::endQuery(QueryPool *queryPool, uint32_t query) { addCommand<::CmdEndQuery>(queryPool, query); } void CommandBuffer::resetQueryPool(QueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount) { addCommand<::CmdResetQueryPool>(queryPool, firstQuery, queryCount); } void CommandBuffer::writeTimestamp(VkPipelineStageFlags2 pipelineStage, QueryPool *queryPool, uint32_t query) { addCommand<::CmdWriteTimeStamp>(queryPool, query, pipelineStage); } void CommandBuffer::copyQueryPoolResults(const QueryPool *queryPool, uint32_t firstQuery, uint32_t queryCount, Buffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) { addCommand<::CmdCopyQueryPoolResults>(queryPool, firstQuery, queryCount, dstBuffer, dstOffset, stride, flags); } void CommandBuffer::pushConstants(PipelineLayout *layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void *pValues) { addCommand<::CmdSetPushConstants>(offset, size, pValues); } void CommandBuffer::setViewport(uint32_t firstViewport, uint32_t viewportCount, const VkViewport *pViewports) { if(firstViewport != 0 || viewportCount > 1) { UNSUPPORTED("VkPhysicalDeviceFeatures::multiViewport"); } for(uint32_t i = 0; i < viewportCount; i++) { addCommand<::CmdSetViewport>(pViewports[i], i + firstViewport); } } void CommandBuffer::setScissor(uint32_t firstScissor, uint32_t scissorCount, const VkRect2D *pScissors) { if(firstScissor != 0 || scissorCount > 1) { UNSUPPORTED("VkPhysicalDeviceFeatures::multiViewport"); } for(uint32_t i = 0; i < scissorCount; i++) { addCommand<::CmdSetScissor>(pScissors[i], i + firstScissor); } } void CommandBuffer::setLineWidth(float lineWidth) { addCommand<::CmdSetLineWidth>(lineWidth); } void CommandBuffer::setDepthBias(float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor) { addCommand<::CmdSetDepthBias>(depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor); } void CommandBuffer::setBlendConstants(const float blendConstants[4]) { addCommand<::CmdSetBlendConstants>(blendConstants); } void CommandBuffer::setDepthBounds(float minDepthBounds, float maxDepthBounds) { addCommand<::CmdSetDepthBounds>(minDepthBounds, maxDepthBounds); } void CommandBuffer::setStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t compareMask) { // faceMask must not be 0 ASSERT(faceMask != 0); addCommand<::CmdSetStencilCompareMask>(faceMask, compareMask); } void CommandBuffer::setStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t writeMask) { // faceMask must not be 0 ASSERT(faceMask != 0); addCommand<::CmdSetStencilWriteMask>(faceMask, writeMask); } void CommandBuffer::setStencilReference(VkStencilFaceFlags faceMask, uint32_t reference) { // faceMask must not be 0 ASSERT(faceMask != 0); addCommand<::CmdSetStencilReference>(faceMask, reference); } void CommandBuffer::setCullMode(VkCullModeFlags cullMode) { addCommand<::CmdSetCullMode>(cullMode); } void CommandBuffer::setDepthBoundsTestEnable(VkBool32 depthBoundsTestEnable) { addCommand<::CmdSetDepthBoundsTestEnable>(depthBoundsTestEnable); } void CommandBuffer::setDepthCompareOp(VkCompareOp depthCompareOp) { addCommand<::CmdSetDepthCompareOp>(depthCompareOp); } void CommandBuffer::setDepthTestEnable(VkBool32 depthTestEnable) { addCommand<::CmdSetDepthTestEnable>(depthTestEnable); } void CommandBuffer::setDepthWriteEnable(VkBool32 depthWriteEnable) { addCommand<::CmdSetDepthWriteEnable>(depthWriteEnable); } void CommandBuffer::setFrontFace(VkFrontFace frontFace) { addCommand<::CmdSetFrontFace>(frontFace); } void CommandBuffer::setPrimitiveTopology(VkPrimitiveTopology primitiveTopology) { addCommand<::CmdSetPrimitiveTopology>(primitiveTopology); } void CommandBuffer::setScissorWithCount(uint32_t scissorCount, const VkRect2D *pScissors) { addCommand<::CmdSetScissorWithCount>(scissorCount, pScissors); } void CommandBuffer::setStencilOp(VkStencilFaceFlags faceMask, VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp) { addCommand<::CmdSetStencilOp>(faceMask, failOp, passOp, depthFailOp, compareOp); } void CommandBuffer::setStencilTestEnable(VkBool32 stencilTestEnable) { addCommand<::CmdSetStencilTestEnable>(stencilTestEnable); } void CommandBuffer::setViewportWithCount(uint32_t viewportCount, const VkViewport *pViewports) { addCommand<::CmdSetViewportWithCount>(viewportCount, pViewports); } void CommandBuffer::setRasterizerDiscardEnable(VkBool32 rasterizerDiscardEnable) { addCommand<::CmdSetRasterizerDiscardEnable>(rasterizerDiscardEnable); } void CommandBuffer::setDepthBiasEnable(VkBool32 depthBiasEnable) { addCommand<::CmdSetDepthBiasEnable>(depthBiasEnable); } void CommandBuffer::setPrimitiveRestartEnable(VkBool32 primitiveRestartEnable) { addCommand<::CmdSetPrimitiveRestartEnable>(primitiveRestartEnable); } void CommandBuffer::setVertexInput(uint32_t vertexBindingDescriptionCount, const VkVertexInputBindingDescription2EXT *pVertexBindingDescriptions, uint32_t vertexAttributeDescriptionCount, const VkVertexInputAttributeDescription2EXT *pVertexAttributeDescriptions) { addCommand<::CmdSetVertexInput>(vertexBindingDescriptionCount, pVertexBindingDescriptions, vertexAttributeDescriptionCount, pVertexAttributeDescriptions); } void CommandBuffer::bindDescriptorSets(VkPipelineBindPoint pipelineBindPoint, const PipelineLayout *pipelineLayout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t *pDynamicOffsets) { ASSERT(state == RECORDING); auto firstDynamicOffset = (dynamicOffsetCount != 0) ? pipelineLayout->getDynamicOffsetIndex(firstSet, 0) : 0; addCommand<::CmdBindDescriptorSets>( pipelineBindPoint, firstSet, descriptorSetCount, pDescriptorSets, firstDynamicOffset, dynamicOffsetCount, pDynamicOffsets); } void CommandBuffer::bindIndexBuffer(Buffer *buffer, VkDeviceSize offset, VkIndexType indexType) { addCommand<::CmdIndexBufferBind>(buffer, offset, indexType); } void CommandBuffer::dispatch(uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { addCommand<::CmdDispatch>(0, 0, 0, groupCountX, groupCountY, groupCountZ); } void CommandBuffer::dispatchIndirect(Buffer *buffer, VkDeviceSize offset) { addCommand<::CmdDispatchIndirect>(buffer, offset); } void CommandBuffer::copyBuffer(const VkCopyBufferInfo2 ©BufferInfo) { ASSERT(state == RECORDING); for(uint32_t i = 0; i < copyBufferInfo.regionCount; i++) { addCommand<::CmdCopyBuffer>( vk::Cast(copyBufferInfo.srcBuffer), vk::Cast(copyBufferInfo.dstBuffer), copyBufferInfo.pRegions[i]); } } void CommandBuffer::copyImage(const VkCopyImageInfo2 ©ImageInfo) { ASSERT(state == RECORDING); ASSERT(copyImageInfo.srcImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL || copyImageInfo.srcImageLayout == VK_IMAGE_LAYOUT_GENERAL); ASSERT(copyImageInfo.dstImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL || copyImageInfo.dstImageLayout == VK_IMAGE_LAYOUT_GENERAL); for(uint32_t i = 0; i < copyImageInfo.regionCount; i++) { addCommand<::CmdCopyImage>( vk::Cast(copyImageInfo.srcImage), vk::Cast(copyImageInfo.dstImage), copyImageInfo.pRegions[i]); } } void CommandBuffer::blitImage(const VkBlitImageInfo2 &blitImageInfo) { ASSERT(state == RECORDING); ASSERT(blitImageInfo.srcImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL || blitImageInfo.srcImageLayout == VK_IMAGE_LAYOUT_GENERAL); ASSERT(blitImageInfo.dstImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL || blitImageInfo.dstImageLayout == VK_IMAGE_LAYOUT_GENERAL); for(uint32_t i = 0; i < blitImageInfo.regionCount; i++) { addCommand<::CmdBlitImage>( vk::Cast(blitImageInfo.srcImage), vk::Cast(blitImageInfo.dstImage), blitImageInfo.pRegions[i], blitImageInfo.filter); } } void CommandBuffer::copyBufferToImage(const VkCopyBufferToImageInfo2 ©BufferToImageInfo) { ASSERT(state == RECORDING); for(uint32_t i = 0; i < copyBufferToImageInfo.regionCount; i++) { addCommand<::CmdCopyBufferToImage>( vk::Cast(copyBufferToImageInfo.srcBuffer), vk::Cast(copyBufferToImageInfo.dstImage), copyBufferToImageInfo.pRegions[i]); } } void CommandBuffer::copyImageToBuffer(const VkCopyImageToBufferInfo2 ©ImageToBufferInfo) { ASSERT(state == RECORDING); ASSERT(copyImageToBufferInfo.srcImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL || copyImageToBufferInfo.srcImageLayout == VK_IMAGE_LAYOUT_GENERAL); for(uint32_t i = 0; i < copyImageToBufferInfo.regionCount; i++) { addCommand<::CmdCopyImageToBuffer>( vk::Cast(copyImageToBufferInfo.srcImage), vk::Cast(copyImageToBufferInfo.dstBuffer), copyImageToBufferInfo.pRegions[i]); } } void CommandBuffer::updateBuffer(Buffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void *pData) { ASSERT(state == RECORDING); addCommand<::CmdUpdateBuffer>(dstBuffer, dstOffset, dataSize, reinterpret_cast(pData)); } void CommandBuffer::fillBuffer(Buffer *dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data) { ASSERT(state == RECORDING); addCommand<::CmdFillBuffer>(dstBuffer, dstOffset, size, data); } void CommandBuffer::clearColorImage(Image *image, VkImageLayout imageLayout, const VkClearColorValue *pColor, uint32_t rangeCount, const VkImageSubresourceRange *pRanges) { ASSERT(state == RECORDING); for(uint32_t i = 0; i < rangeCount; i++) { addCommand<::CmdClearColorImage>(image, *pColor, pRanges[i]); } } void CommandBuffer::clearDepthStencilImage(Image *image, VkImageLayout imageLayout, const VkClearDepthStencilValue *pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange *pRanges) { ASSERT(state == RECORDING); for(uint32_t i = 0; i < rangeCount; i++) { addCommand<::CmdClearDepthStencilImage>(image, *pDepthStencil, pRanges[i]); } } void CommandBuffer::clearAttachments(uint32_t attachmentCount, const VkClearAttachment *pAttachments, uint32_t rectCount, const VkClearRect *pRects) { ASSERT(state == RECORDING); for(uint32_t i = 0; i < attachmentCount; i++) { for(uint32_t j = 0; j < rectCount; j++) { addCommand<::CmdClearAttachment>(pAttachments[i], pRects[j]); } } } void CommandBuffer::resolveImage(const VkResolveImageInfo2 &resolveImageInfo) { ASSERT(state == RECORDING); ASSERT(resolveImageInfo.srcImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL || resolveImageInfo.srcImageLayout == VK_IMAGE_LAYOUT_GENERAL); ASSERT(resolveImageInfo.dstImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL || resolveImageInfo.dstImageLayout == VK_IMAGE_LAYOUT_GENERAL); for(uint32_t i = 0; i < resolveImageInfo.regionCount; i++) { addCommand<::CmdResolveImage>( vk::Cast(resolveImageInfo.srcImage), vk::Cast(resolveImageInfo.dstImage), resolveImageInfo.pRegions[i]); } } void CommandBuffer::setEvent(Event *event, const VkDependencyInfo &pDependencyInfo) { ASSERT(state == RECORDING); // TODO(b/117835459): We currently ignore the flags and signal the event at the last stage addCommand<::CmdSignalEvent>(event); } void CommandBuffer::resetEvent(Event *event, VkPipelineStageFlags2 stageMask) { ASSERT(state == RECORDING); addCommand<::CmdResetEvent>(event, stageMask); } void CommandBuffer::waitEvents(uint32_t eventCount, const VkEvent *pEvents, const VkDependencyInfo &pDependencyInfo) { ASSERT(state == RECORDING); // TODO(b/117835459): Since we always do a full barrier, all memory barrier related arguments are ignored // Note: srcStageMask and dstStageMask are currently ignored for(uint32_t i = 0; i < eventCount; i++) { addCommand<::CmdWaitEvent>(vk::Cast(pEvents[i])); } } void CommandBuffer::draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { addCommand<::CmdDraw>(vertexCount, instanceCount, firstVertex, firstInstance); } void CommandBuffer::drawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) { addCommand<::CmdDrawIndexed>(indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); } void CommandBuffer::drawIndirect(Buffer *buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) { addCommand<::CmdDrawIndirect>(buffer, offset, drawCount, stride); } void CommandBuffer::drawIndexedIndirect(Buffer *buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) { addCommand<::CmdDrawIndexedIndirect>(buffer, offset, drawCount, stride); } void CommandBuffer::beginDebugUtilsLabel(const VkDebugUtilsLabelEXT *pLabelInfo) { // Optional debug label region } void CommandBuffer::endDebugUtilsLabel() { // Close debug label region opened with beginDebugUtilsLabel() } void CommandBuffer::insertDebugUtilsLabel(const VkDebugUtilsLabelEXT *pLabelInfo) { // Optional single debug label } void CommandBuffer::submit(CommandBuffer::ExecutionState &executionState) { // Perform recorded work state = PENDING; for(auto &command : commands) { command->execute(executionState); } // After work is completed state = EXECUTABLE; } void CommandBuffer::submitSecondary(CommandBuffer::ExecutionState &executionState) const { for(auto &command : commands) { command->execute(executionState); } } void CommandBuffer::ExecutionState::bindAttachments(Attachments *attachments) { // Binds all the attachments for the current subpass // Ideally this would be performed by BeginRenderPass and NextSubpass, but // there is too much stomping of the renderer's state by setContext() in // draws. if(renderPass) { const auto &subpass = renderPass->getSubpass(subpassIndex); for(auto i = 0u; i < subpass.colorAttachmentCount; i++) { auto attachmentReference = subpass.pColorAttachments[i]; if(attachmentReference.attachment != VK_ATTACHMENT_UNUSED) { attachments->colorBuffer[i] = renderPassFramebuffer->getAttachment(attachmentReference.attachment); } } auto attachmentReference = subpass.pDepthStencilAttachment; if(attachmentReference && attachmentReference->attachment != VK_ATTACHMENT_UNUSED) { auto *attachment = renderPassFramebuffer->getAttachment(attachmentReference->attachment); if(attachment->hasDepthAspect()) { attachments->depthBuffer = attachment; } if(attachment->hasStencilAspect()) { attachments->stencilBuffer = attachment; } } } else if(dynamicRendering) { dynamicRendering->getAttachments(attachments); } } VkRect2D CommandBuffer::ExecutionState::getRenderArea() const { VkRect2D renderArea = {}; if(renderPassFramebuffer) { renderArea.extent = renderPassFramebuffer->getExtent(); } else if(dynamicRendering) { renderArea = dynamicRendering->getRenderArea(); } return renderArea; } // The layer mask is the same as the view mask when multiview is enabled, // or 1 if multiview is disabled. uint32_t CommandBuffer::ExecutionState::getLayerMask() const { uint32_t layerMask = 1; if(renderPass) { layerMask = renderPass->getViewMask(subpassIndex); } else if(dynamicRendering) { layerMask = dynamicRendering->getViewMask(); } return sw::max(layerMask, 1u); } // Returns the number of bits set in the view mask, or 1 if multiview is disabled. uint32_t CommandBuffer::ExecutionState::viewCount() const { return static_cast(std::bitset<32>(getLayerMask()).count()); } } // namespace vk