| // 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; |
| } |
| |
| 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)); |
| } |
| |
| depthBoundsTestEnable = (depthStencilState->depthBoundsTestEnable != VK_FALSE); |
| minDepthBounds = depthStencilState->minDepthBounds; |
| maxDepthBounds = depthStencilState->maxDepthBounds; |
| |
| 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; |
| } |
| |
| bool GraphicsState::depthBoundsTestActive() const |
| { |
| return depthBoundsTestEnable; |
| } |
| |
| 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); |
| |
| combinedState.minDepthBounds = dynamicState.minDepthBounds; |
| combinedState.maxDepthBounds = dynamicState.maxDepthBounds; |
| } |
| |
| 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 |