| // Copyright 2020 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 "Context.hpp" | |
| #include "Vulkan/VkBuffer.hpp" | |
| #include "Vulkan/VkDevice.hpp" | |
| #include "Vulkan/VkImageView.hpp" | |
| #include "Vulkan/VkRenderPass.hpp" | |
| #include "Vulkan/VkStringify.hpp" | |
| namespace { | |
| uint32_t ComputePrimitiveCount(VkPrimitiveTopology topology, uint32_t vertexCount) | |
| { | |
| switch(topology) | |
| { | |
| case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: | |
| return vertexCount; | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: | |
| return vertexCount / 2; | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: | |
| return std::max<uint32_t>(vertexCount, 1) - 1; | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: | |
| return vertexCount / 3; | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: | |
| return std::max<uint32_t>(vertexCount, 2) - 2; | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: | |
| return std::max<uint32_t>(vertexCount, 2) - 2; | |
| default: | |
| UNSUPPORTED("VkPrimitiveTopology %d", int(topology)); | |
| } | |
| return 0; | |
| } | |
| template<typename T> | |
| void ProcessPrimitiveRestart(T *indexBuffer, | |
| VkPrimitiveTopology topology, | |
| uint32_t count, | |
| std::vector<std::pair<uint32_t, void *>> *indexBuffers) | |
| { | |
| static const T RestartIndex = static_cast<T>(-1); | |
| T *indexBufferStart = indexBuffer; | |
| uint32_t vertexCount = 0; | |
| for(uint32_t i = 0; i < count; i++) | |
| { | |
| if(indexBuffer[i] == RestartIndex) | |
| { | |
| // Record previous segment | |
| if(vertexCount > 0) | |
| { | |
| uint32_t primitiveCount = ComputePrimitiveCount(topology, vertexCount); | |
| if(primitiveCount > 0) | |
| { | |
| indexBuffers->push_back({ primitiveCount, indexBufferStart }); | |
| } | |
| } | |
| vertexCount = 0; | |
| } | |
| else | |
| { | |
| if(vertexCount == 0) | |
| { | |
| indexBufferStart = indexBuffer + i; | |
| } | |
| vertexCount++; | |
| } | |
| } | |
| // Record last segment | |
| if(vertexCount > 0) | |
| { | |
| uint32_t primitiveCount = ComputePrimitiveCount(topology, vertexCount); | |
| if(primitiveCount > 0) | |
| { | |
| indexBuffers->push_back({ primitiveCount, indexBufferStart }); | |
| } | |
| } | |
| } | |
| } // namespace | |
| namespace vk { | |
| int IndexBuffer::bytesPerIndex() const | |
| { | |
| return indexType == VK_INDEX_TYPE_UINT16 ? 2 : 4; | |
| } | |
| void IndexBuffer::setIndexBufferBinding(const VertexInputBinding &indexBufferBinding, VkIndexType type) | |
| { | |
| binding = indexBufferBinding; | |
| indexType = type; | |
| } | |
| void IndexBuffer::getIndexBuffers(VkPrimitiveTopology topology, uint32_t count, uint32_t first, bool indexed, bool hasPrimitiveRestartEnable, std::vector<std::pair<uint32_t, void *>> *indexBuffers) const | |
| { | |
| if(indexed) | |
| { | |
| void *indexBuffer = binding.buffer->getOffsetPointer(binding.offset + first * bytesPerIndex()); | |
| if(hasPrimitiveRestartEnable) | |
| { | |
| switch(indexType) | |
| { | |
| case VK_INDEX_TYPE_UINT16: | |
| ProcessPrimitiveRestart(static_cast<uint16_t *>(indexBuffer), topology, count, indexBuffers); | |
| break; | |
| case VK_INDEX_TYPE_UINT32: | |
| ProcessPrimitiveRestart(static_cast<uint32_t *>(indexBuffer), topology, count, indexBuffers); | |
| break; | |
| default: | |
| UNSUPPORTED("VkIndexType %d", int(indexType)); | |
| } | |
| } | |
| else | |
| { | |
| indexBuffers->push_back({ ComputePrimitiveCount(topology, count), indexBuffer }); | |
| } | |
| } | |
| else | |
| { | |
| indexBuffers->push_back({ ComputePrimitiveCount(topology, count), nullptr }); | |
| } | |
| } | |
| bool Attachments::isColorClamped(int index) const | |
| { | |
| if(renderTarget[index] && renderTarget[index]->getFormat().isFloatFormat()) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| VkFormat Attachments::renderTargetInternalFormat(int index) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| if(renderTarget[index]) | |
| { | |
| return renderTarget[index]->getFormat(); | |
| } | |
| else | |
| { | |
| return VK_FORMAT_UNDEFINED; | |
| } | |
| } | |
| Inputs::Inputs(const VkPipelineVertexInputStateCreateInfo *vertexInputState) | |
| { | |
| if(vertexInputState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("vertexInputState->flags"); | |
| } | |
| // Temporary in-binding-order representation of buffer strides, to be consumed below | |
| // when considering attributes. TODO: unfuse buffers from attributes in backend, is old GL model. | |
| uint32_t vertexStrides[MAX_VERTEX_INPUT_BINDINGS]; | |
| uint32_t instanceStrides[MAX_VERTEX_INPUT_BINDINGS]; | |
| for(uint32_t i = 0; i < vertexInputState->vertexBindingDescriptionCount; i++) | |
| { | |
| auto const &desc = vertexInputState->pVertexBindingDescriptions[i]; | |
| vertexStrides[desc.binding] = desc.inputRate == VK_VERTEX_INPUT_RATE_VERTEX ? desc.stride : 0; | |
| instanceStrides[desc.binding] = desc.inputRate == VK_VERTEX_INPUT_RATE_INSTANCE ? desc.stride : 0; | |
| } | |
| for(uint32_t i = 0; i < vertexInputState->vertexAttributeDescriptionCount; i++) | |
| { | |
| auto const &desc = vertexInputState->pVertexAttributeDescriptions[i]; | |
| sw::Stream &input = stream[desc.location]; | |
| input.format = desc.format; | |
| input.offset = desc.offset; | |
| input.binding = desc.binding; | |
| input.vertexStride = vertexStrides[desc.binding]; | |
| input.instanceStride = instanceStrides[desc.binding]; | |
| } | |
| } | |
| void Inputs::updateDescriptorSets(const DescriptorSet::Array &dso, | |
| const DescriptorSet::Bindings &ds, | |
| const DescriptorSet::DynamicOffsets &ddo) | |
| { | |
| descriptorSetObjects = dso; | |
| descriptorSets = ds; | |
| descriptorDynamicOffsets = ddo; | |
| } | |
| void Inputs::bindVertexInputs(int firstInstance) | |
| { | |
| for(uint32_t i = 0; i < MAX_VERTEX_INPUT_BINDINGS; i++) | |
| { | |
| auto &attrib = stream[i]; | |
| if(attrib.format != VK_FORMAT_UNDEFINED) | |
| { | |
| const auto &vertexInput = vertexInputBindings[attrib.binding]; | |
| VkDeviceSize offset = attrib.offset + vertexInput.offset + | |
| attrib.instanceStride * firstInstance; | |
| attrib.buffer = vertexInput.buffer ? vertexInput.buffer->getOffsetPointer(offset) : nullptr; | |
| VkDeviceSize size = vertexInput.buffer ? vertexInput.buffer->getSize() : 0; | |
| attrib.robustnessSize = (size > offset) ? size - offset : 0; | |
| } | |
| } | |
| } | |
| void Inputs::setVertexInputBinding(const VertexInputBinding bindings[]) | |
| { | |
| for(uint32_t i = 0; i < MAX_VERTEX_INPUT_BINDINGS; ++i) | |
| { | |
| vertexInputBindings[i] = bindings[i]; | |
| } | |
| } | |
| // TODO(b/137740918): Optimize instancing to use a single draw call. | |
| void Inputs::advanceInstanceAttributes() | |
| { | |
| for(uint32_t i = 0; i < vk::MAX_VERTEX_INPUT_BINDINGS; i++) | |
| { | |
| auto &attrib = stream[i]; | |
| if((attrib.format != VK_FORMAT_UNDEFINED) && attrib.instanceStride && (attrib.instanceStride < attrib.robustnessSize)) | |
| { | |
| // Under the casts: attrib.buffer += attrib.instanceStride | |
| attrib.buffer = (void const *)((uintptr_t)attrib.buffer + attrib.instanceStride); | |
| attrib.robustnessSize -= attrib.instanceStride; | |
| } | |
| } | |
| } | |
| GraphicsState::GraphicsState(const Device *device, const VkGraphicsPipelineCreateInfo *pCreateInfo, | |
| const PipelineLayout *layout, bool robustBufferAccess) | |
| : pipelineLayout(layout) | |
| , robustBufferAccess(robustBufferAccess) | |
| { | |
| if((pCreateInfo->flags & | |
| ~(VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT | | |
| VK_PIPELINE_CREATE_DERIVATIVE_BIT | | |
| VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT)) != 0) | |
| { | |
| UNSUPPORTED("pCreateInfo->flags %d", int(pCreateInfo->flags)); | |
| } | |
| if(pCreateInfo->pDynamicState) | |
| { | |
| if(pCreateInfo->pDynamicState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("pCreateInfo->pDynamicState->flags %d", int(pCreateInfo->pDynamicState->flags)); | |
| } | |
| for(uint32_t i = 0; i < pCreateInfo->pDynamicState->dynamicStateCount; i++) | |
| { | |
| VkDynamicState dynamicState = pCreateInfo->pDynamicState->pDynamicStates[i]; | |
| switch(dynamicState) | |
| { | |
| case VK_DYNAMIC_STATE_VIEWPORT: | |
| case VK_DYNAMIC_STATE_SCISSOR: | |
| case VK_DYNAMIC_STATE_LINE_WIDTH: | |
| case VK_DYNAMIC_STATE_DEPTH_BIAS: | |
| case VK_DYNAMIC_STATE_BLEND_CONSTANTS: | |
| case VK_DYNAMIC_STATE_DEPTH_BOUNDS: | |
| case VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK: | |
| case VK_DYNAMIC_STATE_STENCIL_WRITE_MASK: | |
| case VK_DYNAMIC_STATE_STENCIL_REFERENCE: | |
| ASSERT(dynamicState < (sizeof(dynamicStateFlags) * 8)); | |
| dynamicStateFlags |= (1 << dynamicState); | |
| break; | |
| default: | |
| UNSUPPORTED("VkDynamicState %d", int(dynamicState)); | |
| } | |
| } | |
| } | |
| const VkPipelineVertexInputStateCreateInfo *vertexInputState = pCreateInfo->pVertexInputState; | |
| if(vertexInputState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("vertexInputState->flags"); | |
| } | |
| const VkPipelineInputAssemblyStateCreateInfo *inputAssemblyState = pCreateInfo->pInputAssemblyState; | |
| if(inputAssemblyState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("pCreateInfo->pInputAssemblyState->flags %d", int(pCreateInfo->pInputAssemblyState->flags)); | |
| } | |
| primitiveRestartEnable = (inputAssemblyState->primitiveRestartEnable != VK_FALSE); | |
| topology = inputAssemblyState->topology; | |
| const VkPipelineRasterizationStateCreateInfo *rasterizationState = pCreateInfo->pRasterizationState; | |
| if(rasterizationState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("pCreateInfo->pRasterizationState->flags %d", int(pCreateInfo->pRasterizationState->flags)); | |
| } | |
| if(rasterizationState->depthClampEnable != VK_FALSE) | |
| { | |
| UNSUPPORTED("VkPhysicalDeviceFeatures::depthClamp"); | |
| } | |
| rasterizerDiscard = (rasterizationState->rasterizerDiscardEnable != VK_FALSE); | |
| cullMode = rasterizationState->cullMode; | |
| frontFace = rasterizationState->frontFace; | |
| polygonMode = rasterizationState->polygonMode; | |
| constantDepthBias = (rasterizationState->depthBiasEnable != VK_FALSE) ? rasterizationState->depthBiasConstantFactor : 0.0f; | |
| slopeDepthBias = (rasterizationState->depthBiasEnable != VK_FALSE) ? rasterizationState->depthBiasSlopeFactor : 0.0f; | |
| depthBiasClamp = (rasterizationState->depthBiasEnable != VK_FALSE) ? rasterizationState->depthBiasClamp : 0.0f; | |
| depthRangeUnrestricted = device->hasExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); | |
| // From the Vulkan spec for vkCmdSetDepthBias: | |
| // The bias value O for a polygon is: | |
| // O = dbclamp(...) | |
| // where dbclamp(x) = | |
| // * x depthBiasClamp = 0 or NaN | |
| // * min(x, depthBiasClamp) depthBiasClamp > 0 | |
| // * max(x, depthBiasClamp) depthBiasClamp < 0 | |
| // So it should be safe to resolve NaNs to 0.0f. | |
| if(std::isnan(depthBiasClamp)) | |
| { | |
| depthBiasClamp = 0.0f; | |
| } | |
| lineWidth = rasterizationState->lineWidth; | |
| const VkBaseInStructure *extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(rasterizationState->pNext); | |
| while(extensionCreateInfo) | |
| { | |
| // Casting to a long since some structures, such as | |
| // VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT | |
| // are not enumerated in the official Vulkan header | |
| switch((long)(extensionCreateInfo->sType)) | |
| { | |
| case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT: | |
| { | |
| const VkPipelineRasterizationLineStateCreateInfoEXT *lineStateCreateInfo = reinterpret_cast<const VkPipelineRasterizationLineStateCreateInfoEXT *>(extensionCreateInfo); | |
| lineRasterizationMode = lineStateCreateInfo->lineRasterizationMode; | |
| } | |
| break; | |
| case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT: | |
| { | |
| const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *provokingVertexModeCreateInfo = | |
| reinterpret_cast<const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *>(extensionCreateInfo); | |
| provokingVertexMode = provokingVertexModeCreateInfo->provokingVertexMode; | |
| } | |
| break; | |
| default: | |
| WARN("pCreateInfo->pRasterizationState->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str()); | |
| break; | |
| } | |
| extensionCreateInfo = extensionCreateInfo->pNext; | |
| } | |
| // The sample count affects the batch size, so it needs initialization even if rasterization is disabled. | |
| // TODO(b/147812380): Eliminate the dependency between multisampling and batch size. | |
| sampleCount = 1; | |
| // Only access rasterization state if rasterization is not disabled. | |
| if(rasterizationState->rasterizerDiscardEnable == VK_FALSE) | |
| { | |
| const VkPipelineViewportStateCreateInfo *viewportState = pCreateInfo->pViewportState; | |
| const VkPipelineMultisampleStateCreateInfo *multisampleState = pCreateInfo->pMultisampleState; | |
| const VkPipelineDepthStencilStateCreateInfo *depthStencilState = pCreateInfo->pDepthStencilState; | |
| const VkPipelineColorBlendStateCreateInfo *colorBlendState = pCreateInfo->pColorBlendState; | |
| if(viewportState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("pCreateInfo->pViewportState->flags %d", int(pCreateInfo->pViewportState->flags)); | |
| } | |
| if((viewportState->viewportCount != 1) || | |
| (viewportState->scissorCount != 1)) | |
| { | |
| UNSUPPORTED("VkPhysicalDeviceFeatures::multiViewport"); | |
| } | |
| if(!hasDynamicState(VK_DYNAMIC_STATE_SCISSOR)) | |
| { | |
| scissor = viewportState->pScissors[0]; | |
| } | |
| if(!hasDynamicState(VK_DYNAMIC_STATE_VIEWPORT)) | |
| { | |
| viewport = viewportState->pViewports[0]; | |
| } | |
| if(multisampleState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("pCreateInfo->pMultisampleState->flags %d", int(pCreateInfo->pMultisampleState->flags)); | |
| } | |
| sampleShadingEnable = (multisampleState->sampleShadingEnable != VK_FALSE); | |
| if(sampleShadingEnable) | |
| { | |
| minSampleShading = multisampleState->minSampleShading; | |
| UNSUPPORTED("VkPhysicalDeviceFeatures::sampleRateShading"); | |
| } | |
| if(multisampleState->alphaToOneEnable != VK_FALSE) | |
| { | |
| UNSUPPORTED("VkPhysicalDeviceFeatures::alphaToOne"); | |
| } | |
| switch(multisampleState->rasterizationSamples) | |
| { | |
| case VK_SAMPLE_COUNT_1_BIT: | |
| sampleCount = 1; | |
| break; | |
| case VK_SAMPLE_COUNT_4_BIT: | |
| sampleCount = 4; | |
| break; | |
| default: | |
| UNSUPPORTED("Unsupported sample count"); | |
| } | |
| VkSampleMask sampleMask; | |
| if(multisampleState->pSampleMask) | |
| { | |
| sampleMask = multisampleState->pSampleMask[0]; | |
| } | |
| else // "If pSampleMask is NULL, it is treated as if the mask has all bits set to 1." | |
| { | |
| sampleMask = ~0; | |
| } | |
| alphaToCoverage = (multisampleState->alphaToCoverageEnable != VK_FALSE); | |
| multiSampleMask = sampleMask & ((unsigned)0xFFFFFFFF >> (32 - sampleCount)); | |
| const vk::RenderPass *renderPass = vk::Cast(pCreateInfo->renderPass); | |
| const VkSubpassDescription &subpass = renderPass->getSubpass(pCreateInfo->subpass); | |
| // Ignore pDepthStencilState when "the subpass of the render pass the pipeline is created against does not use a depth/stencil attachment" | |
| if(subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) | |
| { | |
| if(depthStencilState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("pCreateInfo->pDepthStencilState->flags %d", int(pCreateInfo->pDepthStencilState->flags)); | |
| } | |
| if(depthStencilState->depthBoundsTestEnable != VK_FALSE) | |
| { | |
| UNSUPPORTED("VkPhysicalDeviceFeatures::depthBounds"); | |
| } | |
| depthBoundsTestEnable = (depthStencilState->depthBoundsTestEnable != VK_FALSE); | |
| depthBufferEnable = (depthStencilState->depthTestEnable != VK_FALSE); | |
| depthWriteEnable = (depthStencilState->depthWriteEnable != VK_FALSE); | |
| depthCompareMode = depthStencilState->depthCompareOp; | |
| stencilEnable = (depthStencilState->stencilTestEnable != VK_FALSE); | |
| if(stencilEnable) | |
| { | |
| frontStencil = depthStencilState->front; | |
| backStencil = depthStencilState->back; | |
| } | |
| } | |
| bool colorAttachmentUsed = false; | |
| for(uint32_t i = 0; i < subpass.colorAttachmentCount; i++) | |
| { | |
| if(subpass.pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) | |
| { | |
| colorAttachmentUsed = true; | |
| break; | |
| } | |
| } | |
| // Ignore pColorBlendState when "the subpass of the render pass the pipeline is created against does not use any color attachments" | |
| if(colorAttachmentUsed) | |
| { | |
| if(colorBlendState->flags != 0) | |
| { | |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" | |
| UNSUPPORTED("pCreateInfo->pColorBlendState->flags %d", int(pCreateInfo->pColorBlendState->flags)); | |
| } | |
| if(colorBlendState->logicOpEnable != VK_FALSE) | |
| { | |
| UNSUPPORTED("VkPhysicalDeviceFeatures::logicOp"); | |
| } | |
| if(!hasDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS)) | |
| { | |
| blendConstants.x = colorBlendState->blendConstants[0]; | |
| blendConstants.y = colorBlendState->blendConstants[1]; | |
| blendConstants.z = colorBlendState->blendConstants[2]; | |
| blendConstants.w = colorBlendState->blendConstants[3]; | |
| } | |
| ASSERT(colorBlendState->attachmentCount <= sw::RENDERTARGETS); | |
| for(auto i = 0u; i < colorBlendState->attachmentCount; i++) | |
| { | |
| const VkPipelineColorBlendAttachmentState &attachment = colorBlendState->pAttachments[i]; | |
| colorWriteMask[i] = attachment.colorWriteMask; | |
| blendState[i] = { (attachment.blendEnable != VK_FALSE), | |
| attachment.srcColorBlendFactor, attachment.dstColorBlendFactor, attachment.colorBlendOp, | |
| attachment.srcAlphaBlendFactor, attachment.dstAlphaBlendFactor, attachment.alphaBlendOp }; | |
| } | |
| } | |
| } | |
| } | |
| bool GraphicsState::isDrawPoint(bool polygonModeAware) const | |
| { | |
| switch(topology) | |
| { | |
| case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: | |
| return true; | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: | |
| return false; | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: | |
| return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_POINT) : false; | |
| default: | |
| UNSUPPORTED("topology %d", int(topology)); | |
| } | |
| return false; | |
| } | |
| bool GraphicsState::isDrawLine(bool polygonModeAware) const | |
| { | |
| switch(topology) | |
| { | |
| case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: | |
| return false; | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: | |
| return true; | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: | |
| return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_LINE) : false; | |
| default: | |
| UNSUPPORTED("topology %d", int(topology)); | |
| } | |
| return false; | |
| } | |
| bool GraphicsState::isDrawTriangle(bool polygonModeAware) const | |
| { | |
| switch(topology) | |
| { | |
| case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: | |
| case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: | |
| return false; | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: | |
| case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: | |
| return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_FILL) : true; | |
| default: | |
| UNSUPPORTED("topology %d", int(topology)); | |
| } | |
| return false; | |
| } | |
| bool GraphicsState::depthWriteActive(const Attachments &attachments) const | |
| { | |
| if(!depthBufferActive(attachments)) return false; | |
| return depthWriteEnable; | |
| } | |
| bool GraphicsState::depthBufferActive(const Attachments &attachments) const | |
| { | |
| return attachments.depthBuffer && depthBufferEnable; | |
| } | |
| bool GraphicsState::stencilActive(const Attachments &attachments) const | |
| { | |
| return attachments.stencilBuffer && stencilEnable; | |
| } | |
| const GraphicsState GraphicsState::combineStates(const DynamicState &dynamicState) const | |
| { | |
| GraphicsState combinedState = *this; | |
| // Apply either pipeline state or dynamic state | |
| if(hasDynamicState(VK_DYNAMIC_STATE_SCISSOR)) | |
| { | |
| combinedState.scissor = dynamicState.scissor; | |
| } | |
| if(hasDynamicState(VK_DYNAMIC_STATE_VIEWPORT)) | |
| { | |
| combinedState.viewport = dynamicState.viewport; | |
| } | |
| if(hasDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS)) | |
| { | |
| combinedState.blendConstants = dynamicState.blendConstants; | |
| } | |
| if(hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS)) | |
| { | |
| combinedState.constantDepthBias = dynamicState.depthBiasConstantFactor; | |
| combinedState.slopeDepthBias = dynamicState.depthBiasSlopeFactor; | |
| combinedState.depthBiasClamp = dynamicState.depthBiasClamp; | |
| } | |
| if(hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BOUNDS) && depthBoundsTestEnable) | |
| { | |
| // Unless the VK_EXT_depth_range_unrestricted extension is enabled, | |
| // minDepthBounds and maxDepthBounds must be between 0.0 and 1.0, inclusive | |
| ASSERT(dynamicState.minDepthBounds >= 0.0f && dynamicState.minDepthBounds <= 1.0f); | |
| ASSERT(dynamicState.maxDepthBounds >= 0.0f && dynamicState.maxDepthBounds <= 1.0f); | |
| UNSUPPORTED("VkPhysicalDeviceFeatures::depthBounds"); | |
| } | |
| if(hasDynamicState(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK) && stencilEnable) | |
| { | |
| combinedState.frontStencil.compareMask = dynamicState.compareMask[0]; | |
| combinedState.backStencil.compareMask = dynamicState.compareMask[1]; | |
| } | |
| if(hasDynamicState(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK) && stencilEnable) | |
| { | |
| combinedState.frontStencil.writeMask = dynamicState.writeMask[0]; | |
| combinedState.backStencil.writeMask = dynamicState.writeMask[1]; | |
| } | |
| if(hasDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE) && stencilEnable) | |
| { | |
| combinedState.frontStencil.reference = dynamicState.reference[0]; | |
| combinedState.backStencil.reference = dynamicState.reference[1]; | |
| } | |
| return combinedState; | |
| } | |
| BlendState GraphicsState::getBlendState(int index, const Attachments &attachments, bool fragmentContainsKill) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| BlendState activeBlendState; | |
| activeBlendState.alphaBlendEnable = alphaBlendActive(index, attachments, fragmentContainsKill); | |
| activeBlendState.sourceBlendFactor = sourceBlendFactor(index); | |
| activeBlendState.destBlendFactor = destBlendFactor(index); | |
| activeBlendState.blendOperation = blendOperation(index, attachments); | |
| activeBlendState.sourceBlendFactorAlpha = sourceBlendFactorAlpha(index); | |
| activeBlendState.destBlendFactorAlpha = destBlendFactorAlpha(index); | |
| activeBlendState.blendOperationAlpha = blendOperationAlpha(index, attachments); | |
| return activeBlendState; | |
| } | |
| bool GraphicsState::alphaBlendActive(int index, const Attachments &attachments, bool fragmentContainsKill) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| if(!blendState[index].alphaBlendEnable) | |
| { | |
| return false; | |
| } | |
| if(!(colorWriteActive(attachments) || fragmentContainsKill)) | |
| { | |
| return false; | |
| } | |
| bool colorBlend = !(blendOperation(index, attachments) == VK_BLEND_OP_SRC_EXT && | |
| sourceBlendFactor(index) == VK_BLEND_FACTOR_ONE); | |
| bool alphaBlend = !(blendOperationAlpha(index, attachments) == VK_BLEND_OP_SRC_EXT && | |
| sourceBlendFactorAlpha(index) == VK_BLEND_FACTOR_ONE); | |
| return colorBlend || alphaBlend; | |
| } | |
| VkBlendFactor GraphicsState::sourceBlendFactor(int index) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| if(!blendState[index].alphaBlendEnable) return VK_BLEND_FACTOR_ONE; | |
| switch(blendState[index].blendOperation) | |
| { | |
| case VK_BLEND_OP_ADD: | |
| case VK_BLEND_OP_SUBTRACT: | |
| case VK_BLEND_OP_REVERSE_SUBTRACT: | |
| return blendState[index].sourceBlendFactor; | |
| case VK_BLEND_OP_MIN: | |
| return VK_BLEND_FACTOR_ONE; | |
| case VK_BLEND_OP_MAX: | |
| return VK_BLEND_FACTOR_ONE; | |
| default: | |
| ASSERT(false); | |
| } | |
| return blendState[index].sourceBlendFactor; | |
| } | |
| VkBlendFactor GraphicsState::destBlendFactor(int index) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| if(!blendState[index].alphaBlendEnable) return VK_BLEND_FACTOR_ONE; | |
| switch(blendState[index].blendOperation) | |
| { | |
| case VK_BLEND_OP_ADD: | |
| case VK_BLEND_OP_SUBTRACT: | |
| case VK_BLEND_OP_REVERSE_SUBTRACT: | |
| return blendState[index].destBlendFactor; | |
| case VK_BLEND_OP_MIN: | |
| return VK_BLEND_FACTOR_ONE; | |
| case VK_BLEND_OP_MAX: | |
| return VK_BLEND_FACTOR_ONE; | |
| default: | |
| ASSERT(false); | |
| } | |
| return blendState[index].destBlendFactor; | |
| } | |
| VkBlendOp GraphicsState::blendOperation(int index, const Attachments &attachments) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| if(!blendState[index].alphaBlendEnable) return VK_BLEND_OP_SRC_EXT; | |
| switch(blendState[index].blendOperation) | |
| { | |
| case VK_BLEND_OP_ADD: | |
| if(sourceBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_DST_EXT; | |
| } | |
| } | |
| else if(sourceBlendFactor(index) == VK_BLEND_FACTOR_ONE) | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_ADD; | |
| } | |
| } | |
| else | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_ADD; | |
| } | |
| } | |
| case VK_BLEND_OP_SUBTRACT: | |
| if(sourceBlendFactor(index) == VK_BLEND_FACTOR_ZERO && attachments.isColorClamped(index)) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero | |
| } | |
| else if(sourceBlendFactor(index) == VK_BLEND_FACTOR_ONE) | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_SUBTRACT; | |
| } | |
| } | |
| else | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_SUBTRACT; | |
| } | |
| } | |
| case VK_BLEND_OP_REVERSE_SUBTRACT: | |
| if(sourceBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_DST_EXT; | |
| } | |
| } | |
| else if(sourceBlendFactor(index) == VK_BLEND_FACTOR_ONE) | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO && attachments.isColorClamped(index)) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_REVERSE_SUBTRACT; | |
| } | |
| } | |
| else | |
| { | |
| if(destBlendFactor(index) == VK_BLEND_FACTOR_ZERO && attachments.isColorClamped(index)) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_REVERSE_SUBTRACT; | |
| } | |
| } | |
| case VK_BLEND_OP_MIN: | |
| return VK_BLEND_OP_MIN; | |
| case VK_BLEND_OP_MAX: | |
| return VK_BLEND_OP_MAX; | |
| default: | |
| ASSERT(false); | |
| } | |
| return blendState[index].blendOperation; | |
| } | |
| VkBlendFactor GraphicsState::sourceBlendFactorAlpha(int index) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| switch(blendState[index].blendOperationAlpha) | |
| { | |
| case VK_BLEND_OP_ADD: | |
| case VK_BLEND_OP_SUBTRACT: | |
| case VK_BLEND_OP_REVERSE_SUBTRACT: | |
| return blendState[index].sourceBlendFactorAlpha; | |
| case VK_BLEND_OP_MIN: | |
| return VK_BLEND_FACTOR_ONE; | |
| case VK_BLEND_OP_MAX: | |
| return VK_BLEND_FACTOR_ONE; | |
| default: | |
| ASSERT(false); | |
| } | |
| return blendState[index].sourceBlendFactorAlpha; | |
| } | |
| VkBlendFactor GraphicsState::destBlendFactorAlpha(int index) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| switch(blendState[index].blendOperationAlpha) | |
| { | |
| case VK_BLEND_OP_ADD: | |
| case VK_BLEND_OP_SUBTRACT: | |
| case VK_BLEND_OP_REVERSE_SUBTRACT: | |
| return blendState[index].destBlendFactorAlpha; | |
| case VK_BLEND_OP_MIN: | |
| return VK_BLEND_FACTOR_ONE; | |
| case VK_BLEND_OP_MAX: | |
| return VK_BLEND_FACTOR_ONE; | |
| default: | |
| ASSERT(false); | |
| } | |
| return blendState[index].destBlendFactorAlpha; | |
| } | |
| VkBlendOp GraphicsState::blendOperationAlpha(int index, const Attachments &attachments) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| switch(blendState[index].blendOperationAlpha) | |
| { | |
| case VK_BLEND_OP_ADD: | |
| if(sourceBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_DST_EXT; | |
| } | |
| } | |
| else if(sourceBlendFactorAlpha(index) == VK_BLEND_FACTOR_ONE) | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_ADD; | |
| } | |
| } | |
| else | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_ADD; | |
| } | |
| } | |
| case VK_BLEND_OP_SUBTRACT: | |
| if(sourceBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO && attachments.isColorClamped(index)) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero | |
| } | |
| else if(sourceBlendFactorAlpha(index) == VK_BLEND_FACTOR_ONE) | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_SUBTRACT; | |
| } | |
| } | |
| else | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_SRC_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_SUBTRACT; | |
| } | |
| } | |
| case VK_BLEND_OP_REVERSE_SUBTRACT: | |
| if(sourceBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_DST_EXT; | |
| } | |
| } | |
| else if(sourceBlendFactorAlpha(index) == VK_BLEND_FACTOR_ONE) | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO && attachments.isColorClamped(index)) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_REVERSE_SUBTRACT; | |
| } | |
| } | |
| else | |
| { | |
| if(destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ZERO && attachments.isColorClamped(index)) | |
| { | |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero | |
| } | |
| else | |
| { | |
| return VK_BLEND_OP_REVERSE_SUBTRACT; | |
| } | |
| } | |
| case VK_BLEND_OP_MIN: | |
| return VK_BLEND_OP_MIN; | |
| case VK_BLEND_OP_MAX: | |
| return VK_BLEND_OP_MAX; | |
| default: | |
| ASSERT(false); | |
| } | |
| return blendState[index].blendOperationAlpha; | |
| } | |
| bool GraphicsState::colorWriteActive(const Attachments &attachments) const | |
| { | |
| for(int i = 0; i < sw::RENDERTARGETS; i++) | |
| { | |
| if(colorWriteActive(i, attachments)) | |
| { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| int GraphicsState::colorWriteActive(int index, const Attachments &attachments) const | |
| { | |
| ASSERT((index >= 0) && (index < sw::RENDERTARGETS)); | |
| if(!attachments.renderTarget[index] || attachments.renderTarget[index]->getFormat() == VK_FORMAT_UNDEFINED) | |
| { | |
| return 0; | |
| } | |
| if(blendOperation(index, attachments) == VK_BLEND_OP_DST_EXT && destBlendFactor(index) == VK_BLEND_FACTOR_ONE && | |
| (blendOperationAlpha(index, attachments) == VK_BLEND_OP_DST_EXT && destBlendFactorAlpha(index) == VK_BLEND_FACTOR_ONE)) | |
| { | |
| return 0; | |
| } | |
| return colorWriteMask[index]; | |
| } | |
| } // namespace vk |