| // 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/VkPipeline.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 }); |
| } |
| } |
| } |
| |
| vk::DynamicStateFlags ParseDynamicStateFlags(const VkPipelineDynamicStateCreateInfo *dynamicStateCreateInfo) |
| { |
| vk::DynamicStateFlags dynamicStateFlags = {}; |
| |
| if(dynamicStateCreateInfo == nullptr) |
| { |
| return dynamicStateFlags; |
| } |
| |
| if(dynamicStateCreateInfo->flags != 0) |
| { |
| // Vulkan 1.3: "flags is reserved for future use." "flags must be 0" |
| UNSUPPORTED("dynamicStateCreateInfo->flags 0x%08X", int(dynamicStateCreateInfo->flags)); |
| } |
| |
| for(uint32_t i = 0; i < dynamicStateCreateInfo->dynamicStateCount; i++) |
| { |
| VkDynamicState dynamicState = dynamicStateCreateInfo->pDynamicStates[i]; |
| switch(dynamicState) |
| { |
| // Vertex input interface: |
| case VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE: |
| dynamicStateFlags.vertexInputInterface.dynamicPrimitiveRestartEnable = true; |
| break; |
| case VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY: |
| dynamicStateFlags.vertexInputInterface.dynamicPrimitiveTopology = true; |
| break; |
| case VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE: |
| dynamicStateFlags.vertexInputInterface.dynamicVertexInputBindingStride = true; |
| break; |
| |
| // Pre-rasterization: |
| case VK_DYNAMIC_STATE_LINE_WIDTH: |
| dynamicStateFlags.preRasterization.dynamicLineWidth = true; |
| break; |
| case VK_DYNAMIC_STATE_DEPTH_BIAS: |
| dynamicStateFlags.preRasterization.dynamicDepthBias = true; |
| break; |
| case VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE: |
| dynamicStateFlags.preRasterization.dynamicDepthBiasEnable = true; |
| break; |
| case VK_DYNAMIC_STATE_CULL_MODE: |
| dynamicStateFlags.preRasterization.dynamicCullMode = true; |
| break; |
| case VK_DYNAMIC_STATE_FRONT_FACE: |
| dynamicStateFlags.preRasterization.dynamicFrontFace = true; |
| break; |
| case VK_DYNAMIC_STATE_VIEWPORT: |
| dynamicStateFlags.preRasterization.dynamicViewport = true; |
| break; |
| case VK_DYNAMIC_STATE_SCISSOR: |
| dynamicStateFlags.preRasterization.dynamicScissor = true; |
| break; |
| case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT: |
| dynamicStateFlags.preRasterization.dynamicViewportWithCount = true; |
| break; |
| case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT: |
| dynamicStateFlags.preRasterization.dynamicScissorWithCount = true; |
| break; |
| case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE: |
| dynamicStateFlags.preRasterization.dynamicRasterizerDiscardEnable = true; |
| break; |
| |
| // Fragment: |
| case VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE: |
| dynamicStateFlags.fragment.dynamicDepthTestEnable = true; |
| break; |
| case VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE: |
| dynamicStateFlags.fragment.dynamicDepthWriteEnable = true; |
| break; |
| case VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE: |
| dynamicStateFlags.fragment.dynamicDepthBoundsTestEnable = true; |
| break; |
| case VK_DYNAMIC_STATE_DEPTH_BOUNDS: |
| dynamicStateFlags.fragment.dynamicDepthBounds = true; |
| break; |
| case VK_DYNAMIC_STATE_DEPTH_COMPARE_OP: |
| dynamicStateFlags.fragment.dynamicDepthCompareOp = true; |
| break; |
| case VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE: |
| dynamicStateFlags.fragment.dynamicStencilTestEnable = true; |
| break; |
| case VK_DYNAMIC_STATE_STENCIL_OP: |
| dynamicStateFlags.fragment.dynamicStencilOp = true; |
| break; |
| case VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK: |
| dynamicStateFlags.fragment.dynamicStencilCompareMask = true; |
| break; |
| case VK_DYNAMIC_STATE_STENCIL_WRITE_MASK: |
| dynamicStateFlags.fragment.dynamicStencilWriteMask = true; |
| break; |
| case VK_DYNAMIC_STATE_STENCIL_REFERENCE: |
| dynamicStateFlags.fragment.dynamicStencilReference = true; |
| break; |
| |
| // Fragment output interface: |
| case VK_DYNAMIC_STATE_BLEND_CONSTANTS: |
| dynamicStateFlags.fragmentOutputInterface.dynamicBlendConstants = true; |
| break; |
| |
| default: |
| UNSUPPORTED("VkDynamicState %d", int(dynamicState)); |
| } |
| } |
| |
| return dynamicStateFlags; |
| } |
| } // 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 }); |
| } |
| } |
| |
| VkFormat Attachments::colorFormat(int index) const |
| { |
| ASSERT((index >= 0) && (index < sw::MAX_COLOR_BUFFERS)); |
| |
| if(colorBuffer[index]) |
| { |
| return colorBuffer[index]->getFormat(); |
| } |
| else |
| { |
| return VK_FORMAT_UNDEFINED; |
| } |
| } |
| |
| VkFormat Attachments::depthFormat() const |
| { |
| if(depthBuffer) |
| { |
| return depthBuffer->getFormat(); |
| } |
| else |
| { |
| return VK_FORMAT_UNDEFINED; |
| } |
| } |
| |
| void Inputs::initialize(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]; |
| VkVertexInputRate inputRates[MAX_VERTEX_INPUT_BINDINGS]; |
| for(uint32_t i = 0; i < vertexInputState->vertexBindingDescriptionCount; i++) |
| { |
| const auto &desc = vertexInputState->pVertexBindingDescriptions[i]; |
| inputRates[desc.binding] = desc.inputRate; |
| 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++) |
| { |
| const auto &desc = vertexInputState->pVertexAttributeDescriptions[i]; |
| sw::Stream &input = stream[desc.location]; |
| input.format = desc.format; |
| input.offset = desc.offset; |
| input.binding = desc.binding; |
| input.inputRate = inputRates[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, bool dynamicInstanceStride) |
| { |
| 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 + |
| getInstanceStride(i, dynamicInstanceStride) * 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(bool dynamicInstanceStride) |
| { |
| for(uint32_t i = 0; i < vk::MAX_VERTEX_INPUT_BINDINGS; i++) |
| { |
| auto &attrib = stream[i]; |
| |
| VkDeviceSize instanceStride = getInstanceStride(i, dynamicInstanceStride); |
| if((attrib.format != VK_FORMAT_UNDEFINED) && instanceStride && (instanceStride < attrib.robustnessSize)) |
| { |
| // Under the casts: attrib.buffer += instanceStride |
| attrib.buffer = (const void *)((uintptr_t)attrib.buffer + instanceStride); |
| attrib.robustnessSize -= instanceStride; |
| } |
| } |
| } |
| |
| VkDeviceSize Inputs::getVertexStride(uint32_t i, bool dynamicVertexStride) const |
| { |
| auto &attrib = stream[i]; |
| if(attrib.format != VK_FORMAT_UNDEFINED && attrib.inputRate == VK_VERTEX_INPUT_RATE_VERTEX) |
| { |
| if(dynamicVertexStride) |
| { |
| return vertexInputBindings[attrib.binding].stride; |
| } |
| else |
| { |
| return attrib.vertexStride; |
| } |
| } |
| |
| return 0; |
| } |
| |
| VkDeviceSize Inputs::getInstanceStride(uint32_t i, bool dynamicInstanceStride) const |
| { |
| auto &attrib = stream[i]; |
| if(attrib.format != VK_FORMAT_UNDEFINED && attrib.inputRate == VK_VERTEX_INPUT_RATE_INSTANCE) |
| { |
| if(dynamicInstanceStride) |
| { |
| return vertexInputBindings[attrib.binding].stride; |
| } |
| else |
| { |
| return attrib.instanceStride; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void MultisampleState::set(const VkPipelineMultisampleStateCreateInfo *multisampleState) |
| { |
| if(multisampleState->flags != 0) |
| { |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" |
| UNSUPPORTED("pCreateInfo->pMultisampleState->flags 0x%08X", int(multisampleState->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)); |
| } |
| |
| void VertexInputInterfaceState::initialize(const VkPipelineVertexInputStateCreateInfo *vertexInputState, |
| const VkPipelineInputAssemblyStateCreateInfo *inputAssemblyState, |
| const DynamicStateFlags &allDynamicStateFlags) |
| { |
| dynamicStateFlags = allDynamicStateFlags.vertexInputInterface; |
| |
| if(vertexInputState->flags != 0) |
| { |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" |
| UNSUPPORTED("vertexInputState->flags"); |
| } |
| |
| if(inputAssemblyState->flags != 0) |
| { |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" |
| UNSUPPORTED("pCreateInfo->pInputAssemblyState->flags 0x%08X", int(inputAssemblyState->flags)); |
| } |
| |
| primitiveRestartEnable = (inputAssemblyState->primitiveRestartEnable != VK_FALSE); |
| topology = inputAssemblyState->topology; |
| } |
| |
| void VertexInputInterfaceState::applyState(const DynamicState &dynamicState) |
| { |
| if(dynamicStateFlags.dynamicPrimitiveRestartEnable) |
| { |
| primitiveRestartEnable = dynamicState.primitiveRestartEnable; |
| } |
| |
| if(dynamicStateFlags.dynamicPrimitiveTopology) |
| { |
| topology = dynamicState.primitiveTopology; |
| } |
| } |
| |
| bool VertexInputInterfaceState::isDrawPoint(bool polygonModeAware, VkPolygonMode polygonMode) 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 VertexInputInterfaceState::isDrawLine(bool polygonModeAware, VkPolygonMode polygonMode) 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 VertexInputInterfaceState::isDrawTriangle(bool polygonModeAware, VkPolygonMode polygonMode) 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; |
| } |
| |
| void PreRasterizationState::initialize(const vk::Device *device, |
| const PipelineLayout *layout, |
| const VkPipelineViewportStateCreateInfo *viewportState, |
| const VkPipelineRasterizationStateCreateInfo *rasterizationState, |
| const vk::RenderPass *renderPass, uint32_t subpassIndex, |
| const VkPipelineRenderingCreateInfo *rendering, |
| const DynamicStateFlags &allDynamicStateFlags) |
| { |
| pipelineLayout = layout; |
| dynamicStateFlags = allDynamicStateFlags.preRasterization; |
| |
| if(rasterizationState->flags != 0) |
| { |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" |
| UNSUPPORTED("pCreateInfo->pRasterizationState->flags 0x%08X", int(rasterizationState->flags)); |
| } |
| |
| rasterizerDiscard = rasterizationState->rasterizerDiscardEnable != VK_FALSE; |
| cullMode = rasterizationState->cullMode; |
| frontFace = rasterizationState->frontFace; |
| polygonMode = rasterizationState->polygonMode; |
| depthBiasEnable = rasterizationState->depthBiasEnable; |
| constantDepthBias = rasterizationState->depthBiasConstantFactor; |
| slopeDepthBias = rasterizationState->depthBiasSlopeFactor; |
| depthBiasClamp = rasterizationState->depthBiasClamp; |
| depthRangeUnrestricted = device->hasExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); |
| depthClampEnable = rasterizationState->depthClampEnable != VK_FALSE; |
| depthClipEnable = !depthClampEnable; |
| |
| // 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; |
| } |
| |
| if(!dynamicStateFlags.dynamicLineWidth) |
| { |
| lineWidth = rasterizationState->lineWidth; |
| } |
| |
| const VkBaseInStructure *extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(rasterizationState->pNext); |
| while(extensionCreateInfo) |
| { |
| switch(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; |
| case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT: |
| { |
| const auto *depthClipInfo = reinterpret_cast<const VkPipelineRasterizationDepthClipStateCreateInfoEXT *>(extensionCreateInfo); |
| // Reserved for future use. |
| ASSERT(depthClipInfo->flags == 0); |
| depthClipEnable = depthClipInfo->depthClipEnable != VK_FALSE; |
| } |
| break; |
| case VK_STRUCTURE_TYPE_APPLICATION_INFO: |
| // SwiftShader doesn't interact with application info, but dEQP includes it |
| break; |
| case VK_STRUCTURE_TYPE_MAX_ENUM: |
| // dEQP tests that this value is ignored. |
| break; |
| default: |
| UNSUPPORTED("pCreateInfo->pRasterizationState->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str()); |
| break; |
| } |
| |
| extensionCreateInfo = extensionCreateInfo->pNext; |
| } |
| |
| if(!rasterizerDiscard || dynamicStateFlags.dynamicRasterizerDiscardEnable) |
| { |
| extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(viewportState->pNext); |
| while(extensionCreateInfo != nullptr) |
| { |
| switch(extensionCreateInfo->sType) |
| { |
| case VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT: |
| { |
| const auto *depthClipControl = reinterpret_cast<const VkPipelineViewportDepthClipControlCreateInfoEXT *>(extensionCreateInfo); |
| depthClipNegativeOneToOne = depthClipControl->negativeOneToOne != VK_FALSE; |
| } |
| break; |
| case VK_STRUCTURE_TYPE_MAX_ENUM: |
| // dEQP passes this value expecting the driver to ignore it. |
| break; |
| default: |
| UNSUPPORTED("pCreateInfo->pViewportState->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str()); |
| break; |
| } |
| extensionCreateInfo = extensionCreateInfo->pNext; |
| } |
| |
| if(viewportState->flags != 0) |
| { |
| // Vulkan 1.2: "flags is reserved for future use." "flags must be 0" |
| UNSUPPORTED("pCreateInfo->pViewportState->flags 0x%08X", int(viewportState->flags)); |
| } |
| |
| if((viewportState->viewportCount > 1) || |
| (viewportState->scissorCount > 1)) |
| { |
| UNSUPPORTED("VkPhysicalDeviceFeatures::multiViewport"); |
| } |
| |
| if(!dynamicStateFlags.dynamicScissor && !dynamicStateFlags.dynamicScissorWithCount) |
| { |
| scissor = viewportState->pScissors[0]; |
| } |
| |
| if(!dynamicStateFlags.dynamicViewport && !dynamicStateFlags.dynamicViewportWithCount) |
| { |
| viewport = viewportState->pViewports[0]; |
| } |
| } |
| } |
| |
| void PreRasterizationState::applyState(const DynamicState &dynamicState) |
| { |
| if(dynamicStateFlags.dynamicLineWidth) |
| { |
| lineWidth = dynamicState.lineWidth; |
| } |
| |
| if(dynamicStateFlags.dynamicDepthBias) |
| { |
| constantDepthBias = dynamicState.depthBiasConstantFactor; |
| slopeDepthBias = dynamicState.depthBiasSlopeFactor; |
| depthBiasClamp = dynamicState.depthBiasClamp; |
| } |
| |
| if(dynamicStateFlags.dynamicDepthBiasEnable) |
| { |
| depthBiasEnable = dynamicState.depthBiasEnable; |
| } |
| |
| if(dynamicStateFlags.dynamicCullMode) |
| { |
| cullMode = dynamicState.cullMode; |
| } |
| |
| if(dynamicStateFlags.dynamicFrontFace) |
| { |
| frontFace = dynamicState.frontFace; |
| } |
| |
| if(dynamicStateFlags.dynamicViewport) |
| { |
| viewport = dynamicState.viewport; |
| } |
| |
| if(dynamicStateFlags.dynamicScissor) |
| { |
| scissor = dynamicState.scissor; |
| } |
| |
| if(dynamicStateFlags.dynamicViewportWithCount && dynamicState.viewportCount > 0) |
| { |
| viewport.width = static_cast<float>(dynamicState.viewports[0].extent.width); |
| viewport.height = static_cast<float>(dynamicState.viewports[0].extent.height); |
| viewport.x = static_cast<float>(dynamicState.viewports[0].offset.x); |
| viewport.y = static_cast<float>(dynamicState.viewports[0].offset.y); |
| } |
| |
| if(dynamicStateFlags.dynamicScissorWithCount && dynamicState.scissorCount > 0) |
| { |
| scissor = dynamicState.scissors[0]; |
| } |
| |
| if(dynamicStateFlags.dynamicRasterizerDiscardEnable) |
| { |
| rasterizerDiscard = dynamicState.rasterizerDiscardEnable; |
| } |
| } |
| |
| void FragmentState::initialize( |
| const PipelineLayout *layout, |
| const VkPipelineDepthStencilStateCreateInfo *depthStencilState, |
| const vk::RenderPass *renderPass, uint32_t subpassIndex, |
| const VkPipelineRenderingCreateInfo *rendering, |
| const DynamicStateFlags &allDynamicStateFlags) |
| { |
| pipelineLayout = layout; |
| dynamicStateFlags = allDynamicStateFlags.fragment; |
| |
| if(renderPass) |
| { |
| const VkSubpassDescription &subpass = renderPass->getSubpass(subpassIndex); |
| |
| // 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) |
| { |
| setDepthStencilState(depthStencilState); |
| } |
| } |
| else // No render pass |
| { |
| // When a pipeline is created without a VkRenderPass, if the VkPipelineRenderingCreateInfo structure |
| // is present in the pNext chain of VkGraphicsPipelineCreateInfo, it specifies the view mask and |
| // format of attachments used for rendering. If this structure is not specified, and the pipeline |
| // does not include a VkRenderPass, viewMask and colorAttachmentCount are 0, and |
| // depthAttachmentFormat and stencilAttachmentFormat are VK_FORMAT_UNDEFINED. If a graphics pipeline |
| // is created with a valid VkRenderPass, parameters of this structure are ignored. |
| |
| if(rendering) |
| { |
| if((rendering->depthAttachmentFormat != VK_FORMAT_UNDEFINED) || |
| (rendering->stencilAttachmentFormat != VK_FORMAT_UNDEFINED)) |
| { |
| // If renderPass is VK_NULL_HANDLE, the pipeline is being created with fragment |
| // shader state, and either of VkPipelineRenderingCreateInfo::depthAttachmentFormat |
| // or VkPipelineRenderingCreateInfo::stencilAttachmentFormat are not |
| // VK_FORMAT_UNDEFINED, pDepthStencilState must be a valid pointer to a valid |
| // VkPipelineDepthStencilStateCreateInfo structure |
| ASSERT(depthStencilState); |
| |
| setDepthStencilState(depthStencilState); |
| } |
| } |
| } |
| } |
| |
| void FragmentState::applyState(const DynamicState &dynamicState) |
| { |
| if(dynamicStateFlags.dynamicDepthTestEnable) |
| { |
| depthTestEnable = dynamicState.depthTestEnable; |
| } |
| |
| if(dynamicStateFlags.dynamicDepthWriteEnable) |
| { |
| depthWriteEnable = dynamicState.depthWriteEnable; |
| } |
| |
| if(dynamicStateFlags.dynamicDepthBoundsTestEnable) |
| { |
| depthBoundsTestEnable = dynamicState.depthBoundsTestEnable; |
| } |
| |
| if(dynamicStateFlags.dynamicDepthBounds && depthBoundsTestEnable) |
| { |
| minDepthBounds = dynamicState.minDepthBounds; |
| maxDepthBounds = dynamicState.maxDepthBounds; |
| } |
| |
| if(dynamicStateFlags.dynamicDepthCompareOp) |
| { |
| depthCompareMode = dynamicState.depthCompareOp; |
| } |
| |
| if(dynamicStateFlags.dynamicStencilTestEnable) |
| { |
| stencilEnable = dynamicState.stencilTestEnable; |
| } |
| |
| if(dynamicStateFlags.dynamicStencilOp && stencilEnable) |
| { |
| if(dynamicState.faceMask & VK_STENCIL_FACE_FRONT_BIT) |
| { |
| frontStencil.compareOp = dynamicState.frontStencil.compareOp; |
| frontStencil.depthFailOp = dynamicState.frontStencil.depthFailOp; |
| frontStencil.failOp = dynamicState.frontStencil.failOp; |
| frontStencil.passOp = dynamicState.frontStencil.passOp; |
| } |
| |
| if(dynamicState.faceMask & VK_STENCIL_FACE_BACK_BIT) |
| { |
| backStencil.compareOp = dynamicState.backStencil.compareOp; |
| backStencil.depthFailOp = dynamicState.backStencil.depthFailOp; |
| backStencil.failOp = dynamicState.backStencil.failOp; |
| backStencil.passOp = dynamicState.backStencil.passOp; |
| } |
| } |
| |
| if(dynamicStateFlags.dynamicStencilCompareMask && stencilEnable) |
| { |
| frontStencil.compareMask = dynamicState.frontStencil.compareMask; |
| backStencil.compareMask = dynamicState.backStencil.compareMask; |
| } |
| |
| if(dynamicStateFlags.dynamicStencilWriteMask && stencilEnable) |
| { |
| frontStencil.writeMask = dynamicState.frontStencil.writeMask; |
| backStencil.writeMask = dynamicState.backStencil.writeMask; |
| } |
| |
| if(dynamicStateFlags.dynamicStencilReference && stencilEnable) |
| { |
| frontStencil.reference = dynamicState.frontStencil.reference; |
| backStencil.reference = dynamicState.backStencil.reference; |
| } |
| } |
| |
| bool FragmentState::depthWriteActive(const Attachments &attachments) const |
| { |
| // "Depth writes are always disabled when depthTestEnable is VK_FALSE." |
| return depthTestActive(attachments) && depthWriteEnable; |
| } |
| |
| bool FragmentState::depthTestActive(const Attachments &attachments) const |
| { |
| return attachments.depthBuffer && depthTestEnable; |
| } |
| |
| bool FragmentState::stencilActive(const Attachments &attachments) const |
| { |
| return attachments.stencilBuffer && stencilEnable; |
| } |
| |
| bool FragmentState::depthBoundsTestActive(const Attachments &attachments) const |
| { |
| return attachments.depthBuffer && depthBoundsTestEnable; |
| } |
| |
| void FragmentState::setDepthStencilState(const VkPipelineDepthStencilStateCreateInfo *depthStencilState) |
| { |
| if((depthStencilState->flags & |
| ~(VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_DEPTH_ACCESS_BIT_EXT | |
| VK_PIPELINE_DEPTH_STENCIL_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_STENCIL_ACCESS_BIT_EXT)) != 0) |
| { |
| UNSUPPORTED("depthStencilState->flags 0x%08X", int(depthStencilState->flags)); |
| } |
| |
| depthBoundsTestEnable = (depthStencilState->depthBoundsTestEnable != VK_FALSE); |
| minDepthBounds = depthStencilState->minDepthBounds; |
| maxDepthBounds = depthStencilState->maxDepthBounds; |
| |
| depthTestEnable = (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; |
| } |
| } |
| |
| void FragmentOutputInterfaceState::initialize(const VkPipelineColorBlendStateCreateInfo *colorBlendState, |
| const VkPipelineMultisampleStateCreateInfo *multisampleState, |
| const vk::RenderPass *renderPass, uint32_t subpassIndex, |
| const VkPipelineRenderingCreateInfo *rendering, |
| const DynamicStateFlags &allDynamicStateFlags) |
| { |
| dynamicStateFlags = allDynamicStateFlags.fragmentOutputInterface; |
| |
| multisample.set(multisampleState); |
| |
| if(renderPass) |
| { |
| const VkSubpassDescription &subpass = renderPass->getSubpass(subpassIndex); |
| |
| // Ignore pColorBlendState when "the subpass of the render pass the pipeline |
| // is created against does not use any color attachments" |
| for(uint32_t i = 0; i < subpass.colorAttachmentCount; i++) |
| { |
| if(subpass.pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) |
| { |
| setColorBlendState(colorBlendState); |
| break; |
| } |
| } |
| } |
| else // No render pass |
| { |
| // When a pipeline is created without a VkRenderPass, if the VkPipelineRenderingCreateInfo structure |
| // is present in the pNext chain of VkGraphicsPipelineCreateInfo, it specifies the view mask and |
| // format of attachments used for rendering. If this structure is not specified, and the pipeline |
| // does not include a VkRenderPass, viewMask and colorAttachmentCount are 0, and |
| // depthAttachmentFormat and stencilAttachmentFormat are VK_FORMAT_UNDEFINED. If a graphics pipeline |
| // is created with a valid VkRenderPass, parameters of this structure are ignored. |
| |
| if(rendering) |
| { |
| if(rendering->colorAttachmentCount > 0) |
| { |
| // If renderPass is VK_NULL_HANDLE, the pipeline is being created with fragment |
| // output interface state, and VkPipelineRenderingCreateInfo::colorAttachmentCount |
| // is not equal to 0, pColorBlendState must be a valid pointer to a valid |
| // VkPipelineColorBlendStateCreateInfo structure |
| ASSERT(colorBlendState); |
| |
| setColorBlendState(colorBlendState); |
| } |
| } |
| } |
| } |
| |
| void FragmentOutputInterfaceState::applyState(const DynamicState &dynamicState) |
| { |
| if(dynamicStateFlags.dynamicBlendConstants) |
| { |
| blendConstants = dynamicState.blendConstants; |
| } |
| } |
| |
| void FragmentOutputInterfaceState::setColorBlendState(const VkPipelineColorBlendStateCreateInfo *colorBlendState) |
| { |
| if(colorBlendState->flags != 0 && |
| colorBlendState->flags != VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_EXT) |
| { |
| UNSUPPORTED("colorBlendState->flags 0x%08X", int(colorBlendState->flags)); |
| } |
| |
| if(colorBlendState->logicOpEnable != VK_FALSE) |
| { |
| UNSUPPORTED("VkPhysicalDeviceFeatures::logicOp"); |
| } |
| |
| if(!dynamicStateFlags.dynamicBlendConstants) |
| { |
| blendConstants.x = colorBlendState->blendConstants[0]; |
| blendConstants.y = colorBlendState->blendConstants[1]; |
| blendConstants.z = colorBlendState->blendConstants[2]; |
| blendConstants.w = colorBlendState->blendConstants[3]; |
| } |
| |
| const VkBaseInStructure *extensionColorBlendInfo = reinterpret_cast<const VkBaseInStructure *>(colorBlendState->pNext); |
| while(extensionColorBlendInfo) |
| { |
| switch(extensionColorBlendInfo->sType) |
| { |
| case VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT: |
| { |
| const VkPipelineColorBlendAdvancedStateCreateInfoEXT *colorBlendAdvancedCreateInfo = reinterpret_cast<const VkPipelineColorBlendAdvancedStateCreateInfoEXT *>(extensionColorBlendInfo); |
| ASSERT(colorBlendAdvancedCreateInfo->blendOverlap == VK_BLEND_OVERLAP_UNCORRELATED_EXT); |
| ASSERT(colorBlendAdvancedCreateInfo->dstPremultiplied == VK_TRUE); |
| ASSERT(colorBlendAdvancedCreateInfo->srcPremultiplied == VK_TRUE); |
| } |
| break; |
| case VK_STRUCTURE_TYPE_MAX_ENUM: |
| // dEQP tests that this value is ignored. |
| break; |
| default: |
| UNSUPPORTED("colorBlendState->pNext sType = %s", vk::Stringify(extensionColorBlendInfo->sType).c_str()); |
| break; |
| } |
| |
| extensionColorBlendInfo = extensionColorBlendInfo->pNext; |
| } |
| |
| ASSERT(colorBlendState->attachmentCount <= sw::MAX_COLOR_BUFFERS); |
| 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 }; |
| } |
| } |
| |
| BlendState FragmentOutputInterfaceState::getBlendState(int index, const Attachments &attachments, bool fragmentContainsKill) const |
| { |
| ASSERT((index >= 0) && (index < sw::MAX_COLOR_BUFFERS)); |
| auto &state = blendState[index]; |
| |
| BlendState activeBlendState = {}; |
| activeBlendState.alphaBlendEnable = alphaBlendActive(index, attachments, fragmentContainsKill); |
| |
| if(activeBlendState.alphaBlendEnable) |
| { |
| vk::Format format = attachments.colorBuffer[index]->getFormat(VK_IMAGE_ASPECT_COLOR_BIT); |
| |
| activeBlendState.sourceBlendFactor = blendFactor(state.blendOperation, state.sourceBlendFactor); |
| activeBlendState.destBlendFactor = blendFactor(state.blendOperation, state.destBlendFactor); |
| activeBlendState.blendOperation = blendOperation(state.blendOperation, state.sourceBlendFactor, state.destBlendFactor, format); |
| activeBlendState.sourceBlendFactorAlpha = blendFactor(state.blendOperationAlpha, state.sourceBlendFactorAlpha); |
| activeBlendState.destBlendFactorAlpha = blendFactor(state.blendOperationAlpha, state.destBlendFactorAlpha); |
| activeBlendState.blendOperationAlpha = blendOperation(state.blendOperationAlpha, state.sourceBlendFactorAlpha, state.destBlendFactorAlpha, format); |
| } |
| |
| return activeBlendState; |
| } |
| |
| bool FragmentOutputInterfaceState::alphaBlendActive(int index, const Attachments &attachments, bool fragmentContainsKill) const |
| { |
| ASSERT((index >= 0) && (index < sw::MAX_COLOR_BUFFERS)); |
| auto &state = blendState[index]; |
| |
| if(!attachments.colorBuffer[index] || !blendState[index].alphaBlendEnable) |
| { |
| return false; |
| } |
| |
| if(!(colorWriteActive(attachments) || fragmentContainsKill)) |
| { |
| return false; |
| } |
| |
| vk::Format format = attachments.colorBuffer[index]->getFormat(VK_IMAGE_ASPECT_COLOR_BIT); |
| bool colorBlend = blendOperation(state.blendOperation, state.sourceBlendFactor, state.destBlendFactor, format) != VK_BLEND_OP_SRC_EXT; |
| bool alphaBlend = blendOperation(state.blendOperationAlpha, state.sourceBlendFactorAlpha, state.destBlendFactorAlpha, format) != VK_BLEND_OP_SRC_EXT; |
| |
| return colorBlend || alphaBlend; |
| } |
| |
| VkBlendFactor FragmentOutputInterfaceState::blendFactor(VkBlendOp blendOperation, VkBlendFactor blendFactor) const |
| { |
| switch(blendOperation) |
| { |
| case VK_BLEND_OP_ADD: |
| case VK_BLEND_OP_SUBTRACT: |
| case VK_BLEND_OP_REVERSE_SUBTRACT: |
| return blendFactor; |
| case VK_BLEND_OP_MIN: |
| case VK_BLEND_OP_MAX: |
| case VK_BLEND_OP_MULTIPLY_EXT: |
| case VK_BLEND_OP_SCREEN_EXT: |
| case VK_BLEND_OP_OVERLAY_EXT: |
| case VK_BLEND_OP_DARKEN_EXT: |
| case VK_BLEND_OP_LIGHTEN_EXT: |
| case VK_BLEND_OP_COLORDODGE_EXT: |
| case VK_BLEND_OP_COLORBURN_EXT: |
| case VK_BLEND_OP_HARDLIGHT_EXT: |
| case VK_BLEND_OP_SOFTLIGHT_EXT: |
| case VK_BLEND_OP_DIFFERENCE_EXT: |
| case VK_BLEND_OP_EXCLUSION_EXT: |
| case VK_BLEND_OP_HSL_HUE_EXT: |
| case VK_BLEND_OP_HSL_SATURATION_EXT: |
| case VK_BLEND_OP_HSL_COLOR_EXT: |
| case VK_BLEND_OP_HSL_LUMINOSITY_EXT: |
| return VK_BLEND_FACTOR_ONE; |
| default: |
| ASSERT(false); |
| return blendFactor; |
| } |
| } |
| |
| VkBlendOp FragmentOutputInterfaceState::blendOperation(VkBlendOp blendOperation, VkBlendFactor sourceBlendFactor, VkBlendFactor destBlendFactor, vk::Format format) const |
| { |
| switch(blendOperation) |
| { |
| case VK_BLEND_OP_ADD: |
| if(sourceBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| if(destBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| return VK_BLEND_OP_ZERO_EXT; |
| } |
| else if(destBlendFactor == VK_BLEND_FACTOR_ONE) |
| { |
| return VK_BLEND_OP_DST_EXT; |
| } |
| } |
| else if(sourceBlendFactor == VK_BLEND_FACTOR_ONE) |
| { |
| if(destBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| return VK_BLEND_OP_SRC_EXT; |
| } |
| } |
| break; |
| case VK_BLEND_OP_SUBTRACT: |
| if(sourceBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| if(destBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| return VK_BLEND_OP_ZERO_EXT; |
| } |
| else if(format.isUnsignedNormalized()) |
| { |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero |
| } |
| } |
| else if(sourceBlendFactor == VK_BLEND_FACTOR_ONE) |
| { |
| if(destBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| return VK_BLEND_OP_SRC_EXT; |
| } |
| } |
| break; |
| case VK_BLEND_OP_REVERSE_SUBTRACT: |
| if(sourceBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| if(destBlendFactor == VK_BLEND_FACTOR_ZERO) |
| { |
| return VK_BLEND_OP_ZERO_EXT; |
| } |
| else if(destBlendFactor == VK_BLEND_FACTOR_ONE) |
| { |
| return VK_BLEND_OP_DST_EXT; |
| } |
| } |
| else |
| { |
| if(destBlendFactor == VK_BLEND_FACTOR_ZERO && format.isUnsignedNormalized()) |
| { |
| return VK_BLEND_OP_ZERO_EXT; // Negative, clamped to zero |
| } |
| } |
| break; |
| case VK_BLEND_OP_MIN: |
| return VK_BLEND_OP_MIN; |
| case VK_BLEND_OP_MAX: |
| return VK_BLEND_OP_MAX; |
| case VK_BLEND_OP_MULTIPLY_EXT: |
| case VK_BLEND_OP_SCREEN_EXT: |
| case VK_BLEND_OP_OVERLAY_EXT: |
| case VK_BLEND_OP_DARKEN_EXT: |
| case VK_BLEND_OP_LIGHTEN_EXT: |
| case VK_BLEND_OP_COLORDODGE_EXT: |
| case VK_BLEND_OP_COLORBURN_EXT: |
| case VK_BLEND_OP_HARDLIGHT_EXT: |
| case VK_BLEND_OP_SOFTLIGHT_EXT: |
| case VK_BLEND_OP_DIFFERENCE_EXT: |
| case VK_BLEND_OP_EXCLUSION_EXT: |
| case VK_BLEND_OP_HSL_HUE_EXT: |
| case VK_BLEND_OP_HSL_SATURATION_EXT: |
| case VK_BLEND_OP_HSL_COLOR_EXT: |
| case VK_BLEND_OP_HSL_LUMINOSITY_EXT: |
| return blendOperation; |
| default: |
| ASSERT(false); |
| } |
| |
| return blendOperation; |
| } |
| |
| bool FragmentOutputInterfaceState::colorWriteActive(const Attachments &attachments) const |
| { |
| for(int i = 0; i < sw::MAX_COLOR_BUFFERS; i++) |
| { |
| if(colorWriteActive(i, attachments)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| int FragmentOutputInterfaceState::colorWriteActive(int index, const Attachments &attachments) const |
| { |
| ASSERT((index >= 0) && (index < sw::MAX_COLOR_BUFFERS)); |
| auto &state = blendState[index]; |
| |
| if(!attachments.colorBuffer[index] || attachments.colorBuffer[index]->getFormat() == VK_FORMAT_UNDEFINED) |
| { |
| return 0; |
| } |
| |
| vk::Format format = attachments.colorBuffer[index]->getFormat(VK_IMAGE_ASPECT_COLOR_BIT); |
| |
| if(blendOperation(state.blendOperation, state.sourceBlendFactor, state.destBlendFactor, format) == VK_BLEND_OP_DST_EXT && |
| blendOperation(state.blendOperationAlpha, state.sourceBlendFactorAlpha, state.destBlendFactorAlpha, format) == VK_BLEND_OP_DST_EXT) |
| { |
| return 0; |
| } |
| |
| return colorWriteMask[index]; |
| } |
| |
| GraphicsState::GraphicsState(const Device *device, const VkGraphicsPipelineCreateInfo *pCreateInfo, |
| const PipelineLayout *layout) |
| { |
| if((pCreateInfo->flags & |
| ~(VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT | |
| VK_PIPELINE_CREATE_DERIVATIVE_BIT | |
| VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT | |
| VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT | |
| VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT | |
| VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | |
| VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT | |
| VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT)) != 0) |
| { |
| UNSUPPORTED("pCreateInfo->flags 0x%08X", int(pCreateInfo->flags)); |
| } |
| |
| DynamicStateFlags dynamicStateFlags = ParseDynamicStateFlags(pCreateInfo->pDynamicState); |
| const auto *rendering = GetExtendedStruct<VkPipelineRenderingCreateInfo>(pCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO); |
| |
| // First, get the subset of state specified in pCreateInfo itself. |
| validSubset = GraphicsPipeline::GetGraphicsPipelineSubset(pCreateInfo); |
| |
| // If rasterizer discard is enabled (and not dynamically overridable), ignore the fragment |
| // and fragment output subsets, as they will not be used. |
| if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0 && |
| pCreateInfo->pRasterizationState->rasterizerDiscardEnable && |
| !dynamicStateFlags.preRasterization.dynamicRasterizerDiscardEnable) |
| { |
| validSubset &= ~(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT | VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT); |
| } |
| |
| if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) != 0) |
| { |
| vertexInputInterfaceState.initialize(pCreateInfo->pVertexInputState, |
| pCreateInfo->pInputAssemblyState, |
| dynamicStateFlags); |
| } |
| if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0) |
| { |
| preRasterizationState.initialize(device, |
| layout, |
| pCreateInfo->pViewportState, |
| pCreateInfo->pRasterizationState, |
| vk::Cast(pCreateInfo->renderPass), |
| pCreateInfo->subpass, |
| rendering, |
| dynamicStateFlags); |
| } |
| if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0) |
| { |
| fragmentState.initialize(layout, |
| pCreateInfo->pDepthStencilState, |
| vk::Cast(pCreateInfo->renderPass), |
| pCreateInfo->subpass, |
| rendering, |
| dynamicStateFlags); |
| } |
| if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) != 0) |
| { |
| fragmentOutputInterfaceState.initialize(pCreateInfo->pColorBlendState, |
| pCreateInfo->pMultisampleState, |
| vk::Cast(pCreateInfo->renderPass), |
| pCreateInfo->subpass, |
| rendering, |
| dynamicStateFlags); |
| } |
| |
| // Then, apply state coming from pipeline libraries. |
| const auto *libraryCreateInfo = vk::GetExtendedStruct<VkPipelineLibraryCreateInfoKHR>(pCreateInfo->pNext, VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR); |
| if(libraryCreateInfo) |
| { |
| for(uint32_t i = 0; i < libraryCreateInfo->libraryCount; ++i) |
| { |
| const auto *library = static_cast<const GraphicsPipeline *>(vk::Cast(libraryCreateInfo->pLibraries[i])); |
| const GraphicsState &libraryState = library->getState(); |
| const VkGraphicsPipelineLibraryFlagsEXT librarySubset = libraryState.validSubset; |
| |
| // The library subsets should be disjoint |
| ASSERT((libraryState.validSubset & validSubset) == 0); |
| |
| if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) != 0) |
| { |
| vertexInputInterfaceState = libraryState.vertexInputInterfaceState; |
| } |
| if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0) |
| { |
| preRasterizationState = libraryState.preRasterizationState; |
| if(layout) |
| { |
| preRasterizationState.overridePipelineLayout(layout); |
| } |
| } |
| if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0) |
| { |
| fragmentState = libraryState.fragmentState; |
| if(layout) |
| { |
| fragmentState.overridePipelineLayout(layout); |
| } |
| } |
| if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) != 0) |
| { |
| fragmentOutputInterfaceState = libraryState.fragmentOutputInterfaceState; |
| } |
| |
| validSubset |= libraryState.validSubset; |
| } |
| } |
| } |
| |
| GraphicsState GraphicsState::combineStates(const DynamicState &dynamicState) const |
| { |
| GraphicsState combinedState = *this; |
| |
| // Make a copy of the states for modification, then either keep the pipeline state or apply the dynamic state. |
| combinedState.vertexInputInterfaceState.applyState(dynamicState); |
| combinedState.preRasterizationState.applyState(dynamicState); |
| combinedState.fragmentState.applyState(dynamicState); |
| combinedState.fragmentOutputInterfaceState.applyState(dynamicState); |
| |
| return combinedState; |
| } |
| |
| } // namespace vk |