|  | // 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 { | 
|  |  | 
|  | uint32_t IndexBuffer::bytesPerIndex() const | 
|  | { | 
|  | return indexType == VK_INDEX_TYPE_UINT16 ? 2u : 4u; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | const VkDeviceSize bufferSize = binding.buffer->getSize(); | 
|  | if(binding.offset >= bufferSize) | 
|  | { | 
|  | return;  // Nothing to draw | 
|  | } | 
|  |  | 
|  | const VkDeviceSize maxIndices = (bufferSize - binding.offset) / bytesPerIndex(); | 
|  | if(first > maxIndices) | 
|  | { | 
|  | return;  // Nothing to draw | 
|  | } | 
|  |  | 
|  | 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]; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 |