blob: 3061a76631eeb5b523b3379f6efd585bc59fb471 [file] [log] [blame]
//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// vk_cache_utils.cpp:
// Contains the classes for the Pipeline State Object cache as well as the RenderPass cache.
// Also contains the structures for the packed descriptions for the RenderPass and Pipeline.
//
#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
#include "common/aligned_memory.h"
#include "common/system_utils.h"
#include "libANGLE/BlobCache.h"
#include "libANGLE/VertexAttribute.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
#include "libANGLE/renderer/vulkan/VertexArrayVk.h"
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
#include "libANGLE/renderer/vulkan/vk_renderer.h"
#include <type_traits>
namespace rx
{
#if defined(ANGLE_DUMP_PIPELINE_CACHE_GRAPH)
constexpr bool kDumpPipelineCacheGraph = true;
#else
constexpr bool kDumpPipelineCacheGraph = false;
#endif // ANGLE_DUMP_PIPELINE_CACHE_GRAPH
namespace vk
{
namespace
{
static_assert(static_cast<uint32_t>(RenderPassLoadOp::Load) == VK_ATTACHMENT_LOAD_OP_LOAD,
"ConvertRenderPassLoadOpToVkLoadOp must be updated");
static_assert(static_cast<uint32_t>(RenderPassLoadOp::Clear) == VK_ATTACHMENT_LOAD_OP_CLEAR,
"ConvertRenderPassLoadOpToVkLoadOp must be updated");
static_assert(static_cast<uint32_t>(RenderPassLoadOp::DontCare) == VK_ATTACHMENT_LOAD_OP_DONT_CARE,
"ConvertRenderPassLoadOpToVkLoadOp must be updated");
static_assert(static_cast<uint32_t>(RenderPassLoadOp::None) == 3,
"ConvertRenderPassLoadOpToVkLoadOp must be updated");
static_assert(static_cast<uint32_t>(RenderPassStoreOp::Store) == VK_ATTACHMENT_STORE_OP_STORE,
"ConvertRenderPassStoreOpToVkStoreOp must be updated");
static_assert(static_cast<uint32_t>(RenderPassStoreOp::DontCare) ==
VK_ATTACHMENT_STORE_OP_DONT_CARE,
"ConvertRenderPassStoreOpToVkStoreOp must be updated");
static_assert(static_cast<uint32_t>(RenderPassStoreOp::None) == 2,
"ConvertRenderPassStoreOpToVkStoreOp must be updated");
constexpr uint16_t kMinSampleShadingScale = angle::BitMask<uint16_t>(8);
VkAttachmentLoadOp ConvertRenderPassLoadOpToVkLoadOp(RenderPassLoadOp loadOp)
{
return loadOp == RenderPassLoadOp::None ? VK_ATTACHMENT_LOAD_OP_NONE_EXT
: static_cast<VkAttachmentLoadOp>(loadOp);
}
VkAttachmentStoreOp ConvertRenderPassStoreOpToVkStoreOp(RenderPassStoreOp storeOp)
{
return storeOp == RenderPassStoreOp::None ? VK_ATTACHMENT_STORE_OP_NONE_EXT
: static_cast<VkAttachmentStoreOp>(storeOp);
}
constexpr size_t TransitionBits(size_t size)
{
return size / kGraphicsPipelineDirtyBitBytes;
}
constexpr size_t kPipelineShadersDescOffset = 0;
constexpr size_t kPipelineShadersDescSize =
kGraphicsPipelineShadersStateSize + kGraphicsPipelineSharedNonVertexInputStateSize;
constexpr size_t kPipelineFragmentOutputDescOffset = kGraphicsPipelineShadersStateSize;
constexpr size_t kPipelineFragmentOutputDescSize =
kGraphicsPipelineSharedNonVertexInputStateSize + kGraphicsPipelineFragmentOutputStateSize;
constexpr size_t kPipelineVertexInputDescOffset =
kGraphicsPipelineShadersStateSize + kPipelineFragmentOutputDescSize;
constexpr size_t kPipelineVertexInputDescSize = kGraphicsPipelineVertexInputStateSize;
static_assert(kPipelineShadersDescOffset % kGraphicsPipelineDirtyBitBytes == 0);
static_assert(kPipelineShadersDescSize % kGraphicsPipelineDirtyBitBytes == 0);
static_assert(kPipelineFragmentOutputDescOffset % kGraphicsPipelineDirtyBitBytes == 0);
static_assert(kPipelineFragmentOutputDescSize % kGraphicsPipelineDirtyBitBytes == 0);
static_assert(kPipelineVertexInputDescOffset % kGraphicsPipelineDirtyBitBytes == 0);
static_assert(kPipelineVertexInputDescSize % kGraphicsPipelineDirtyBitBytes == 0);
constexpr GraphicsPipelineTransitionBits kPipelineShadersTransitionBitsMask =
GraphicsPipelineTransitionBits::Mask(TransitionBits(kPipelineShadersDescSize) +
TransitionBits(kPipelineShadersDescOffset)) &
~GraphicsPipelineTransitionBits::Mask(TransitionBits(kPipelineShadersDescOffset));
constexpr GraphicsPipelineTransitionBits kPipelineFragmentOutputTransitionBitsMask =
GraphicsPipelineTransitionBits::Mask(TransitionBits(kPipelineFragmentOutputDescSize) +
TransitionBits(kPipelineFragmentOutputDescOffset)) &
~GraphicsPipelineTransitionBits::Mask(TransitionBits(kPipelineFragmentOutputDescOffset));
constexpr GraphicsPipelineTransitionBits kPipelineVertexInputTransitionBitsMask =
GraphicsPipelineTransitionBits::Mask(TransitionBits(kPipelineVertexInputDescSize) +
TransitionBits(kPipelineVertexInputDescOffset)) &
~GraphicsPipelineTransitionBits::Mask(TransitionBits(kPipelineVertexInputDescOffset));
bool GraphicsPipelineHasVertexInput(GraphicsPipelineSubset subset)
{
return subset == GraphicsPipelineSubset::Complete ||
subset == GraphicsPipelineSubset::VertexInput;
}
bool GraphicsPipelineHasShaders(GraphicsPipelineSubset subset)
{
return subset == GraphicsPipelineSubset::Complete || subset == GraphicsPipelineSubset::Shaders;
}
bool GraphicsPipelineHasShadersOrFragmentOutput(GraphicsPipelineSubset subset)
{
return subset != GraphicsPipelineSubset::VertexInput;
}
bool GraphicsPipelineHasFragmentOutput(GraphicsPipelineSubset subset)
{
return subset == GraphicsPipelineSubset::Complete ||
subset == GraphicsPipelineSubset::FragmentOutput;
}
uint8_t PackGLBlendOp(gl::BlendEquationType blendOp)
{
switch (blendOp)
{
case gl::BlendEquationType::Add:
return static_cast<uint8_t>(VK_BLEND_OP_ADD);
case gl::BlendEquationType::Subtract:
return static_cast<uint8_t>(VK_BLEND_OP_SUBTRACT);
case gl::BlendEquationType::ReverseSubtract:
return static_cast<uint8_t>(VK_BLEND_OP_REVERSE_SUBTRACT);
case gl::BlendEquationType::Min:
return static_cast<uint8_t>(VK_BLEND_OP_MIN);
case gl::BlendEquationType::Max:
return static_cast<uint8_t>(VK_BLEND_OP_MAX);
case gl::BlendEquationType::Multiply:
return static_cast<uint8_t>(VK_BLEND_OP_MULTIPLY_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Screen:
return static_cast<uint8_t>(VK_BLEND_OP_SCREEN_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Overlay:
return static_cast<uint8_t>(VK_BLEND_OP_OVERLAY_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Darken:
return static_cast<uint8_t>(VK_BLEND_OP_DARKEN_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Lighten:
return static_cast<uint8_t>(VK_BLEND_OP_LIGHTEN_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Colordodge:
return static_cast<uint8_t>(VK_BLEND_OP_COLORDODGE_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Colorburn:
return static_cast<uint8_t>(VK_BLEND_OP_COLORBURN_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Hardlight:
return static_cast<uint8_t>(VK_BLEND_OP_HARDLIGHT_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Softlight:
return static_cast<uint8_t>(VK_BLEND_OP_SOFTLIGHT_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Difference:
return static_cast<uint8_t>(VK_BLEND_OP_DIFFERENCE_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::Exclusion:
return static_cast<uint8_t>(VK_BLEND_OP_EXCLUSION_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::HslHue:
return static_cast<uint8_t>(VK_BLEND_OP_HSL_HUE_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::HslSaturation:
return static_cast<uint8_t>(VK_BLEND_OP_HSL_SATURATION_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::HslColor:
return static_cast<uint8_t>(VK_BLEND_OP_HSL_COLOR_EXT - VK_BLEND_OP_ZERO_EXT);
case gl::BlendEquationType::HslLuminosity:
return static_cast<uint8_t>(VK_BLEND_OP_HSL_LUMINOSITY_EXT - VK_BLEND_OP_ZERO_EXT);
default:
UNREACHABLE();
return 0;
}
}
VkBlendOp UnpackBlendOp(uint8_t packedBlendOp)
{
if (packedBlendOp <= VK_BLEND_OP_MAX)
{
return static_cast<VkBlendOp>(packedBlendOp);
}
return static_cast<VkBlendOp>(packedBlendOp + VK_BLEND_OP_ZERO_EXT);
}
uint8_t PackGLBlendFactor(gl::BlendFactorType blendFactor)
{
switch (blendFactor)
{
case gl::BlendFactorType::Zero:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ZERO);
case gl::BlendFactorType::One:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE);
case gl::BlendFactorType::SrcColor:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_COLOR);
case gl::BlendFactorType::DstColor:
return static_cast<uint8_t>(VK_BLEND_FACTOR_DST_COLOR);
case gl::BlendFactorType::OneMinusSrcColor:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR);
case gl::BlendFactorType::SrcAlpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_ALPHA);
case gl::BlendFactorType::OneMinusSrcAlpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
case gl::BlendFactorType::DstAlpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_DST_ALPHA);
case gl::BlendFactorType::OneMinusDstAlpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA);
case gl::BlendFactorType::OneMinusDstColor:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR);
case gl::BlendFactorType::SrcAlphaSaturate:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_ALPHA_SATURATE);
case gl::BlendFactorType::ConstantColor:
return static_cast<uint8_t>(VK_BLEND_FACTOR_CONSTANT_COLOR);
case gl::BlendFactorType::ConstantAlpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_CONSTANT_ALPHA);
case gl::BlendFactorType::OneMinusConstantColor:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR);
case gl::BlendFactorType::OneMinusConstantAlpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA);
case gl::BlendFactorType::Src1Color:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_COLOR);
case gl::BlendFactorType::Src1Alpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_ALPHA);
case gl::BlendFactorType::OneMinusSrc1Color:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR);
case gl::BlendFactorType::OneMinusSrc1Alpha:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA);
default:
UNREACHABLE();
return 0;
}
}
void UnpackAttachmentDesc(Context *context,
VkAttachmentDescription2 *desc,
angle::FormatID formatID,
uint8_t samples,
const PackedAttachmentOpsDesc &ops)
{
*desc = {};
desc->sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
desc->format = GetVkFormatFromFormatID(formatID);
desc->samples = gl_vk::GetSamples(samples, context->getFeatures().limitSampleCountTo2.enabled);
desc->loadOp = ConvertRenderPassLoadOpToVkLoadOp(static_cast<RenderPassLoadOp>(ops.loadOp));
desc->storeOp =
ConvertRenderPassStoreOpToVkStoreOp(static_cast<RenderPassStoreOp>(ops.storeOp));
desc->stencilLoadOp =
ConvertRenderPassLoadOpToVkLoadOp(static_cast<RenderPassLoadOp>(ops.stencilLoadOp));
desc->stencilStoreOp =
ConvertRenderPassStoreOpToVkStoreOp(static_cast<RenderPassStoreOp>(ops.stencilStoreOp));
desc->initialLayout =
ConvertImageLayoutToVkImageLayout(context, static_cast<ImageLayout>(ops.initialLayout));
desc->finalLayout =
ConvertImageLayoutToVkImageLayout(context, static_cast<ImageLayout>(ops.finalLayout));
}
struct AttachmentInfo
{
bool usedAsInputAttachment;
bool isInvalidated;
// If only one aspect of a depth/stencil image is resolved, the following is used to retain the
// other aspect.
bool isUnused;
};
void UnpackColorResolveAttachmentDesc(VkAttachmentDescription2 *desc,
angle::FormatID formatID,
const AttachmentInfo &info)
{
*desc = {};
desc->sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
desc->format = GetVkFormatFromFormatID(formatID);
// This function is for color resolve attachments.
const angle::Format &angleFormat = angle::Format::Get(formatID);
ASSERT(angleFormat.depthBits == 0 && angleFormat.stencilBits == 0);
// Resolve attachments always have a sample count of 1.
//
// If the corresponding color attachment needs to take its initial value from the resolve
// attachment (i.e. needs to be unresolved), loadOp needs to be set to LOAD, otherwise it should
// be DONT_CARE as it gets overwritten during resolve.
//
// storeOp should be STORE. If the attachment is invalidated, it is set to DONT_CARE.
desc->samples = VK_SAMPLE_COUNT_1_BIT;
desc->loadOp =
info.usedAsInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->storeOp =
info.isInvalidated ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
desc->initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
desc->finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
void UnpackDepthStencilResolveAttachmentDesc(vk::Context *context,
VkAttachmentDescription2 *desc,
angle::FormatID formatID,
const AttachmentInfo &depthInfo,
const AttachmentInfo &stencilInfo)
{
// There cannot be simultaneous usages of the depth/stencil resolve image, as depth/stencil
// resolve currently only comes from depth/stencil renderbuffers.
*desc = {};
desc->sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
desc->format = GetVkFormatFromFormatID(formatID);
// This function is for depth/stencil resolve attachment.
const angle::Format &angleFormat = angle::Format::Get(formatID);
ASSERT(angleFormat.depthBits != 0 || angleFormat.stencilBits != 0);
// Missing aspects are folded in isInvalidate parameters, so no need to double check.
ASSERT(angleFormat.depthBits > 0 || depthInfo.isInvalidated);
ASSERT(angleFormat.stencilBits > 0 || stencilInfo.isInvalidated);
const bool supportsLoadStoreOpNone =
context->getRenderer()->getFeatures().supportsRenderPassLoadStoreOpNone.enabled;
const bool supportsStoreOpNone =
supportsLoadStoreOpNone ||
context->getRenderer()->getFeatures().supportsRenderPassStoreOpNone.enabled;
const VkAttachmentLoadOp preserveLoadOp =
supportsLoadStoreOpNone ? VK_ATTACHMENT_LOAD_OP_NONE_EXT : VK_ATTACHMENT_LOAD_OP_LOAD;
const VkAttachmentStoreOp preserveStoreOp =
supportsStoreOpNone ? VK_ATTACHMENT_STORE_OP_NONE : VK_ATTACHMENT_STORE_OP_STORE;
// Similarly to color resolve attachments, sample count is 1, loadOp is LOAD or DONT_CARE based
// on whether unresolve is required, and storeOp is STORE or DONT_CARE based on whether the
// attachment is invalidated or the aspect exists.
desc->samples = VK_SAMPLE_COUNT_1_BIT;
if (depthInfo.isUnused)
{
desc->loadOp = preserveLoadOp;
desc->storeOp = preserveStoreOp;
}
else
{
desc->loadOp = depthInfo.usedAsInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD
: VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->storeOp = depthInfo.isInvalidated ? VK_ATTACHMENT_STORE_OP_DONT_CARE
: VK_ATTACHMENT_STORE_OP_STORE;
}
if (stencilInfo.isUnused)
{
desc->stencilLoadOp = preserveLoadOp;
desc->stencilStoreOp = preserveStoreOp;
}
else
{
desc->stencilLoadOp = stencilInfo.usedAsInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD
: VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->stencilStoreOp = stencilInfo.isInvalidated ? VK_ATTACHMENT_STORE_OP_DONT_CARE
: VK_ATTACHMENT_STORE_OP_STORE;
}
desc->initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
desc->finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
void UnpackFragmentShadingRateAttachmentDesc(VkAttachmentDescription2 *desc)
{
*desc = {};
desc->sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
desc->flags = 0;
desc->format = VK_FORMAT_R8_UINT;
desc->samples = VK_SAMPLE_COUNT_1_BIT;
desc->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
desc->storeOp = VK_ATTACHMENT_STORE_OP_NONE;
desc->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
desc->initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
desc->finalLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
}
void UnpackStencilState(const PackedStencilOpState &packedState,
VkStencilOpState *stateOut,
bool writeMaskWorkaround)
{
// Any non-zero value works for the purposes of the useNonZeroStencilWriteMaskStaticState driver
// bug workaround.
constexpr uint32_t kNonZeroWriteMaskForWorkaround = 1;
stateOut->failOp = static_cast<VkStencilOp>(packedState.fail);
stateOut->passOp = static_cast<VkStencilOp>(packedState.pass);
stateOut->depthFailOp = static_cast<VkStencilOp>(packedState.depthFail);
stateOut->compareOp = static_cast<VkCompareOp>(packedState.compare);
stateOut->compareMask = 0;
stateOut->writeMask = writeMaskWorkaround ? kNonZeroWriteMaskForWorkaround : 0;
stateOut->reference = 0;
}
void UnpackBlendAttachmentState(const PackedColorBlendAttachmentState &packedState,
VkPipelineColorBlendAttachmentState *stateOut)
{
stateOut->srcColorBlendFactor = static_cast<VkBlendFactor>(packedState.srcColorBlendFactor);
stateOut->dstColorBlendFactor = static_cast<VkBlendFactor>(packedState.dstColorBlendFactor);
stateOut->colorBlendOp = UnpackBlendOp(packedState.colorBlendOp);
stateOut->srcAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.srcAlphaBlendFactor);
stateOut->dstAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.dstAlphaBlendFactor);
stateOut->alphaBlendOp = UnpackBlendOp(packedState.alphaBlendOp);
}
void SetPipelineShaderStageInfo(const VkStructureType type,
const VkShaderStageFlagBits stage,
const VkShaderModule module,
const VkSpecializationInfo &specializationInfo,
VkPipelineShaderStageCreateInfo *shaderStage)
{
shaderStage->sType = type;
shaderStage->flags = 0;
shaderStage->stage = stage;
shaderStage->module = module;
shaderStage->pName = "main";
shaderStage->pSpecializationInfo = &specializationInfo;
}
// Defines a subpass that uses the resolve attachments as input attachments to initialize color and
// depth/stencil attachments that need to be "unresolved" at the start of the render pass. The
// subpass will only contain the attachments that need to be unresolved to simplify the shader that
// performs the operations.
void InitializeUnresolveSubpass(
const RenderPassDesc &desc,
const gl::DrawBuffersVector<VkAttachmentReference2> &drawSubpassColorAttachmentRefs,
const gl::DrawBuffersVector<VkAttachmentReference2> &drawSubpassResolveAttachmentRefs,
const VkAttachmentReference2 &depthStencilAttachmentRef,
const VkAttachmentReference2 &depthStencilResolveAttachmentRef,
gl::DrawBuffersVector<VkAttachmentReference2> *unresolveColorAttachmentRefs,
VkAttachmentReference2 *unresolveDepthStencilAttachmentRef,
FramebufferAttachmentsVector<VkAttachmentReference2> *unresolveInputAttachmentRefs,
FramebufferAttachmentsVector<uint32_t> *unresolvePreserveAttachmentRefs,
VkSubpassDescription2 *subpassDesc)
{
// Assume the GL Framebuffer has the following attachments enabled:
//
// GL Color 0
// GL Color 3
// GL Color 4
// GL Color 6
// GL Color 7
// GL Depth/Stencil
//
// Additionally, assume Color 0, 4 and 6 are multisampled-render-to-texture (or for any other
// reason) have corresponding resolve attachments. Furthermore, say Color 4 and 6 require an
// initial unresolve operation.
//
// In the above example, the render pass is created with the following attachments:
//
// RP Attachment[0] <- corresponding to GL Color 0
// RP Attachment[1] <- corresponding to GL Color 3
// RP Attachment[2] <- corresponding to GL Color 4
// RP Attachment[3] <- corresponding to GL Color 6
// RP Attachment[4] <- corresponding to GL Color 7
// RP Attachment[5] <- corresponding to GL Depth/Stencil
// RP Attachment[6] <- corresponding to resolve attachment of GL Color 0
// RP Attachment[7] <- corresponding to resolve attachment of GL Color 4
// RP Attachment[8] <- corresponding to resolve attachment of GL Color 6
//
// If the depth/stencil attachment is to be resolved, the following attachment would also be
// present:
//
// RP Attachment[9] <- corresponding to resolve attachment of GL Depth/Stencil
//
// The subpass that takes the application draw calls has the following attachments, creating the
// mapping from the Vulkan attachment indices (i.e. RP attachment indices) to GL indices as
// indicated by the GL shaders:
//
// Subpass[1] Color[0] -> RP Attachment[0]
// Subpass[1] Color[1] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Color[2] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Color[3] -> RP Attachment[1]
// Subpass[1] Color[4] -> RP Attachment[2]
// Subpass[1] Color[5] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Color[6] -> RP Attachment[3]
// Subpass[1] Color[7] -> RP Attachment[4]
// Subpass[1] Depth/Stencil -> RP Attachment[5]
// Subpass[1] Resolve[0] -> RP Attachment[6]
// Subpass[1] Resolve[1] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[2] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[3] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[4] -> RP Attachment[7]
// Subpass[1] Resolve[5] -> VK_ATTACHMENT_UNUSED
// Subpass[1] Resolve[6] -> RP Attachment[8]
// Subpass[1] Resolve[7] -> VK_ATTACHMENT_UNUSED
//
// With depth/stencil resolve attachment:
//
// Subpass[1] Depth/Stencil Resolve -> RP Attachment[9]
//
// The initial subpass that's created here is (remember that in the above example Color 4 and 6
// need to be unresolved):
//
// Subpass[0] Input[0] -> RP Attachment[7] = Subpass[1] Resolve[4]
// Subpass[0] Input[1] -> RP Attachment[8] = Subpass[1] Resolve[6]
// Subpass[0] Color[0] -> RP Attachment[2] = Subpass[1] Color[4]
// Subpass[0] Color[1] -> RP Attachment[3] = Subpass[1] Color[6]
//
// The trick here therefore is to use the color attachment refs already created for the
// application draw subpass indexed with colorIndexGL.
//
// If depth/stencil needs to be unresolved (note that as input attachment, it's inserted before
// the color attachments. See UtilsVk::unresolve()):
//
// Subpass[0] Input[0] -> RP Attachment[9] = Subpass[1] Depth/Stencil Resolve
// Subpass[0] Depth/Stencil -> RP Attachment[5] = Subpass[1] Depth/Stencil
//
// As an additional note, the attachments that are not used in the unresolve subpass must be
// preserved. That is color attachments and the depth/stencil attachment if any. Resolve
// attachments are rewritten by the next subpass, so they don't need to be preserved. Note that
// there's no need to preserve attachments whose loadOp is DONT_CARE. For simplicity, we
// preserve those as well. The driver would ideally avoid preserving attachments with
// loadOp=DONT_CARE.
//
// With the above example:
//
// Subpass[0] Preserve[0] -> RP Attachment[0] = Subpass[1] Color[0]
// Subpass[0] Preserve[1] -> RP Attachment[1] = Subpass[1] Color[3]
// Subpass[0] Preserve[2] -> RP Attachment[4] = Subpass[1] Color[7]
//
// If depth/stencil is not unresolved:
//
// Subpass[0] Preserve[3] -> RP Attachment[5] = Subpass[1] Depth/Stencil
//
// Again, the color attachment refs already created for the application draw subpass can be used
// indexed with colorIndexGL.
if (desc.hasDepthStencilUnresolveAttachment())
{
ASSERT(desc.hasDepthStencilAttachment());
ASSERT(desc.hasDepthStencilResolveAttachment());
*unresolveDepthStencilAttachmentRef = depthStencilAttachmentRef;
VkAttachmentReference2 unresolveDepthStencilInputAttachmentRef = {};
unresolveDepthStencilInputAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
unresolveDepthStencilInputAttachmentRef.attachment =
depthStencilResolveAttachmentRef.attachment;
unresolveDepthStencilInputAttachmentRef.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
unresolveDepthStencilInputAttachmentRef.aspectMask = 0;
if (desc.hasDepthUnresolveAttachment())
{
unresolveDepthStencilInputAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (desc.hasStencilUnresolveAttachment())
{
unresolveDepthStencilInputAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
unresolveInputAttachmentRefs->push_back(unresolveDepthStencilInputAttachmentRef);
}
else if (desc.hasDepthStencilAttachment())
{
// Preserve the depth/stencil attachment if not unresolved. Again, there's no need to
// preserve this attachment if loadOp=DONT_CARE, but we do for simplicity.
unresolvePreserveAttachmentRefs->push_back(depthStencilAttachmentRef.attachment);
}
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
if (!desc.hasColorUnresolveAttachment(colorIndexGL))
{
if (desc.isColorAttachmentEnabled(colorIndexGL))
{
unresolvePreserveAttachmentRefs->push_back(
drawSubpassColorAttachmentRefs[colorIndexGL].attachment);
}
continue;
}
ASSERT(desc.isColorAttachmentEnabled(colorIndexGL));
ASSERT(desc.hasColorResolveAttachment(colorIndexGL));
ASSERT(drawSubpassColorAttachmentRefs[colorIndexGL].attachment != VK_ATTACHMENT_UNUSED);
ASSERT(drawSubpassResolveAttachmentRefs[colorIndexGL].attachment != VK_ATTACHMENT_UNUSED);
unresolveColorAttachmentRefs->push_back(drawSubpassColorAttachmentRefs[colorIndexGL]);
unresolveInputAttachmentRefs->push_back(drawSubpassResolveAttachmentRefs[colorIndexGL]);
// Note the input attachment layout should be shader read-only. The subpass dependency
// will take care of transitioning the layout of the resolve attachment to color attachment
// automatically.
unresolveInputAttachmentRefs->back().layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
ASSERT(!unresolveColorAttachmentRefs->empty() ||
unresolveDepthStencilAttachmentRef->attachment != VK_ATTACHMENT_UNUSED);
ASSERT(unresolveColorAttachmentRefs->size() +
(desc.hasDepthStencilUnresolveAttachment() ? 1 : 0) ==
unresolveInputAttachmentRefs->size());
subpassDesc->sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2;
subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc->inputAttachmentCount = static_cast<uint32_t>(unresolveInputAttachmentRefs->size());
subpassDesc->pInputAttachments = unresolveInputAttachmentRefs->data();
subpassDesc->colorAttachmentCount = static_cast<uint32_t>(unresolveColorAttachmentRefs->size());
subpassDesc->pColorAttachments = unresolveColorAttachmentRefs->data();
subpassDesc->pDepthStencilAttachment = unresolveDepthStencilAttachmentRef;
subpassDesc->preserveAttachmentCount =
static_cast<uint32_t>(unresolvePreserveAttachmentRefs->size());
subpassDesc->pPreserveAttachments = unresolvePreserveAttachmentRefs->data();
}
// There is normally one subpass, and occasionally another for the unresolve operation.
constexpr size_t kSubpassFastVectorSize = 2;
template <typename T>
using SubpassVector = angle::FastVector<T, kSubpassFastVectorSize>;
void InitializeUnresolveSubpassDependencies(const SubpassVector<VkSubpassDescription2> &subpassDesc,
bool unresolveColor,
bool unresolveDepthStencil,
std::vector<VkSubpassDependency2> *subpassDependencies)
{
ASSERT(subpassDesc.size() >= 2);
ASSERT(unresolveColor || unresolveDepthStencil);
// The unresolve subpass is the first subpass. The application draw subpass is the next one.
constexpr uint32_t kUnresolveSubpassIndex = 0;
constexpr uint32_t kDrawSubpassIndex = 1;
// A subpass dependency is needed between the unresolve and draw subpasses. There are two
// hazards here:
//
// - Subpass 0 writes to color/depth/stencil attachments, subpass 1 writes to the same
// attachments. This is a WaW hazard (color/depth/stencil write -> color/depth/stencil write)
// similar to when two subsequent render passes write to the same images.
// - Subpass 0 reads from resolve attachments, subpass 1 writes to the same resolve attachments.
// This is a WaR hazard (fragment shader read -> color write) which only requires an execution
// barrier.
//
// Note: the DEPENDENCY_BY_REGION flag is necessary to create a "framebuffer-local" dependency,
// as opposed to "framebuffer-global". The latter is effectively a render pass break. The
// former creates a dependency per framebuffer region. If dependency scopes correspond to
// attachments with:
//
// - Same sample count: dependency is at sample granularity
// - Different sample count: dependency is at pixel granularity
//
// The latter is clarified by the spec as such:
//
// > Practically, the pixel vs sample granularity dependency means that if an input attachment
// > has a different number of samples than the pipeline's rasterizationSamples, then a fragment
// > can access any sample in the input attachment's pixel even if it only uses
// > framebuffer-local dependencies.
//
// The dependency for the first hazard above (attachment write -> attachment write) is on
// same-sample attachments, so it will not allow the use of input attachments as required by the
// unresolve subpass. As a result, even though the second hazard seems to be subsumed by the
// first (its src stage is earlier and its dst stage is the same), a separate dependency is
// created for it just to obtain a pixel granularity dependency.
//
// Note: depth/stencil resolve is considered to be done in the color write stage:
//
// > Moving to the next subpass automatically performs any multisample resolve operations in the
// > subpass being ended. End-of-subpass multisample resolves are treated as color attachment
// > writes for the purposes of synchronization. This applies to resolve operations for both
// > color and depth/stencil attachments. That is, they are considered to execute in the
// > VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT pipeline stage and their writes are
// > synchronized with VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT.
subpassDependencies->push_back({});
VkSubpassDependency2 *dependency = &subpassDependencies->back();
constexpr VkPipelineStageFlags kColorWriteStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
constexpr VkPipelineStageFlags kColorReadWriteStage =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
constexpr VkAccessFlags kColorWriteFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
constexpr VkAccessFlags kColorReadWriteFlags =
kColorWriteFlags | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
constexpr VkPipelineStageFlags kDepthStencilWriteStage =
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
constexpr VkPipelineStageFlags kDepthStencilReadWriteStage =
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
constexpr VkAccessFlags kDepthStencilWriteFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
constexpr VkAccessFlags kDepthStencilReadWriteFlags =
kDepthStencilWriteFlags | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
VkPipelineStageFlags attachmentWriteStages = 0;
VkPipelineStageFlags attachmentReadWriteStages = 0;
VkAccessFlags attachmentWriteFlags = 0;
VkAccessFlags attachmentReadWriteFlags = 0;
if (unresolveColor)
{
attachmentWriteStages |= kColorWriteStage;
attachmentReadWriteStages |= kColorReadWriteStage;
attachmentWriteFlags |= kColorWriteFlags;
attachmentReadWriteFlags |= kColorReadWriteFlags;
}
if (unresolveDepthStencil)
{
attachmentWriteStages |= kDepthStencilWriteStage;
attachmentReadWriteStages |= kDepthStencilReadWriteStage;
attachmentWriteFlags |= kDepthStencilWriteFlags;
attachmentReadWriteFlags |= kDepthStencilReadWriteFlags;
}
dependency->sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
dependency->srcSubpass = kUnresolveSubpassIndex;
dependency->dstSubpass = kDrawSubpassIndex;
dependency->srcStageMask = attachmentWriteStages;
dependency->dstStageMask = attachmentReadWriteStages;
dependency->srcAccessMask = attachmentWriteFlags;
dependency->dstAccessMask = attachmentReadWriteFlags;
dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
subpassDependencies->push_back({});
dependency = &subpassDependencies->back();
// Note again that depth/stencil resolve is considered to be done in the color output stage.
dependency->sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
dependency->srcSubpass = kUnresolveSubpassIndex;
dependency->dstSubpass = kDrawSubpassIndex;
dependency->srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency->dstStageMask = kColorWriteStage;
dependency->srcAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
dependency->dstAccessMask = kColorWriteFlags;
dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
}
// glFramebufferFetchBarrierEXT and glBlendBarrierKHR require a pipeline barrier to be inserted in
// the render pass. This requires a subpass self-dependency.
//
// For framebuffer fetch:
//
// srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
// dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
// srcAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
// dstAccess = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT
//
// For advanced blend:
//
// srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
// dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
// srcAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
// dstAccess = VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT
//
// Subpass dependencies cannot be added after the fact at the end of the render pass due to render
// pass compatibility rules. ANGLE specifies a subpass self-dependency with the above stage/access
// masks in preparation of potential framebuffer fetch and advanced blend barriers. This is known
// not to add any overhead on any hardware we have been able to gather information from.
void InitializeDefaultSubpassSelfDependencies(
Context *context,
const RenderPassDesc &desc,
uint32_t subpassIndex,
std::vector<VkSubpassDependency2> *subpassDependencies)
{
Renderer *renderer = context->getRenderer();
const bool hasRasterizationOrderAttachmentAccess =
renderer->getFeatures().supportsRasterizationOrderAttachmentAccess.enabled;
const bool hasBlendOperationAdvanced =
renderer->getFeatures().supportsBlendOperationAdvanced.enabled;
if (hasRasterizationOrderAttachmentAccess && !hasBlendOperationAdvanced)
{
// No need to specify a subpass dependency if VK_EXT_rasterization_order_attachment_access
// is enabled, as that extension makes this subpass dependency implicit.
return;
}
subpassDependencies->push_back({});
VkSubpassDependency2 *dependency = &subpassDependencies->back();
dependency->sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2;
dependency->srcSubpass = subpassIndex;
dependency->dstSubpass = subpassIndex;
dependency->srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency->dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency->srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency->dstAccessMask = 0;
if (!hasRasterizationOrderAttachmentAccess)
{
dependency->dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency->dstAccessMask |= VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
}
if (renderer->getFeatures().supportsBlendOperationAdvanced.enabled)
{
dependency->dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT;
}
dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
if (desc.viewCount() > 0)
{
dependency->dependencyFlags |= VK_DEPENDENCY_VIEW_LOCAL_BIT;
}
}
void InitializeMSRTSS(Context *context,
uint8_t renderToTextureSamples,
VkSubpassDescription2 *subpass,
VkSubpassDescriptionDepthStencilResolve *msrtssResolve,
VkMultisampledRenderToSingleSampledInfoEXT *msrtss,
VkMultisampledRenderToSingleSampledInfoGOOGLEX *msrtssGOOGLEX)
{
Renderer *renderer = context->getRenderer();
ASSERT(renderer->getFeatures().supportsMultisampledRenderToSingleSampled.enabled ||
renderer->getFeatures().supportsMultisampledRenderToSingleSampledGOOGLEX.enabled);
*msrtssResolve = {};
msrtssResolve->sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE;
msrtssResolve->depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
msrtssResolve->stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
*msrtss = {};
msrtss->sType = VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT;
msrtss->pNext = msrtssResolve;
msrtss->multisampledRenderToSingleSampledEnable = true;
msrtss->rasterizationSamples = gl_vk::GetSamples(
renderToTextureSamples, context->getFeatures().limitSampleCountTo2.enabled);
*msrtssGOOGLEX = {};
msrtssGOOGLEX->sType = VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_GOOGLEX;
msrtssGOOGLEX->multisampledRenderToSingleSampledEnable = true;
msrtssGOOGLEX->rasterizationSamples = msrtss->rasterizationSamples;
msrtssGOOGLEX->depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
msrtssGOOGLEX->stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
if (renderer->getFeatures().supportsMultisampledRenderToSingleSampled.enabled)
{
// msrtss->pNext is not null so can't use AddToPNextChain
AppendToPNextChain(subpass, msrtss);
}
else
{
AddToPNextChain(subpass, msrtssGOOGLEX);
}
}
void SetRenderPassViewMask(Context *context,
const uint32_t *viewMask,
VkRenderPassCreateInfo2 *createInfo,
SubpassVector<VkSubpassDescription2> *subpassDesc)
{
for (VkSubpassDescription2 &subpass : *subpassDesc)
{
subpass.viewMask = *viewMask;
}
// For VR, the views are correlated, so this would be an optimization. However, an
// application can also use multiview for example to render to all 6 faces of a cubemap, in
// which case the views are actually not so correlated. In the absence of any hints from
// the application, we have to decide on one or the other. Since VR is more expensive, the
// views are marked as correlated to optimize that use case.
createInfo->correlatedViewMaskCount = 1;
createInfo->pCorrelatedViewMasks = viewMask;
}
void ToAttachmentDesciption1(const VkAttachmentDescription2 &desc,
VkAttachmentDescription *desc1Out)
{
ASSERT(desc.pNext == nullptr);
*desc1Out = {};
desc1Out->flags = desc.flags;
desc1Out->format = desc.format;
desc1Out->samples = desc.samples;
desc1Out->loadOp = desc.loadOp;
desc1Out->storeOp = desc.storeOp;
desc1Out->stencilLoadOp = desc.stencilLoadOp;
desc1Out->stencilStoreOp = desc.stencilStoreOp;
desc1Out->initialLayout = desc.initialLayout;
desc1Out->finalLayout = desc.finalLayout;
}
void ToAttachmentReference1(const VkAttachmentReference2 &ref, VkAttachmentReference *ref1Out)
{
ASSERT(ref.pNext == nullptr);
*ref1Out = {};
ref1Out->attachment = ref.attachment;
ref1Out->layout = ref.layout;
}
void ToSubpassDescription1(const VkSubpassDescription2 &desc,
const FramebufferAttachmentsVector<VkAttachmentReference> &inputRefs,
const gl::DrawBuffersVector<VkAttachmentReference> &colorRefs,
const gl::DrawBuffersVector<VkAttachmentReference> &resolveRefs,
const VkAttachmentReference &depthStencilRef,
VkSubpassDescription *desc1Out)
{
ASSERT(desc.pNext == nullptr);
*desc1Out = {};
desc1Out->flags = desc.flags;
desc1Out->pipelineBindPoint = desc.pipelineBindPoint;
desc1Out->inputAttachmentCount = static_cast<uint32_t>(inputRefs.size());
desc1Out->pInputAttachments = !inputRefs.empty() ? inputRefs.data() : nullptr;
desc1Out->colorAttachmentCount = static_cast<uint32_t>(colorRefs.size());
desc1Out->pColorAttachments = !colorRefs.empty() ? colorRefs.data() : nullptr;
desc1Out->pResolveAttachments = !resolveRefs.empty() ? resolveRefs.data() : nullptr;
desc1Out->pDepthStencilAttachment = desc.pDepthStencilAttachment ? &depthStencilRef : nullptr;
desc1Out->preserveAttachmentCount = desc.preserveAttachmentCount;
desc1Out->pPreserveAttachments = desc.pPreserveAttachments;
}
void ToSubpassDependency1(const VkSubpassDependency2 &dep, VkSubpassDependency *dep1Out)
{
ASSERT(dep.pNext == nullptr);
*dep1Out = {};
dep1Out->srcSubpass = dep.srcSubpass;
dep1Out->dstSubpass = dep.dstSubpass;
dep1Out->srcStageMask = dep.srcStageMask;
dep1Out->dstStageMask = dep.dstStageMask;
dep1Out->srcAccessMask = dep.srcAccessMask;
dep1Out->dstAccessMask = dep.dstAccessMask;
dep1Out->dependencyFlags = dep.dependencyFlags;
}
void ToRenderPassMultiviewCreateInfo(const VkRenderPassCreateInfo2 &createInfo,
VkRenderPassCreateInfo *createInfo1,
SubpassVector<uint32_t> *viewMasks,
VkRenderPassMultiviewCreateInfo *multiviewInfo)
{
ASSERT(createInfo.correlatedViewMaskCount == 1);
const uint32_t viewMask = createInfo.pCorrelatedViewMasks[0];
viewMasks->resize(createInfo.subpassCount, viewMask);
multiviewInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
multiviewInfo->subpassCount = createInfo.subpassCount;
multiviewInfo->pViewMasks = viewMasks->data();
multiviewInfo->correlationMaskCount = createInfo.correlatedViewMaskCount;
multiviewInfo->pCorrelationMasks = createInfo.pCorrelatedViewMasks;
AddToPNextChain(createInfo1, multiviewInfo);
}
angle::Result CreateRenderPass1(Context *context,
const VkRenderPassCreateInfo2 &createInfo,
uint8_t viewCount,
RenderPass *renderPass)
{
// Convert the attachments to VkAttachmentDescription.
FramebufferAttachmentArray<VkAttachmentDescription> attachmentDescs;
for (uint32_t index = 0; index < createInfo.attachmentCount; ++index)
{
ToAttachmentDesciption1(createInfo.pAttachments[index], &attachmentDescs[index]);
}
// Convert subpass attachments to VkAttachmentReference and the subpass description to
// VkSubpassDescription.
SubpassVector<FramebufferAttachmentsVector<VkAttachmentReference>> subpassInputAttachmentRefs(
createInfo.subpassCount);
SubpassVector<gl::DrawBuffersVector<VkAttachmentReference>> subpassColorAttachmentRefs(
createInfo.subpassCount);
SubpassVector<gl::DrawBuffersVector<VkAttachmentReference>> subpassResolveAttachmentRefs(
createInfo.subpassCount);
SubpassVector<VkAttachmentReference> subpassDepthStencilAttachmentRefs(createInfo.subpassCount);
SubpassVector<VkSubpassDescription> subpassDescriptions(createInfo.subpassCount);
for (uint32_t subpass = 0; subpass < createInfo.subpassCount; ++subpass)
{
const VkSubpassDescription2 &desc = createInfo.pSubpasses[subpass];
FramebufferAttachmentsVector<VkAttachmentReference> &inputRefs =
subpassInputAttachmentRefs[subpass];
gl::DrawBuffersVector<VkAttachmentReference> &colorRefs =
subpassColorAttachmentRefs[subpass];
gl::DrawBuffersVector<VkAttachmentReference> &resolveRefs =
subpassResolveAttachmentRefs[subpass];
VkAttachmentReference &depthStencilRef = subpassDepthStencilAttachmentRefs[subpass];
inputRefs.resize(desc.inputAttachmentCount);
colorRefs.resize(desc.colorAttachmentCount);
for (uint32_t index = 0; index < desc.inputAttachmentCount; ++index)
{
ToAttachmentReference1(desc.pInputAttachments[index], &inputRefs[index]);
}
for (uint32_t index = 0; index < desc.colorAttachmentCount; ++index)
{
ToAttachmentReference1(desc.pColorAttachments[index], &colorRefs[index]);
}
if (desc.pResolveAttachments)
{
resolveRefs.resize(desc.colorAttachmentCount);
for (uint32_t index = 0; index < desc.colorAttachmentCount; ++index)
{
ToAttachmentReference1(desc.pResolveAttachments[index], &resolveRefs[index]);
}
}
if (desc.pDepthStencilAttachment)
{
ToAttachmentReference1(*desc.pDepthStencilAttachment, &depthStencilRef);
}
// Convert subpass itself.
ToSubpassDescription1(desc, inputRefs, colorRefs, resolveRefs, depthStencilRef,
&subpassDescriptions[subpass]);
}
// Convert subpass dependencies to VkSubpassDependency.
std::vector<VkSubpassDependency> subpassDependencies(createInfo.dependencyCount);
for (uint32_t index = 0; index < createInfo.dependencyCount; ++index)
{
ToSubpassDependency1(createInfo.pDependencies[index], &subpassDependencies[index]);
}
// Convert CreateInfo itself
VkRenderPassCreateInfo createInfo1 = {};
createInfo1.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
createInfo1.flags = createInfo.flags;
createInfo1.attachmentCount = createInfo.attachmentCount;
createInfo1.pAttachments = attachmentDescs.data();
createInfo1.subpassCount = createInfo.subpassCount;
createInfo1.pSubpasses = subpassDescriptions.data();
createInfo1.dependencyCount = static_cast<uint32_t>(subpassDependencies.size());
createInfo1.pDependencies = !subpassDependencies.empty() ? subpassDependencies.data() : nullptr;
SubpassVector<uint32_t> viewMasks;
VkRenderPassMultiviewCreateInfo multiviewInfo = {};
if (viewCount > 0)
{
ToRenderPassMultiviewCreateInfo(createInfo, &createInfo1, &viewMasks, &multiviewInfo);
}
// Initialize the render pass.
ANGLE_VK_TRY(context, renderPass->init(context->getDevice(), createInfo1));
return angle::Result::Continue;
}
void UpdateRenderPassColorPerfCounters(const VkRenderPassCreateInfo2 &createInfo,
FramebufferAttachmentMask depthStencilAttachmentIndices,
RenderPassPerfCounters *countersOut)
{
for (uint32_t index = 0; index < createInfo.attachmentCount; index++)
{
if (depthStencilAttachmentIndices.test(index))
{
continue;
}
VkAttachmentLoadOp loadOp = createInfo.pAttachments[index].loadOp;
VkAttachmentStoreOp storeOp = createInfo.pAttachments[index].storeOp;
countersOut->colorLoadOpClears += loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->colorLoadOpLoads += loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->colorLoadOpNones += loadOp == VK_ATTACHMENT_LOAD_OP_NONE_EXT ? 1 : 0;
countersOut->colorStoreOpStores += storeOp == VK_ATTACHMENT_STORE_OP_STORE ? 1 : 0;
countersOut->colorStoreOpNones += storeOp == VK_ATTACHMENT_STORE_OP_NONE_EXT ? 1 : 0;
}
}
void UpdateSubpassColorPerfCounters(const VkRenderPassCreateInfo2 &createInfo,
const VkSubpassDescription2 &subpass,
RenderPassPerfCounters *countersOut)
{
// Color resolve counters.
if (subpass.pResolveAttachments == nullptr)
{
return;
}
for (uint32_t colorSubpassIndex = 0; colorSubpassIndex < subpass.colorAttachmentCount;
++colorSubpassIndex)
{
uint32_t resolveRenderPassIndex = subpass.pResolveAttachments[colorSubpassIndex].attachment;
if (resolveRenderPassIndex == VK_ATTACHMENT_UNUSED)
{
continue;
}
++countersOut->colorAttachmentResolves;
}
}
void UpdateRenderPassDepthStencilPerfCounters(const VkRenderPassCreateInfo2 &createInfo,
size_t renderPassIndex,
RenderPassPerfCounters *countersOut)
{
ASSERT(renderPassIndex != VK_ATTACHMENT_UNUSED);
// Depth/stencil ops counters.
const VkAttachmentDescription2 &ds = createInfo.pAttachments[renderPassIndex];
countersOut->depthLoadOpClears += ds.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->depthLoadOpLoads += ds.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->depthLoadOpNones += ds.loadOp == VK_ATTACHMENT_LOAD_OP_NONE_EXT ? 1 : 0;
countersOut->depthStoreOpStores += ds.storeOp == VK_ATTACHMENT_STORE_OP_STORE ? 1 : 0;
countersOut->depthStoreOpNones += ds.storeOp == VK_ATTACHMENT_STORE_OP_NONE_EXT ? 1 : 0;
countersOut->stencilLoadOpClears += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->stencilLoadOpLoads += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->stencilLoadOpNones += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_NONE_EXT ? 1 : 0;
countersOut->stencilStoreOpStores += ds.stencilStoreOp == VK_ATTACHMENT_STORE_OP_STORE ? 1 : 0;
countersOut->stencilStoreOpNones +=
ds.stencilStoreOp == VK_ATTACHMENT_STORE_OP_NONE_EXT ? 1 : 0;
// Depth/stencil read-only mode.
countersOut->readOnlyDepthStencil +=
ds.finalLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL ? 1 : 0;
}
void UpdateRenderPassDepthStencilResolvePerfCounters(
const VkRenderPassCreateInfo2 &createInfo,
const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve,
RenderPassPerfCounters *countersOut)
{
if (depthStencilResolve.pDepthStencilResolveAttachment == nullptr)
{
return;
}
uint32_t resolveRenderPassIndex =
depthStencilResolve.pDepthStencilResolveAttachment->attachment;
if (resolveRenderPassIndex == VK_ATTACHMENT_UNUSED)
{
return;
}
const VkAttachmentDescription2 &dsResolve = createInfo.pAttachments[resolveRenderPassIndex];
// Resolve depth/stencil ops counters.
countersOut->depthLoadOpClears += dsResolve.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->depthLoadOpLoads += dsResolve.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->depthStoreOpStores +=
dsResolve.storeOp == static_cast<uint16_t>(RenderPassStoreOp::Store) ? 1 : 0;
countersOut->stencilLoadOpClears +=
dsResolve.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->stencilLoadOpLoads +=
dsResolve.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->stencilStoreOpStores +=
dsResolve.stencilStoreOp == static_cast<uint16_t>(RenderPassStoreOp::Store) ? 1 : 0;
// Depth/stencil resolve counters.
countersOut->depthAttachmentResolves +=
depthStencilResolve.depthResolveMode != VK_RESOLVE_MODE_NONE ? 1 : 0;
countersOut->stencilAttachmentResolves +=
depthStencilResolve.stencilResolveMode != VK_RESOLVE_MODE_NONE ? 1 : 0;
}
void UpdateRenderPassPerfCounters(
const RenderPassDesc &desc,
const VkRenderPassCreateInfo2 &createInfo,
const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve,
RenderPassPerfCounters *countersOut)
{
// Accumulate depth/stencil attachment indices in all subpasses to avoid double-counting
// counters.
FramebufferAttachmentMask depthStencilAttachmentIndices;
for (uint32_t subpassIndex = 0; subpassIndex < createInfo.subpassCount; ++subpassIndex)
{
const VkSubpassDescription2 &subpass = createInfo.pSubpasses[subpassIndex];
// Color counters.
// NOTE: For simplicity, this will accumulate counts for all subpasses in the renderpass.
UpdateSubpassColorPerfCounters(createInfo, subpass, countersOut);
// Record index of depth/stencil attachment.
if (subpass.pDepthStencilAttachment != nullptr)
{
uint32_t attachmentRenderPassIndex = subpass.pDepthStencilAttachment->attachment;
if (attachmentRenderPassIndex != VK_ATTACHMENT_UNUSED)
{
depthStencilAttachmentIndices.set(attachmentRenderPassIndex);
}
}
}
UpdateRenderPassColorPerfCounters(createInfo, depthStencilAttachmentIndices, countersOut);
// Depth/stencil counters. Currently, both subpasses use the same depth/stencil attachment (if
// any).
ASSERT(depthStencilAttachmentIndices.count() <= 1);
for (size_t attachmentRenderPassIndex : depthStencilAttachmentIndices)
{
UpdateRenderPassDepthStencilPerfCounters(createInfo, attachmentRenderPassIndex,
countersOut);
}
UpdateRenderPassDepthStencilResolvePerfCounters(createInfo, depthStencilResolve, countersOut);
// Determine unresolve counters from the render pass desc, to avoid making guesses from subpass
// count etc.
countersOut->colorAttachmentUnresolves += desc.getColorUnresolveAttachmentMask().count();
countersOut->depthAttachmentUnresolves += desc.hasDepthUnresolveAttachment() ? 1 : 0;
countersOut->stencilAttachmentUnresolves += desc.hasStencilUnresolveAttachment() ? 1 : 0;
}
void GetRenderPassAndUpdateCounters(ContextVk *contextVk,
bool updatePerfCounters,
RenderPassHelper *renderPassHelper,
const RenderPass **renderPassOut)
{
*renderPassOut = &renderPassHelper->getRenderPass();
if (updatePerfCounters)
{
angle::VulkanPerfCounters &counters = contextVk->getPerfCounters();
const RenderPassPerfCounters &rpCounters = renderPassHelper->getPerfCounters();
counters.colorLoadOpClears += rpCounters.colorLoadOpClears;
counters.colorLoadOpLoads += rpCounters.colorLoadOpLoads;
counters.colorLoadOpNones += rpCounters.colorLoadOpNones;
counters.colorStoreOpStores += rpCounters.colorStoreOpStores;
counters.colorStoreOpNones += rpCounters.colorStoreOpNones;
counters.depthLoadOpClears += rpCounters.depthLoadOpClears;
counters.depthLoadOpLoads += rpCounters.depthLoadOpLoads;
counters.depthLoadOpNones += rpCounters.depthLoadOpNones;
counters.depthStoreOpStores += rpCounters.depthStoreOpStores;
counters.depthStoreOpNones += rpCounters.depthStoreOpNones;
counters.stencilLoadOpClears += rpCounters.stencilLoadOpClears;
counters.stencilLoadOpLoads += rpCounters.stencilLoadOpLoads;
counters.stencilLoadOpNones += rpCounters.stencilLoadOpNones;
counters.stencilStoreOpStores += rpCounters.stencilStoreOpStores;
counters.stencilStoreOpNones += rpCounters.stencilStoreOpNones;
counters.colorAttachmentUnresolves += rpCounters.colorAttachmentUnresolves;
counters.colorAttachmentResolves += rpCounters.colorAttachmentResolves;
counters.depthAttachmentUnresolves += rpCounters.depthAttachmentUnresolves;
counters.depthAttachmentResolves += rpCounters.depthAttachmentResolves;
counters.stencilAttachmentUnresolves += rpCounters.stencilAttachmentUnresolves;
counters.stencilAttachmentResolves += rpCounters.stencilAttachmentResolves;
counters.readOnlyDepthStencilRenderPasses += rpCounters.readOnlyDepthStencil;
}
}
void InitializeSpecializationInfo(
const SpecializationConstants &specConsts,
SpecializationConstantMap<VkSpecializationMapEntry> *specializationEntriesOut,
VkSpecializationInfo *specializationInfoOut)
{
// Collect specialization constants.
for (const sh::vk::SpecializationConstantId id :
angle::AllEnums<sh::vk::SpecializationConstantId>())
{
(*specializationEntriesOut)[id].constantID = static_cast<uint32_t>(id);
switch (id)
{
case sh::vk::SpecializationConstantId::SurfaceRotation:
(*specializationEntriesOut)[id].offset =
offsetof(SpecializationConstants, surfaceRotation);
(*specializationEntriesOut)[id].size = sizeof(specConsts.surfaceRotation);
break;
case sh::vk::SpecializationConstantId::Dither:
(*specializationEntriesOut)[id].offset =
offsetof(vk::SpecializationConstants, dither);
(*specializationEntriesOut)[id].size = sizeof(specConsts.dither);
break;
default:
UNREACHABLE();
break;
}
}
specializationInfoOut->mapEntryCount = static_cast<uint32_t>(specializationEntriesOut->size());
specializationInfoOut->pMapEntries = specializationEntriesOut->data();
specializationInfoOut->dataSize = sizeof(specConsts);
specializationInfoOut->pData = &specConsts;
}
// Utility for setting a value on a packed 4-bit integer array.
template <typename SrcT>
void Int4Array_Set(uint8_t *arrayBytes, uint32_t arrayIndex, SrcT value)
{
uint32_t byteIndex = arrayIndex >> 1;
ASSERT(value < 16);
if ((arrayIndex & 1) == 0)
{
arrayBytes[byteIndex] &= 0xF0;
arrayBytes[byteIndex] |= static_cast<uint8_t>(value);
}
else
{
arrayBytes[byteIndex] &= 0x0F;
arrayBytes[byteIndex] |= static_cast<uint8_t>(value) << 4;
}
}
// Utility for getting a value from a packed 4-bit integer array.
template <typename DestT>
DestT Int4Array_Get(const uint8_t *arrayBytes, uint32_t arrayIndex)
{
uint32_t byteIndex = arrayIndex >> 1;
if ((arrayIndex & 1) == 0)
{
return static_cast<DestT>(arrayBytes[byteIndex] & 0xF);
}
else
{
return static_cast<DestT>(arrayBytes[byteIndex] >> 4);
}
}
// When converting a byte number to a transition bit index we can shift instead of divide.
constexpr size_t kTransitionByteShift = Log2(kGraphicsPipelineDirtyBitBytes);
// When converting a number of bits offset to a transition bit index we can also shift.
constexpr size_t kBitsPerByte = 8;
constexpr size_t kTransitionBitShift = kTransitionByteShift + Log2(kBitsPerByte);
// Helper macro to map from a PipelineDesc struct and field to a dirty bit index.
// Uses the 'offsetof' macro to compute the offset 'Member' within the PipelineDesc.
// We can optimize the dirty bit setting by computing the shifted dirty bit at compile time instead
// of calling "set".
#define ANGLE_GET_TRANSITION_BIT(Member) \
(offsetof(GraphicsPipelineDesc, Member) >> kTransitionByteShift)
// Indexed dirty bits cannot be entirely computed at compile time since the index is passed to
// the update function.
#define ANGLE_GET_INDEXED_TRANSITION_BIT(Member, Index, BitWidth) \
(((BitWidth * Index) >> kTransitionBitShift) + ANGLE_GET_TRANSITION_BIT(Member))
constexpr char kDescriptorTypeNameMap[][30] = {"sampler",
"combined image sampler",
"sampled image",
"storage image",
"uniform texel buffer",
"storage texel buffer",
"uniform buffer",
"storage buffer",
"uniform buffer dynamic",
"storage buffer dynamic",
"input attachment"};
// Helpers for creating a readable dump of the graphics pipeline graph. Each program generates a
// group of nodes. The group's description is the common state among all nodes. Each node contains
// the diff with the shared state. Arrows between nodes indicate the GraphicsPipelineTransitionBits
// that have caused the transition. State that is 0 is not output for brevity.
enum class PipelineState
{
VertexAttribFormat,
VertexAttribDivisor = VertexAttribFormat + gl::MAX_VERTEX_ATTRIBS,
VertexAttribOffset = VertexAttribDivisor + gl::MAX_VERTEX_ATTRIBS,
VertexAttribStride = VertexAttribOffset + gl::MAX_VERTEX_ATTRIBS,
VertexAttribCompressed = VertexAttribStride + gl::MAX_VERTEX_ATTRIBS,
VertexAttribShaderComponentType = VertexAttribCompressed + gl::MAX_VERTEX_ATTRIBS,
RenderPassSamples = VertexAttribShaderComponentType + gl::MAX_VERTEX_ATTRIBS,
RenderPassColorAttachmentRange,
RenderPassViewCount,
RenderPassSrgbWriteControl,
RenderPassHasFramebufferFetch,
RenderPassIsRenderToTexture,
RenderPassResolveDepth,
RenderPassResolveStencil,
RenderPassUnresolveDepth,
RenderPassUnresolveStencil,
RenderPassColorResolveMask,
RenderPassColorUnresolveMask,
RenderPassColorFormat,
RenderPassDepthStencilFormat = RenderPassColorFormat + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
Subpass,
Topology,
PatchVertices,
PrimitiveRestartEnable,
PolygonMode,
CullMode,
FrontFace,
SurfaceRotation,
ViewportNegativeOneToOne,
SampleShadingEnable,
RasterizationSamples,
MinSampleShading,
SampleMask,
AlphaToCoverageEnable,
AlphaToOneEnable,
LogicOpEnable,
LogicOp,
RasterizerDiscardEnable,
ColorWriteMask,
BlendEnableMask = ColorWriteMask + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
MissingOutputsMask,
SrcColorBlendFactor,
DstColorBlendFactor = SrcColorBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
ColorBlendOp = DstColorBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
SrcAlphaBlendFactor = ColorBlendOp + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
DstAlphaBlendFactor = SrcAlphaBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
AlphaBlendOp = DstAlphaBlendFactor + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
EmulatedDitherControl = AlphaBlendOp + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS,
DepthClampEnable,
DepthBoundsTest,
DepthCompareOp,
DepthTest,
DepthWrite,
StencilTest,
DepthBiasEnable,
StencilOpFailFront,
StencilOpPassFront,
StencilOpDepthFailFront,
StencilCompareFront,
StencilOpFailBack,
StencilOpPassBack,
StencilOpDepthFailBack,
StencilCompareBack,
InvalidEnum,
EnumCount = InvalidEnum,
};
using UnpackedPipelineState = angle::PackedEnumMap<PipelineState, uint32_t>;
using PipelineStateBitSet = angle::BitSetArray<angle::EnumSize<PipelineState>()>;
[[maybe_unused]] void UnpackPipelineState(const GraphicsPipelineDesc &state,
GraphicsPipelineSubset subset,
UnpackedPipelineState *valuesOut)
{
const bool hasVertexInput = GraphicsPipelineHasVertexInput(subset);
const bool hasShaders = GraphicsPipelineHasShaders(subset);
const bool hasShadersOrFragmentOutput = GraphicsPipelineHasShadersOrFragmentOutput(subset);
const bool hasFragmentOutput = GraphicsPipelineHasFragmentOutput(subset);
valuesOut->fill(0);
if (hasVertexInput)
{
const PipelineVertexInputState &vertexInputState = state.getVertexInputStateForLog();
const PackedVertexInputAttributes &vertex = vertexInputState.vertex;
uint32_t *vaFormats = &(*valuesOut)[PipelineState::VertexAttribFormat];
uint32_t *vaDivisors = &(*valuesOut)[PipelineState::VertexAttribDivisor];
uint32_t *vaOffsets = &(*valuesOut)[PipelineState::VertexAttribOffset];
uint32_t *vaStrides = &(*valuesOut)[PipelineState::VertexAttribStride];
uint32_t *vaCompressed = &(*valuesOut)[PipelineState::VertexAttribCompressed];
uint32_t *vaShaderComponentType =
&(*valuesOut)[PipelineState::VertexAttribShaderComponentType];
for (uint32_t attribIndex = 0; attribIndex < gl::MAX_VERTEX_ATTRIBS; ++attribIndex)
{
vaFormats[attribIndex] = vertex.attribs[attribIndex].format;
vaDivisors[attribIndex] = vertex.attribs[attribIndex].divisor;
vaOffsets[attribIndex] = vertex.attribs[attribIndex].offset;
vaStrides[attribIndex] = vertex.strides[attribIndex];
vaCompressed[attribIndex] = vertex.attribs[attribIndex].compressed;
gl::ComponentType componentType = gl::GetComponentTypeMask(
gl::ComponentTypeMask(vertex.shaderAttribComponentType), attribIndex);
vaShaderComponentType[attribIndex] = componentType == gl::ComponentType::InvalidEnum
? 0
: static_cast<uint32_t>(componentType);
}
const PackedInputAssemblyState &inputAssembly = vertexInputState.inputAssembly;
(*valuesOut)[PipelineState::Topology] = inputAssembly.bits.topology;
(*valuesOut)[PipelineState::PrimitiveRestartEnable] =
inputAssembly.bits.primitiveRestartEnable;
}
if (hasShaders)
{
const PipelineShadersState &shadersState = state.getShadersStateForLog();
const PackedPreRasterizationAndFragmentStates &shaders = shadersState.shaders;
(*valuesOut)[PipelineState::ViewportNegativeOneToOne] =
shaders.bits.viewportNegativeOneToOne;
(*valuesOut)[PipelineState::DepthClampEnable] = shaders.bits.depthClampEnable;
(*valuesOut)[PipelineState::PolygonMode] = shaders.bits.polygonMode;
(*valuesOut)[PipelineState::CullMode] = shaders.bits.cullMode;
(*valuesOut)[PipelineState::FrontFace] = shaders.bits.frontFace;
(*valuesOut)[PipelineState::RasterizerDiscardEnable] = shaders.bits.rasterizerDiscardEnable;
(*valuesOut)[PipelineState::DepthBiasEnable] = shaders.bits.depthBiasEnable;
(*valuesOut)[PipelineState::PatchVertices] = shaders.bits.patchVertices;
(*valuesOut)[PipelineState::DepthBoundsTest] = shaders.bits.depthBoundsTest;
(*valuesOut)[PipelineState::DepthTest] = shaders.bits.depthTest;
(*valuesOut)[PipelineState::DepthWrite] = shaders.bits.depthWrite;
(*valuesOut)[PipelineState::StencilTest] = shaders.bits.stencilTest;
(*valuesOut)[PipelineState::DepthCompareOp] = shaders.bits.depthCompareOp;
(*valuesOut)[PipelineState::SurfaceRotation] = shaders.bits.surfaceRotation;
(*valuesOut)[PipelineState::EmulatedDitherControl] = shaders.emulatedDitherControl;
(*valuesOut)[PipelineState::StencilOpFailFront] = shaders.front.fail;
(*valuesOut)[PipelineState::StencilOpPassFront] = shaders.front.pass;
(*valuesOut)[PipelineState::StencilOpDepthFailFront] = shaders.front.depthFail;
(*valuesOut)[PipelineState::StencilCompareFront] = shaders.front.compare;
(*valuesOut)[PipelineState::StencilOpFailBack] = shaders.back.fail;
(*valuesOut)[PipelineState::StencilOpPassBack] = shaders.back.pass;
(*valuesOut)[PipelineState::StencilOpDepthFailBack] = shaders.back.depthFail;
(*valuesOut)[PipelineState::StencilCompareBack] = shaders.back.compare;
}
if (hasShadersOrFragmentOutput)
{
const PipelineSharedNonVertexInputState &sharedNonVertexInputState =
state.getSharedNonVertexInputStateForLog();
const PackedMultisampleAndSubpassState &multisample = sharedNonVertexInputState.multisample;
(*valuesOut)[PipelineState::SampleMask] = multisample.bits.sampleMask;
(*valuesOut)[PipelineState::RasterizationSamples] =
multisample.bits.rasterizationSamplesMinusOne + 1;
(*valuesOut)[PipelineState::SampleShadingEnable] = multisample.bits.sampleShadingEnable;
(*valuesOut)[PipelineState::AlphaToCoverageEnable] = multisample.bits.alphaToCoverageEnable;
(*valuesOut)[PipelineState::AlphaToOneEnable] = multisample.bits.alphaToOneEnable;
(*valuesOut)[PipelineState::Subpass] = multisample.bits.subpass;
(*valuesOut)[PipelineState::MinSampleShading] = multisample.bits.minSampleShading;
const RenderPassDesc renderPass = sharedNonVertexInputState.renderPass;
(*valuesOut)[PipelineState::RenderPassSamples] = renderPass.samples();
(*valuesOut)[PipelineState::RenderPassColorAttachmentRange] =
static_cast<uint32_t>(renderPass.colorAttachmentRange());
(*valuesOut)[PipelineState::RenderPassViewCount] = renderPass.viewCount();
(*valuesOut)[PipelineState::RenderPassSrgbWriteControl] =
static_cast<uint32_t>(renderPass.getSRGBWriteControlMode());
(*valuesOut)[PipelineState::RenderPassHasFramebufferFetch] =
renderPass.hasFramebufferFetch();
(*valuesOut)[PipelineState::RenderPassIsRenderToTexture] = renderPass.isRenderToTexture();
(*valuesOut)[PipelineState::RenderPassResolveDepth] =
renderPass.hasDepthResolveAttachment();
(*valuesOut)[PipelineState::RenderPassResolveStencil] =
renderPass.hasStencilResolveAttachment();
(*valuesOut)[PipelineState::RenderPassUnresolveDepth] =
renderPass.hasDepthUnresolveAttachment();
(*valuesOut)[PipelineState::RenderPassUnresolveStencil] =
renderPass.hasStencilUnresolveAttachment();
(*valuesOut)[PipelineState::RenderPassColorResolveMask] =
renderPass.getColorResolveAttachmentMask().bits();
(*valuesOut)[PipelineState::RenderPassColorUnresolveMask] =
renderPass.getColorUnresolveAttachmentMask().bits();
uint32_t *colorFormats = &(*valuesOut)[PipelineState::RenderPassColorFormat];
for (uint32_t colorIndex = 0; colorIndex < renderPass.colorAttachmentRange(); ++colorIndex)
{
colorFormats[colorIndex] = static_cast<uint32_t>(renderPass[colorIndex]);
}
(*valuesOut)[PipelineState::RenderPassDepthStencilFormat] =
static_cast<uint32_t>(renderPass[renderPass.depthStencilAttachmentIndex()]);
}
if (hasFragmentOutput)
{
const PipelineFragmentOutputState &fragmentOutputState =
state.getFragmentOutputStateForLog();
const PackedColorBlendState &blend = fragmentOutputState.blend;
uint32_t *colorWriteMasks = &(*valuesOut)[PipelineState::ColorWriteMask];
uint32_t *srcColorBlendFactors = &(*valuesOut)[PipelineState::SrcColorBlendFactor];
uint32_t *dstColorBlendFactors = &(*valuesOut)[PipelineState::DstColorBlendFactor];
uint32_t *colorBlendOps = &(*valuesOut)[PipelineState::ColorBlendOp];
uint32_t *srcAlphaBlendFactors = &(*valuesOut)[PipelineState::SrcAlphaBlendFactor];
uint32_t *dstAlphaBlendFactors = &(*valuesOut)[PipelineState::DstAlphaBlendFactor];
uint32_t *alphaBlendOps = &(*valuesOut)[PipelineState::AlphaBlendOp];
for (uint32_t colorIndex = 0; colorIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
++colorIndex)
{
colorWriteMasks[colorIndex] =
Int4Array_Get<VkColorComponentFlags>(blend.colorWriteMaskBits, colorIndex);
srcColorBlendFactors[colorIndex] = blend.attachments[colorIndex].srcColorBlendFactor;
dstColorBlendFactors[colorIndex] = blend.attachments[colorIndex].dstColorBlendFactor;
colorBlendOps[colorIndex] = blend.attachments[colorIndex].colorBlendOp;
srcAlphaBlendFactors[colorIndex] = blend.attachments[colorIndex].srcAlphaBlendFactor;
dstAlphaBlendFactors[colorIndex] = blend.attachments[colorIndex].dstAlphaBlendFactor;
alphaBlendOps[colorIndex] = blend.attachments[colorIndex].alphaBlendOp;
}
const PackedBlendMaskAndLogicOpState &blendMaskAndLogic =
fragmentOutputState.blendMaskAndLogic;
(*valuesOut)[PipelineState::BlendEnableMask] = blendMaskAndLogic.bits.blendEnableMask;
(*valuesOut)[PipelineState::LogicOpEnable] = blendMaskAndLogic.bits.logicOpEnable;
(*valuesOut)[PipelineState::LogicOp] = blendMaskAndLogic.bits.logicOp;
(*valuesOut)[PipelineState::MissingOutputsMask] = blendMaskAndLogic.bits.missingOutputsMask;
}
}
[[maybe_unused]] PipelineStateBitSet GetCommonPipelineState(
const std::vector<UnpackedPipelineState> &pipelines)
{
PipelineStateBitSet commonState;
commonState.set();
ASSERT(!pipelines.empty());
const UnpackedPipelineState &firstPipeline = pipelines[0];
for (const UnpackedPipelineState &pipeline : pipelines)
{
for (size_t stateIndex = 0; stateIndex < firstPipeline.size(); ++stateIndex)
{
if (pipeline.data()[stateIndex] != firstPipeline.data()[stateIndex])
{
commonState.reset(stateIndex);
}
}
}
return commonState;
}
bool IsPipelineState(size_t stateIndex, PipelineState pipelineState, size_t range)
{
size_t pipelineStateAsInt = static_cast<size_t>(pipelineState);
return stateIndex >= pipelineStateAsInt && stateIndex < pipelineStateAsInt + range;
}
size_t GetPipelineStateSubIndex(size_t stateIndex, PipelineState pipelineState)
{
return stateIndex - static_cast<size_t>(pipelineState);
}
PipelineState GetPipelineState(size_t stateIndex, bool *isRangedOut, size_t *subIndexOut)
{
constexpr PipelineState kRangedStates[] = {
PipelineState::VertexAttribFormat, PipelineState::VertexAttribDivisor,
PipelineState::VertexAttribOffset, PipelineState::VertexAttribStride,
PipelineState::VertexAttribCompressed, PipelineState::VertexAttribShaderComponentType,
PipelineState::RenderPassColorFormat, PipelineState::ColorWriteMask,
PipelineState::SrcColorBlendFactor, PipelineState::DstColorBlendFactor,
PipelineState::ColorBlendOp, PipelineState::SrcAlphaBlendFactor,
PipelineState::DstAlphaBlendFactor, PipelineState::AlphaBlendOp,
};
for (PipelineState ps : kRangedStates)
{
size_t range;
switch (ps)
{
case PipelineState::VertexAttribFormat:
case PipelineState::VertexAttribDivisor:
case PipelineState::VertexAttribOffset:
case PipelineState::VertexAttribStride:
case PipelineState::VertexAttribCompressed:
case PipelineState::VertexAttribShaderComponentType:
range = gl::MAX_VERTEX_ATTRIBS;
break;
default:
range = gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
break;
}
if (IsPipelineState(stateIndex, ps, range))
{
*subIndexOut = GetPipelineStateSubIndex(stateIndex, ps);
*isRangedOut = true;
return ps;
}
}
*isRangedOut = false;
return static_cast<PipelineState>(stateIndex);
}
[[maybe_unused]] void OutputPipelineState(std::ostream &out, size_t stateIndex, uint32_t state)
{
size_t subIndex = 0;
bool isRanged = false;
PipelineState pipelineState = GetPipelineState(stateIndex, &isRanged, &subIndex);
constexpr angle::PackedEnumMap<PipelineState, const char *> kStateNames = {{
{PipelineState::VertexAttribFormat, "va_format"},
{PipelineState::VertexAttribDivisor, "va_divisor"},
{PipelineState::VertexAttribOffset, "va_offset"},
{PipelineState::VertexAttribStride, "va_stride"},
{PipelineState::VertexAttribCompressed, "va_compressed"},
{PipelineState::VertexAttribShaderComponentType, "va_shader_component_type"},
{PipelineState::RenderPassSamples, "rp_samples"},
{PipelineState::RenderPassColorAttachmentRange, "rp_color_range"},
{PipelineState::RenderPassViewCount, "rp_views"},
{PipelineState::RenderPassSrgbWriteControl, "rp_srgb"},
{PipelineState::RenderPassHasFramebufferFetch, "rp_has_framebuffer_fetch"},
{PipelineState::RenderPassIsRenderToTexture, "rp_is_msrtt"},
{PipelineState::RenderPassResolveDepth, "rp_resolve_depth"},
{PipelineState::RenderPassResolveStencil, "rp_resolve_stencil"},
{PipelineState::RenderPassUnresolveDepth, "rp_unresolve_depth"},
{PipelineState::RenderPassUnresolveStencil, "rp_unresolve_stencil"},
{PipelineState::RenderPassColorResolveMask, "rp_resolve_color"},
{PipelineState::RenderPassColorUnresolveMask, "rp_unresolve_color"},
{PipelineState::RenderPassColorFormat, "rp_color"},
{PipelineState::RenderPassDepthStencilFormat, "rp_depth_stencil"},
{PipelineState::Subpass, "subpass"},
{PipelineState::Topology, "topology"},
{PipelineState::PatchVertices, "patch_vertices"},
{PipelineState::PrimitiveRestartEnable, "primitive_restart"},
{PipelineState::PolygonMode, "polygon_mode"},
{PipelineState::CullMode, "cull_mode"},
{PipelineState::FrontFace, "front_face"},
{PipelineState::SurfaceRotation, "rotated_surface"},
{PipelineState::ViewportNegativeOneToOne, "viewport_depth_[-1,1]"},
{PipelineState::SampleShadingEnable, "sample_shading"},
{PipelineState::RasterizationSamples, "rasterization_samples"},
{PipelineState::MinSampleShading, "min_sample_shading"},
{PipelineState::SampleMask, "sample_mask"},
{PipelineState::AlphaToCoverageEnable, "alpha_to_coverage"},
{PipelineState::AlphaToOneEnable, "alpha_to_one"},
{PipelineState::LogicOpEnable, "logic_op_enable"},
{PipelineState::LogicOp, "logic_op"},
{PipelineState::RasterizerDiscardEnable, "rasterization_discard"},
{PipelineState::ColorWriteMask, "color_write"},
{PipelineState::BlendEnableMask, "blend_mask"},
{PipelineState::MissingOutputsMask, "missing_outputs_mask"},
{PipelineState::SrcColorBlendFactor, "src_color_blend"},
{PipelineState::DstColorBlendFactor, "dst_color_blend"},
{PipelineState::ColorBlendOp, "color_blend"},
{PipelineState::SrcAlphaBlendFactor, "src_alpha_blend"},
{PipelineState::DstAlphaBlendFactor, "dst_alpha_blend"},
{PipelineState::AlphaBlendOp, "alpha_blend"},
{PipelineState::EmulatedDitherControl, "dither"},
{PipelineState::DepthClampEnable, "depth_clamp"},
{PipelineState::DepthBoundsTest, "depth_bounds_test"},
{PipelineState::DepthCompareOp, "depth_compare"},
{PipelineState::DepthTest, "depth_test"},
{PipelineState::DepthWrite, "depth_write"},
{PipelineState::StencilTest, "stencil_test"},
{PipelineState::DepthBiasEnable, "depth_bias"},
{PipelineState::StencilOpFailFront, "stencil_front_fail"},
{PipelineState::StencilOpPassFront, "stencil_front_pass"},
{PipelineState::StencilOpDepthFailFront, "stencil_front_depth_fail"},
{PipelineState::StencilCompareFront, "stencil_front_compare"},
{PipelineState::StencilOpFailBack, "stencil_back_fail"},
{PipelineState::StencilOpPassBack, "stencil_back_pass"},
{PipelineState::StencilOpDepthFailBack, "stencil_back_depth_fail"},
{PipelineState::StencilCompareBack, "stencil_back_compare"},
}};
out << kStateNames[pipelineState];
if (isRanged)
{
out << "_" << subIndex;
}
switch (pipelineState)
{
// Given that state == 0 produces no output, binary state doesn't require anything but
// its name specified, as it being enabled would be implied.
case PipelineState::VertexAttribCompressed:
case PipelineState::RenderPassSrgbWriteControl:
case PipelineState::RenderPassHasFramebufferFetch:
case PipelineState::RenderPassIsRenderToTexture:
case PipelineState::RenderPassResolveDepth:
case PipelineState::RenderPassResolveStencil:
case PipelineState::RenderPassUnresolveDepth:
case PipelineState::RenderPassUnresolveStencil:
case PipelineState::PrimitiveRestartEnable:
case PipelineState::SurfaceRotation:
case PipelineState::ViewportNegativeOneToOne:
case PipelineState::SampleShadingEnable:
case PipelineState::AlphaToCoverageEnable:
case PipelineState::AlphaToOneEnable:
case PipelineState::LogicOpEnable:
case PipelineState::RasterizerDiscardEnable:
case PipelineState::DepthClampEnable:
case PipelineState::DepthBoundsTest:
case PipelineState::DepthTest:
case PipelineState::DepthWrite:
case PipelineState::StencilTest:
case PipelineState::DepthBiasEnable:
break;
// Special formatting for some state
case PipelineState::VertexAttribShaderComponentType:
out << "=";
switch (state)
{
case 0:
static_assert(static_cast<uint32_t>(gl::ComponentType::Float) == 0);
out << "float";
break;
case 1:
static_assert(static_cast<uint32_t>(gl::ComponentType::Int) == 1);
out << "int";
break;
case 2:
static_assert(static_cast<uint32_t>(gl::ComponentType::UnsignedInt) == 2);
out << "uint";
break;
case 3:
static_assert(static_cast<uint32_t>(gl::ComponentType::NoType) == 3);
out << "none";
break;
default:
UNREACHABLE();
}
break;
case PipelineState::Topology:
out << "=";
switch (state)
{
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
out << "points";
break;
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
out << "lines";
break;
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
out << "line_strip";
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
out << "tris";
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
out << "tri_strip";
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
out << "tri_fan";
break;
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
out << "lines_with_adj";
break;
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
out << "line_strip_with_adj";
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
out << "tris_with_adj";
break;
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
out << "tri_strip_with_adj";
break;
case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
out << "patches";
break;
default:
UNREACHABLE();
}
break;
case PipelineState::PolygonMode:
out << "=";
switch (state)
{
case VK_POLYGON_MODE_FILL:
out << "fill";
break;
case VK_POLYGON_MODE_LINE:
out << "line";
break;
case VK_POLYGON_MODE_POINT:
out << "point";
break;
default:
UNREACHABLE();
}
break;
case PipelineState::CullMode:
out << "=";
if ((state & VK_CULL_MODE_FRONT_BIT) != 0)
{
out << "front";
}
if (state == VK_CULL_MODE_FRONT_AND_BACK)
{
out << "+";
}
if ((state & VK_CULL_MODE_BACK_BIT) != 0)
{
out << "back";
}
break;
case PipelineState::FrontFace:
out << "=" << (state == VK_FRONT_FACE_COUNTER_CLOCKWISE ? "ccw" : "cw");
break;
case PipelineState::MinSampleShading:
out << "=" << (static_cast<float>(state) / kMinSampleShadingScale);
break;
case PipelineState::SrcColorBlendFactor:
case PipelineState::DstColorBlendFactor:
case PipelineState::SrcAlphaBlendFactor:
case PipelineState::DstAlphaBlendFactor:
out << "=";
switch (state)
{
case VK_BLEND_FACTOR_ZERO:
out << "0";
break;
case VK_BLEND_FACTOR_ONE:
out << "1";
break;
case VK_BLEND_FACTOR_SRC_COLOR:
out << "sc";
break;
case VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR:
out << "1-sc";
break;
case VK_BLEND_FACTOR_DST_COLOR:
out << "dc";
break;
case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR:
out << "1-dc";
break;
case VK_BLEND_FACTOR_SRC_ALPHA:
out << "sa";
break;
case VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA:
out << "1-sa";
break;
case VK_BLEND_FACTOR_DST_ALPHA:
out << "da";
break;
case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA:
out << "1-da";
break;
case VK_BLEND_FACTOR_CONSTANT_COLOR:
out << "const_color";
break;
case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR:
out << "1-const_color";
break;
case VK_BLEND_FACTOR_CONSTANT_ALPHA:
out << "const_alpha";
break;
case VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA:
out << "1-const_alpha";
break;
case VK_BLEND_FACTOR_SRC_ALPHA_SATURATE:
out << "sat(sa)";
break;
case VK_BLEND_FACTOR_SRC1_COLOR:
out << "sc1";
break;
case VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR:
out << "1-sc1";
break;
case VK_BLEND_FACTOR_SRC1_ALPHA:
out << "sa1";
break;
case VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA:
out << "1-sa1";
break;
default:
UNREACHABLE();
}
break;
case PipelineState::ColorBlendOp:
case PipelineState::AlphaBlendOp:
out << "=";
switch (UnpackBlendOp(static_cast<uint8_t>(state)))
{
case VK_BLEND_OP_ADD:
out << "add";
break;
case VK_BLEND_OP_SUBTRACT:
out << "sub";
break;
case VK_BLEND_OP_REVERSE_SUBTRACT:
out << "reverse_sub";
break;
case VK_BLEND_OP_MIN:
out << "min";
break;
case VK_BLEND_OP_MAX:
out << "max";
break;
case VK_BLEND_OP_MULTIPLY_EXT:
out << "multiply";
break;
case VK_BLEND_OP_SCREEN_EXT:
out << "screen";
break;
case VK_BLEND_OP_OVERLAY_EXT:
out << "overlay";
break;
case VK_BLEND_OP_DARKEN_EXT:
out << "darken";
break;
case VK_BLEND_OP_LIGHTEN_EXT:
out << "lighten";
break;
case VK_BLEND_OP_COLORDODGE_EXT:
out << "dodge";
break;
case VK_BLEND_OP_COLORBURN_EXT:
out << "burn";
break;
case VK_BLEND_OP_HARDLIGHT_EXT:
out << "hardlight";
break;
case VK_BLEND_OP_SOFTLIGHT_EXT:
out << "softlight";
break;
case VK_BLEND_OP_DIFFERENCE_EXT:
out << "difference";
break;
case VK_BLEND_OP_EXCLUSION_EXT:
out << "exclusion";
break;
case VK_BLEND_OP_HSL_HUE_EXT:
out << "hsl_hue";
break;
case VK_BLEND_OP_HSL_SATURATION_EXT:
out << "hsl_sat";
break;
case VK_BLEND_OP_HSL_COLOR_EXT:
out << "hsl_color";
break;
case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
out << "hsl_lum";
break;
default:
UNREACHABLE();
}
break;
case PipelineState::DepthCompareOp:
case PipelineState::StencilCompareFront:
case PipelineState::StencilCompareBack:
out << "=";
switch (state)
{
case VK_COMPARE_OP_NEVER:
out << "never";
break;
case VK_COMPARE_OP_LESS:
out << "'<'";
break;
case VK_COMPARE_OP_EQUAL:
out << "'='";
break;
case VK_COMPARE_OP_LESS_OR_EQUAL:
out << "'<='";
break;
case VK_COMPARE_OP_GREATER:
out << "'>'";
break;
case VK_COMPARE_OP_NOT_EQUAL:
out << "'!='";
break;
case VK_COMPARE_OP_GREATER_OR_EQUAL:
out << "'>='";
break;
case VK_COMPARE_OP_ALWAYS:
out << "always";
break;
default:
UNREACHABLE();
}
break;
case PipelineState::StencilOpFailFront:
case PipelineState::StencilOpPassFront:
case PipelineState::StencilOpDepthFailFront:
case PipelineState::StencilOpFailBack:
case PipelineState::StencilOpPassBack:
case PipelineState::StencilOpDepthFailBack:
out << "=";
switch (state)
{
case VK_STENCIL_OP_KEEP:
out << "keep";
break;
case VK_STENCIL_OP_ZERO:
out << "0";
break;
case VK_STENCIL_OP_REPLACE:
out << "replace";
break;
case VK_STENCIL_OP_INCREMENT_AND_CLAMP:
out << "clamp++";
break;
case VK_STENCIL_OP_DECREMENT_AND_CLAMP:
out << "clamp--";
break;
case VK_STENCIL_OP_INVERT:
out << "'~'";
break;
case VK_STENCIL_OP_INCREMENT_AND_WRAP:
out << "wrap++";
break;
case VK_STENCIL_OP_DECREMENT_AND_WRAP:
out << "wrap--";
break;
default:
UNREACHABLE();
}
break;
// Some state output the value as hex because they are bitmasks
case PipelineState::RenderPassColorResolveMask:
case PipelineState::RenderPassColorUnresolveMask:
case PipelineState::SampleMask:
case PipelineState::ColorWriteMask:
case PipelineState::BlendEnableMask:
case PipelineState::MissingOutputsMask:
case PipelineState::EmulatedDitherControl:
out << "=0x" << std::hex << state << std::dec;
break;
// The rest will simply output the state value
default:
out << "=" << state;
break;
}
out << "\\n";
}
[[maybe_unused]] void OutputAllPipelineState(Context *context,
std::ostream &out,
const UnpackedPipelineState &pipeline,
GraphicsPipelineSubset subset,
const PipelineStateBitSet &include,
bool isCommonState)
{
// Default non-existing state to 0, so they are automatically not output as
// UnpackedPipelineState also sets them to 0.
const bool hasVertexInput = GraphicsPipelineHasVertexInput(subset);
const bool hasShaders = GraphicsPipelineHasShaders(subset);
const bool hasShadersOrFragmentOutput = GraphicsPipelineHasShadersOrFragmentOutput(subset);
const bool hasFragmentOutput = GraphicsPipelineHasFragmentOutput(subset);
const angle::PackedEnumMap<PipelineState, uint32_t> kDefaultState = {{
// Vertex input state
{PipelineState::VertexAttribFormat,
hasVertexInput
? static_cast<uint32_t>(GetCurrentValueFormatID(gl::VertexAttribType::Float))
: 0},
{PipelineState::VertexAttribDivisor, 0},
{PipelineState::VertexAttribOffset, 0},
{PipelineState::VertexAttribStride, 0},
{PipelineState::VertexAttribCompressed, 0},
{PipelineState::VertexAttribShaderComponentType, 0},
{PipelineState::Topology, hasVertexInput ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : 0},
{PipelineState::PrimitiveRestartEnable, 0},
// Shaders state
{PipelineState::ViewportNegativeOneToOne,
hasShaders && context->getFeatures().supportsDepthClipControl.enabled},
{PipelineState::DepthClampEnable, 0},
{PipelineState::PolygonMode, hasShaders ? VK_POLYGON_MODE_FILL : 0},
{PipelineState::CullMode, hasShaders ? VK_CULL_MODE_NONE : 0},
{PipelineState::FrontFace, hasShaders ? VK_FRONT_FACE_COUNTER_CLOCKWISE : 0},
{PipelineState::RasterizerDiscardEnable, 0},
{PipelineState::DepthBiasEnable, 0},
{PipelineState::PatchVertices, hasShaders ? 3 : 0},
{PipelineState::DepthBoundsTest, 0},
{PipelineState::DepthTest, 0},
{PipelineState::DepthWrite, 0},
{PipelineState::StencilTest, 0},
{PipelineState::DepthCompareOp, hasShaders ? VK_COMPARE_OP_LESS : 0},
{PipelineState::SurfaceRotation, 0},
{PipelineState::EmulatedDitherControl, 0},
{PipelineState::StencilOpFailFront, hasShaders ? VK_STENCIL_OP_KEEP : 0},
{PipelineState::StencilOpPassFront, hasShaders ? VK_STENCIL_OP_KEEP : 0},
{PipelineState::StencilOpDepthFailFront, hasShaders ? VK_STENCIL_OP_KEEP : 0},
{PipelineState::StencilCompareFront, hasShaders ? VK_COMPARE_OP_ALWAYS : 0},
{PipelineState::StencilOpFailBack, hasShaders ? VK_STENCIL_OP_KEEP : 0},
{PipelineState::StencilOpPassBack, hasShaders ? VK_STENCIL_OP_KEEP : 0},
{PipelineState::StencilOpDepthFailBack, hasShaders ? VK_STENCIL_OP_KEEP : 0},
{PipelineState::StencilCompareBack, hasShaders ? VK_COMPARE_OP_ALWAYS : 0},
// Shared shaders and fragment output state
{PipelineState::SampleMask,
hasShadersOrFragmentOutput ? std::numeric_limits<uint16_t>::max() : 0},
{PipelineState::RasterizationSamples, hasShadersOrFragmentOutput ? 1 : 0},
{PipelineState::SampleShadingEnable, 0},
{PipelineState::MinSampleShading, hasShadersOrFragmentOutput ? kMinSampleShadingScale : 0},
{PipelineState::AlphaToCoverageEnable, 0},
{PipelineState::AlphaToOneEnable, 0},
{PipelineState::RenderPassSamples, hasShadersOrFragmentOutput ? 1 : 0},
{PipelineState::RenderPassColorAttachmentRange, 0},
{PipelineState::RenderPassViewCount, 0},
{PipelineState::RenderPassSrgbWriteControl, 0},
{PipelineState::RenderPassHasFramebufferFetch, 0},
{PipelineState::RenderPassIsRenderToTexture, 0},
{PipelineState::RenderPassResolveDepth, 0},
{PipelineState::RenderPassResolveStencil, 0},
{PipelineState::RenderPassUnresolveDepth, 0},
{PipelineState::RenderPassUnresolveStencil, 0},
{PipelineState::RenderPassColorResolveMask, 0},
{PipelineState::RenderPassColorUnresolveMask, 0},
{PipelineState::RenderPassColorFormat, 0},
{PipelineState::RenderPassDepthStencilFormat, 0},
{PipelineState::Subpass, 0},
// Fragment output state
{PipelineState::ColorWriteMask, 0},
{PipelineState::SrcColorBlendFactor, hasFragmentOutput ? VK_BLEND_FACTOR_ONE : 0},
{PipelineState::DstColorBlendFactor, hasFragmentOutput ? VK_BLEND_FACTOR_ZERO : 0},
{PipelineState::ColorBlendOp, hasFragmentOutput ? VK_BLEND_OP_ADD : 0},
{PipelineState::SrcAlphaBlendFactor, hasFragmentOutput ? VK_BLEND_FACTOR_ONE : 0},
{PipelineState::DstAlphaBlendFactor, hasFragmentOutput ? VK_BLEND_FACTOR_ZERO : 0},
{PipelineState::AlphaBlendOp, hasFragmentOutput ? VK_BLEND_OP_ADD : 0},
{PipelineState::BlendEnableMask, 0},
{PipelineState::LogicOpEnable, 0},
{PipelineState::LogicOp, hasFragmentOutput ? VK_LOGIC_OP_COPY : 0},
{PipelineState::MissingOutputsMask, 0},
}};
bool anyStateOutput = false;
for (size_t stateIndex : include)
{
size_t subIndex = 0;
bool isRanged = false;
PipelineState pipelineState = GetPipelineState(stateIndex, &isRanged, &subIndex);
const uint32_t state = pipeline.data()[stateIndex];
if (state != kDefaultState[pipelineState])
{
OutputPipelineState(out, stateIndex, state);
anyStateOutput = true;
}
}
if (!isCommonState)
{
out << "(" << (anyStateOutput ? "+" : "") << "common state)\\n";
}
}
template <typename Hash>
void DumpPipelineCacheGraph(
Context *context,
const std::unordered_map<GraphicsPipelineDesc,
PipelineHelper,
Hash,
typename GraphicsPipelineCacheTypeHelper<Hash>::KeyEqual> &cache)
{
constexpr GraphicsPipelineSubset kSubset = GraphicsPipelineCacheTypeHelper<Hash>::kSubset;
std::ostream &out = context->getRenderer()->getPipelineCacheGraphStream();
static std::atomic<uint32_t> sCacheSerial(0);
angle::HashMap<GraphicsPipelineDesc, uint32_t, Hash,
typename GraphicsPipelineCacheTypeHelper<Hash>::KeyEqual>
descToId;
uint32_t cacheSerial = sCacheSerial.fetch_add(1);
uint32_t descId = 0;
// Unpack pipeline states
std::vector<UnpackedPipelineState> pipelines(cache.size());
for (const auto &descAndPipeline : cache)
{
UnpackPipelineState(descAndPipeline.first, kSubset, &pipelines[descId++]);
}
// Extract common state between all pipelines.
PipelineStateBitSet commonState = GetCommonPipelineState(pipelines);
PipelineStateBitSet nodeState = ~commonState;
const char *subsetDescription = "";
const char *subsetTag = "";
switch (kSubset)
{
case GraphicsPipelineSubset::VertexInput:
subsetDescription = "(vertex input)\\n";
subsetTag = "VI_";
break;
case GraphicsPipelineSubset::Shaders:
subsetDescription = "(shaders)\\n";
subsetTag = "S_";
break;
case GraphicsPipelineSubset::FragmentOutput:
subsetDescription = "(fragment output)\\n";
subsetTag = "FO_";
break;
default:
break;
}
out << " subgraph cluster_" << subsetTag << cacheSerial << "{\n";
out << " label=\"Program " << cacheSerial << "\\n"
<< subsetDescription << "\\nCommon state:\\n";
OutputAllPipelineState(context, out, pipelines[0], kSubset, commonState, true);
out << "\";\n";
descId = 0;
for (const auto &descAndPipeline : cache)
{
const GraphicsPipelineDesc &desc = descAndPipeline.first;
const char *style = "";
const char *feedbackDesc = "";
switch (descAndPipeline.second.getCacheLookUpFeedback())
{
case CacheLookUpFeedback::Hit:
// Default is green already
break;
case CacheLookUpFeedback::Miss:
style = "[color=red]";
break;
case CacheLookUpFeedback::LinkedDrawHit:
// Default is green already
style = "[style=dotted]";
feedbackDesc = "(linked)\\n";
break;
case CacheLookUpFeedback::LinkedDrawMiss:
style = "[style=dotted,color=red]";
feedbackDesc = "(linked)\\n";
break;
case CacheLookUpFeedback::WarmUpHit:
// Default is green already
style = "[style=dashed]";
feedbackDesc = "(warm up)\\n";
break;
case CacheLookUpFeedback::WarmUpMiss:
style = "[style=dashed,color=red]";
feedbackDesc = "(warm up)\\n";
break;
case CacheLookUpFeedback::UtilsHit:
style = "[color=yellow]";
feedbackDesc = "(utils)\\n";
break;
case CacheLookUpFeedback::UtilsMiss:
style = "[color=purple]";
feedbackDesc = "(utils)\\n";
break;
default:
// No feedback available
break;
}
out << " p" << subsetTag << cacheSerial << "_" << descId << "[label=\"Pipeline " << descId
<< "\\n"
<< feedbackDesc << "\\n";
OutputAllPipelineState(context, out, pipelines[descId], kSubset, nodeState, false);
out << "\"]" << style << ";\n";
descToId[desc] = descId++;
}
for (const auto &descAndPipeline : cache)
{
const GraphicsPipelineDesc &desc = descAndPipeline.first;
const PipelineHelper &pipelineHelper = descAndPipeline.second;
const std::vector<GraphicsPipelineTransition> &transitions =
pipelineHelper.getTransitions();
for (const GraphicsPipelineTransition &transition : transitions)
{
#if defined(ANGLE_IS_64_BIT_CPU)
const uint64_t transitionBits = transition.bits.bits();
#else
const uint64_t transitionBits =
static_cast<uint64_t>(transition.bits.bits(1)) << 32 | transition.bits.bits(0);
#endif
out << " p" << subsetTag << cacheSerial << "_" << descToId[desc] << " -> p"
<< subsetTag << cacheSerial << "_" << descToId[*transition.desc] << " [label=\"'0x"
<< std::hex << transitionBits << std::dec << "'\"];\n";
}
}
out << " }\n";
}
// Used by SharedCacheKeyManager
void ReleaseCachedObject(ContextVk *contextVk, const FramebufferDesc &desc)
{
contextVk->getShareGroup()->getFramebufferCache().erase(contextVk, desc);
}
void ReleaseCachedObject(Renderer *renderer, const FramebufferDesc &desc)
{
UNREACHABLE();
}
void ReleaseCachedObject(ContextVk *contextVk, const DescriptorSetDescAndPool &descAndPool)
{
UNREACHABLE();
}
void ReleaseCachedObject(Renderer *renderer, const DescriptorSetDescAndPool &descAndPool)
{
ASSERT(descAndPool.mPool != nullptr);
descAndPool.mPool->releaseCachedDescriptorSet(renderer, descAndPool.mDesc);
}
void DestroyCachedObject(Renderer *renderer, const FramebufferDesc &desc)
{
// Framebuffer cache are implemented in a way that each cache entry tracks GPU progress and we
// always guarantee cache entries are released before calling destroy.
}
void DestroyCachedObject(Renderer *renderer, const DescriptorSetDescAndPool &descAndPool)
{
ASSERT(descAndPool.mPool != nullptr);
descAndPool.mPool->destroyCachedDescriptorSet(renderer, descAndPool.mDesc);
}
angle::Result InitializePipelineFromLibraries(Context *context,
PipelineCacheAccess *pipelineCache,
const vk::PipelineLayout &pipelineLayout,
const vk::PipelineHelper &vertexInputPipeline,
const vk::PipelineHelper &shadersPipeline,
const vk::PipelineHelper &fragmentOutputPipeline,
Pipeline *pipelineOut,
CacheLookUpFeedback *feedbackOut)
{
// Nothing in the create info, everything comes from the libraries.
VkGraphicsPipelineCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
createInfo.layout = pipelineLayout.getHandle();
const std::array<VkPipeline, 3> pipelines = {
vertexInputPipeline.getPipeline().getHandle(),
shadersPipeline.getPipeline().getHandle(),
fragmentOutputPipeline.getPipeline().getHandle(),
};
// Specify the three subsets as input libraries.
VkPipelineLibraryCreateInfoKHR libraryInfo = {};
libraryInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR;
libraryInfo.libraryCount = 3;
libraryInfo.pLibraries = pipelines.data();
AddToPNextChain(&createInfo, &libraryInfo);
// If supported, get feedback.
VkPipelineCreationFeedback feedback = {};
VkPipelineCreationFeedbackCreateInfo feedbackInfo = {};
feedbackInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO;
const bool supportsFeedback = context->getFeatures().supportsPipelineCreationFeedback.enabled;
if (supportsFeedback)
{
feedbackInfo.pPipelineCreationFeedback = &feedback;
AddToPNextChain(&createInfo, &feedbackInfo);
}
// Create the pipeline
ANGLE_VK_TRY(context, pipelineCache->createGraphicsPipeline(context, createInfo, pipelineOut));
if (supportsFeedback)
{
const bool cacheHit =
(feedback.flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT) !=
0;
*feedbackOut = cacheHit ? CacheLookUpFeedback::Hit : CacheLookUpFeedback::Miss;
ApplyPipelineCreationFeedback(context, feedback);
}
return angle::Result::Continue;
}
bool ShouldDumpPipelineCacheGraph(Context *context)
{
return kDumpPipelineCacheGraph && context->getRenderer()->isPipelineCacheGraphDumpEnabled();
}
} // anonymous namespace
GraphicsPipelineTransitionBits GetGraphicsPipelineTransitionBitsMask(GraphicsPipelineSubset subset)
{
switch (subset)
{
case GraphicsPipelineSubset::VertexInput:
return kPipelineVertexInputTransitionBitsMask;
case GraphicsPipelineSubset::Shaders:
return kPipelineShadersTransitionBitsMask;
case GraphicsPipelineSubset::FragmentOutput:
return kPipelineFragmentOutputTransitionBitsMask;
default:
break;
}
ASSERT(subset == GraphicsPipelineSubset::Complete);
GraphicsPipelineTransitionBits allBits;
allBits.set();
return allBits;
}
// RenderPassDesc implementation.
RenderPassDesc::RenderPassDesc()
{
memset(this, 0, sizeof(RenderPassDesc));
}
RenderPassDesc::~RenderPassDesc() = default;
RenderPassDesc::RenderPassDesc(const RenderPassDesc &other)
{
memcpy(this, &other, sizeof(RenderPassDesc));
}
void RenderPassDesc::packColorAttachment(size_t colorIndexGL, angle::FormatID formatID)
{
ASSERT(colorIndexGL < mAttachmentFormats.size());
static_assert(angle::kNumANGLEFormats < std::numeric_limits<uint8_t>::max(),
"Too many ANGLE formats to fit in uint8_t");
// Force the user to pack the depth/stencil attachment last.
ASSERT(!hasDepthStencilAttachment());
// This function should only be called for enabled GL color attachments.
ASSERT(formatID != angle::FormatID::NONE);
uint8_t &packedFormat = mAttachmentFormats[colorIndexGL];
SetBitField(packedFormat, formatID);
// Set color attachment range such that it covers the range from index 0 through last active
// index. This is the reasons why we need depth/stencil to be packed last.
SetBitField(mColorAttachmentRange, std::max<size_t>(mColorAttachmentRange, colorIndexGL + 1));
}
void RenderPassDesc::packColorAttachmentGap(size_t colorIndexGL)
{
ASSERT(colorIndexGL < mAttachmentFormats.size());
static_assert(angle::kNumANGLEFormats < std::numeric_limits<uint8_t>::max(),
"Too many ANGLE formats to fit in uint8_t");
// Force the user to pack the depth/stencil attachment last.
ASSERT(!hasDepthStencilAttachment());
// Use NONE as a flag for gaps in GL color attachments.
uint8_t &packedFormat = mAttachmentFormats[colorIndexGL];
SetBitField(packedFormat, angle::FormatID::NONE);
}
void RenderPassDesc::packDepthStencilAttachment(angle::FormatID formatID)
{
ASSERT(!hasDepthStencilAttachment());
size_t index = depthStencilAttachmentIndex();
ASSERT(index < mAttachmentFormats.size());
uint8_t &packedFormat = mAttachmentFormats[index];
SetBitField(packedFormat, formatID);
}
void RenderPassDesc::packColorResolveAttachment(size_t colorIndexGL)
{
ASSERT(isColorAttachmentEnabled(colorIndexGL));
ASSERT(!mColorResolveAttachmentMask.test(colorIndexGL));
ASSERT(mSamples > 1);
mColorResolveAttachmentMask.set(colorIndexGL);
}
void RenderPassDesc::packYUVResolveAttachment(size_t colorIndexGL)
{
ASSERT(isColorAttachmentEnabled(colorIndexGL));
ASSERT(!mColorResolveAttachmentMask.test(colorIndexGL));
mColorResolveAttachmentMask.set(colorIndexGL);
SetBitField(mIsYUVResolve, 1);
}
void RenderPassDesc::removeColorResolveAttachment(size_t colorIndexGL)
{
ASSERT(mColorResolveAttachmentMask.test(colorIndexGL));
mColorResolveAttachmentMask.reset(colorIndexGL);
}
void RenderPassDesc::packColorUnresolveAttachment(size_t colorIndexGL)
{
mColorUnresolveAttachmentMask.set(colorIndexGL);
}
void RenderPassDesc::removeColorUnresolveAttachment(size_t colorIndexGL)
{
mColorUnresolveAttachmentMask.reset(colorIndexGL);
}
void RenderPassDesc::packDepthResolveAttachment()
{
ASSERT(hasDepthStencilAttachment());
ASSERT(!hasDepthResolveAttachment());
mResolveDepth = true;
}
void RenderPassDesc::packStencilResolveAttachment()
{
ASSERT(hasDepthStencilAttachment());
ASSERT(!hasStencilResolveAttachment());
mResolveStencil = true;
}
void RenderPassDesc::packDepthUnresolveAttachment()
{
ASSERT(hasDepthStencilAttachment());
mUnresolveDepth = true;
}
void RenderPassDesc::packStencilUnresolveAttachment()
{
ASSERT(hasDepthStencilAttachment());
mUnresolveStencil = true;
}
void RenderPassDesc::removeDepthStencilUnresolveAttachment()
{
mUnresolveDepth = false;
mUnresolveStencil = false;
}
RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other)
{
memcpy(this, &other, sizeof(RenderPassDesc));
return *this;
}
void RenderPassDesc::setWriteControlMode(gl::SrgbWriteControlMode mode)
{
SetBitField(mSrgbWriteControl, mode);
}
size_t RenderPassDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool RenderPassDesc::isColorAttachmentEnabled(size_t colorIndexGL) const
{
angle::FormatID formatID = operator[](colorIndexGL);
return formatID != angle::FormatID::NONE;
}
bool RenderPassDesc::hasDepthStencilAttachment() const
{
angle::FormatID formatID = operator[](depthStencilAttachmentIndex());
return formatID != angle::FormatID::NONE;
}
size_t RenderPassDesc::clearableAttachmentCount() const
{
size_t colorAttachmentCount = 0;
for (size_t i = 0; i < mColorAttachmentRange; ++i)
{
colorAttachmentCount += isColorAttachmentEnabled(i);
}
// Note that there are no gaps in depth/stencil attachments. In fact there is a maximum of 1 of
// it + 1 for its resolve attachment.
size_t depthStencilCount = hasDepthStencilAttachment() ? 1 : 0;
size_t depthStencilResolveCount = hasDepthStencilResolveAttachment() ? 1 : 0;
return colorAttachmentCount + mColorResolveAttachmentMask.count() + depthStencilCount +
depthStencilResolveCount;
}
size_t RenderPassDesc::attachmentCount() const
{
return clearableAttachmentCount() + (hasFragmentShadingAttachment() ? 1 : 0);
}
void RenderPassDesc::setLegacyDither(bool enabled)
{
SetBitField(mLegacyDitherEnabled, enabled ? 1 : 0);
}
bool operator==(const RenderPassDesc &lhs, const RenderPassDesc &rhs)
{
return memcmp(&lhs, &rhs, sizeof(RenderPassDesc)) == 0;
}
// GraphicsPipelineDesc implementation.
// Use aligned allocation and free so we can use the alignas keyword.
void *GraphicsPipelineDesc::operator new(std::size_t size)
{
return angle::AlignedAlloc(size, 32);
}
void GraphicsPipelineDesc::operator delete(void *ptr)
{
return angle::AlignedFree(ptr);
}
GraphicsPipelineDesc::GraphicsPipelineDesc()
{
memset(this, 0, sizeof(GraphicsPipelineDesc));
}
GraphicsPipelineDesc::~GraphicsPipelineDesc() = default;
GraphicsPipelineDesc::GraphicsPipelineDesc(const GraphicsPipelineDesc &other)
{
*this = other;
}
GraphicsPipelineDesc &GraphicsPipelineDesc::operator=(const GraphicsPipelineDesc &other)
{
memcpy(this, &other, sizeof(*this));
return *this;
}
const void *GraphicsPipelineDesc::getPipelineSubsetMemory(GraphicsPipelineSubset subset,
size_t *sizeOut) const
{
// GraphicsPipelineDesc must be laid out such that the three subsets are contiguous. The layout
// is:
//
// Shaders State \
// )--> Pre-rasterization + fragment subset
// Shared Non-Vertex-Input State / \
// )--> fragment output subset
// Fragment Output State /
//
// Vertex Input State ----> Vertex input subset
static_assert(offsetof(GraphicsPipelineDesc, mShaders) == kPipelineShadersDescOffset);
static_assert(offsetof(GraphicsPipelineDesc, mSharedNonVertexInput) ==
kPipelineShadersDescOffset + kGraphicsPipelineShadersStateSize);
static_assert(offsetof(GraphicsPipelineDesc, mSharedNonVertexInput) ==
kPipelineFragmentOutputDescOffset);
static_assert(offsetof(GraphicsPipelineDesc, mFragmentOutput) ==
kPipelineFragmentOutputDescOffset +
kGraphicsPipelineSharedNonVertexInputStateSize);
static_assert(offsetof(GraphicsPipelineDesc, mVertexInput) == kPipelineVertexInputDescOffset);
// Exclude the full vertex or only vertex strides from the hash. It's conveniently placed last,
// so it would be easy to exclude it from hash.
static_assert(offsetof(GraphicsPipelineDesc, mVertexInput.vertex.strides) +
sizeof(PackedVertexInputAttributes::strides) ==
sizeof(GraphicsPipelineDesc));
static_assert(offsetof(GraphicsPipelineDesc, mVertexInput.vertex) +
sizeof(PackedVertexInputAttributes) ==
sizeof(GraphicsPipelineDesc));
size_t vertexInputReduceSize = 0;
if (mVertexInput.inputAssembly.bits.useVertexInputBindingStrideDynamicState)
{
vertexInputReduceSize = sizeof(PackedVertexInputAttributes::strides);
}
else if (mVertexInput.inputAssembly.bits.useVertexInputDynamicState)
{
vertexInputReduceSize = sizeof(PackedVertexInputAttributes);
}
switch (subset)
{
case GraphicsPipelineSubset::VertexInput:
*sizeOut = kPipelineVertexInputDescSize - vertexInputReduceSize;
return &mVertexInput;
case GraphicsPipelineSubset::Shaders:
*sizeOut = kPipelineShadersDescSize;
return &mShaders;
case GraphicsPipelineSubset::FragmentOutput:
*sizeOut = kPipelineFragmentOutputDescSize;
return &mSharedNonVertexInput;
case GraphicsPipelineSubset::Complete:
default:
*sizeOut = sizeof(*this) - vertexInputReduceSize;
return this;
}
}
size_t GraphicsPipelineDesc::hash(GraphicsPipelineSubset subset) const
{
size_t keySize = 0;
const void *key = getPipelineSubsetMemory(subset, &keySize);
return angle::ComputeGenericHash(key, keySize);
}
bool GraphicsPipelineDesc::keyEqual(const GraphicsPipelineDesc &other,
GraphicsPipelineSubset subset) const
{
size_t keySize = 0;
const void *key = getPipelineSubsetMemory(subset, &keySize);
size_t otherKeySize = 0;
const void *otherKey = other.getPipelineSubsetMemory(subset, &otherKeySize);
// Compare the relevant part of the desc memory. Note that due to workarounds (e.g.
// useVertexInputBindingStrideDynamicState), |this| or |other| may produce different key sizes.
// In that case, comparing the minimum of the two is sufficient; if the workarounds are
// different, the comparison would fail anyway.
return memcmp(key, otherKey, std::min(keySize, otherKeySize)) == 0;
}
// Initialize PSO states, it is consistent with initial value of gl::State.
//
// Some states affect the pipeline, but they are not derived from the GL state, but rather the
// properties of the Vulkan device or the context itself; such as whether a workaround is in
// effect, or the context is robust. For VK_EXT_graphics_pipeline_library, such state that affects
// multiple subsets of the pipeline is duplicated in each subset (for example, there are two
// copies of isRobustContext, one for vertex input and one for shader stages).
void GraphicsPipelineDesc::initDefaults(const Context *context,
GraphicsPipelineSubset subset,
PipelineRobustness pipelineRobustness,
PipelineProtectedAccess pipelineProtectedAccess)
{
if (GraphicsPipelineHasVertexInput(subset))
{
// Set all vertex input attributes to default, the default format is Float
angle::FormatID defaultFormat = GetCurrentValueFormatID(gl::VertexAttribType::Float);
for (PackedAttribDesc &packedAttrib : mVertexInput.vertex.attribs)
{
SetBitField(packedAttrib.divisor, 0);
SetBitField(packedAttrib.format, defaultFormat);
SetBitField(packedAttrib.compressed, 0);
SetBitField(packedAttrib.offset, 0);
}
mVertexInput.vertex.shaderAttribComponentType = 0;
memset(mVertexInput.vertex.strides, 0, sizeof(mVertexInput.vertex.strides));
SetBitField(mVertexInput.inputAssembly.bits.topology, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
mVertexInput.inputAssembly.bits.primitiveRestartEnable = 0;
mVertexInput.inputAssembly.bits.useVertexInputBindingStrideDynamicState =
context->getRenderer()->useVertexInputBindingStrideDynamicState();
mVertexInput.inputAssembly.bits.useVertexInputDynamicState =
context->getFeatures().supportsVertexInputDynamicState.enabled;
mVertexInput.inputAssembly.bits.padding = 0;
}
if (GraphicsPipelineHasShaders(subset))
{
mShaders.shaders.bits.viewportNegativeOneToOne =
context->getFeatures().supportsDepthClipControl.enabled;
mShaders.shaders.bits.depthClampEnable = 0;
SetBitField(mShaders.shaders.bits.polygonMode, VK_POLYGON_MODE_FILL);
SetBitField(mShaders.shaders.bits.cullMode, VK_CULL_MODE_NONE);
SetBitField(mShaders.shaders.bits.frontFace, VK_FRONT_FACE_COUNTER_CLOCKWISE);
mShaders.shaders.bits.rasterizerDiscardEnable = 0;
mShaders.shaders.bits.depthBiasEnable = 0;
SetBitField(mShaders.shaders.bits.patchVertices, 3);
mShaders.shaders.bits.depthBoundsTest = 0;
mShaders.shaders.bits.depthTest = 0;
mShaders.shaders.bits.depthWrite = 0;
mShaders.shaders.bits.stencilTest = 0;
mShaders.shaders.bits.nonZeroStencilWriteMaskWorkaround = 0;
SetBitField(mShaders.shaders.bits.depthCompareOp, VK_COMPARE_OP_LESS);
mShaders.shaders.bits.surfaceRotation = 0;
mShaders.shaders.emulatedDitherControl = 0;
mShaders.shaders.padding = 0;
SetBitField(mShaders.shaders.front.fail, VK_STENCIL_OP_KEEP);
SetBitField(mShaders.shaders.front.pass, VK_STENCIL_OP_KEEP);
SetBitField(mShaders.shaders.front.depthFail, VK_STENCIL_OP_KEEP);
SetBitField(mShaders.shaders.front.compare, VK_COMPARE_OP_ALWAYS);
SetBitField(mShaders.shaders.back.fail, VK_STENCIL_OP_KEEP);
SetBitField(mShaders.shaders.back.pass, VK_STENCIL_OP_KEEP);
SetBitField(mShaders.shaders.back.depthFail, VK_STENCIL_OP_KEEP);
SetBitField(mShaders.shaders.back.compare, VK_COMPARE_OP_ALWAYS);
}
if (GraphicsPipelineHasShadersOrFragmentOutput(subset))
{
mSharedNonVertexInput.multisample.bits.sampleMask = std::numeric_limits<uint16_t>::max();
mSharedNonVertexInput.multisample.bits.rasterizationSamplesMinusOne = 0;
mSharedNonVertexInput.multisample.bits.sampleShadingEnable = 0;
mSharedNonVertexInput.multisample.bits.alphaToCoverageEnable = 0;
mSharedNonVertexInput.multisample.bits.alphaToOneEnable = 0;
mSharedNonVertexInput.multisample.bits.subpass = 0;
mSharedNonVertexInput.multisample.bits.minSampleShading = kMinSampleShadingScale;
}
if (GraphicsPipelineHasFragmentOutput(subset))
{
constexpr VkFlags kAllColorBits = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
for (uint32_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
++colorIndexGL)
{
Int4Array_Set(mFragmentOutput.blend.colorWriteMaskBits, colorIndexGL, kAllColorBits);
}
PackedColorBlendAttachmentState blendAttachmentState;
SetBitField(blendAttachmentState.srcColorBlendFactor, VK_BLEND_FACTOR_ONE);
SetBitField(blendAttachmentState.dstColorBlendFactor, VK_BLEND_FACTOR_ZERO);
SetBitField(blendAttachmentState.colorBlendOp, VK_BLEND_OP_ADD);
SetBitField(blendAttachmentState.srcAlphaBlendFactor, VK_BLEND_FACTOR_ONE);
SetBitField(blendAttachmentState.dstAlphaBlendFactor, VK_BLEND_FACTOR_ZERO);
SetBitField(blendAttachmentState.alphaBlendOp, VK_BLEND_OP_ADD);
std::fill(&mFragmentOutput.blend.attachments[0],
&mFragmentOutput.blend.attachments[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS],
blendAttachmentState);
mFragmentOutput.blendMaskAndLogic.bits.blendEnableMask = 0;
mFragmentOutput.blendMaskAndLogic.bits.logicOpEnable = 0;
SetBitField(mFragmentOutput.blendMaskAndLogic.bits.logicOp, VK_LOGIC_OP_COPY);
mFragmentOutput.blendMaskAndLogic.bits.padding = 0;
}
// Context robustness affects vertex input and shader stages.
mVertexInput.inputAssembly.bits.isRobustContext = mShaders.shaders.bits.isRobustContext =
pipelineRobustness == PipelineRobustness::Robust;
// Context protected-ness affects all subsets.
mVertexInput.inputAssembly.bits.isProtectedContext = mShaders.shaders.bits.isProtectedContext =
mFragmentOutput.blendMaskAndLogic.bits.isProtectedContext =
pipelineProtectedAccess == PipelineProtectedAccess::Protected;
}
VkResult GraphicsPipelineDesc::initializePipeline(Context *context,
PipelineCacheAccess *pipelineCache,
GraphicsPipelineSubset subset,
const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout,
const ShaderModuleMap &shaders,
const SpecializationConstants &specConsts,
Pipeline *pipelineOut,
CacheLookUpFeedback *feedbackOut) const
{
GraphicsPipelineVertexInputVulkanStructs vertexInputState;
GraphicsPipelineShadersVulkanStructs shadersState;
GraphicsPipelineSharedNonVertexInputVulkanStructs sharedNonVertexInputState;
GraphicsPipelineFragmentOutputVulkanStructs fragmentOutputState;
GraphicsPipelineDynamicStateList dynamicStateList;
VkGraphicsPipelineCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
createInfo.flags = 0;
createInfo.renderPass = compatibleRenderPass.getHandle();
createInfo.subpass = mSharedNonVertexInput.multisample.bits.subpass;
const bool hasVertexInput = GraphicsPipelineHasVertexInput(subset);
const bool hasShaders = GraphicsPipelineHasShaders(subset);
const bool hasShadersOrFragmentOutput = GraphicsPipelineHasShadersOrFragmentOutput(subset);
const bool hasFragmentOutput = GraphicsPipelineHasFragmentOutput(subset);
if (hasVertexInput)
{
initializePipelineVertexInputState(context, &vertexInputState, &dynamicStateList);
createInfo.pVertexInputState = &vertexInputState.vertexInputState;
createInfo.pInputAssemblyState = &vertexInputState.inputAssemblyState;
}
if (hasShaders)
{
initializePipelineShadersState(context, shaders, specConsts, &shadersState,
&dynamicStateList);
createInfo.stageCount = static_cast<uint32_t>(shadersState.shaderStages.size());
createInfo.pStages = shadersState.shaderStages.data();
createInfo.pTessellationState = &shadersState.tessellationState;
createInfo.pViewportState = &shadersState.viewportState;
createInfo.pRasterizationState = &shadersState.rasterState;
createInfo.pDepthStencilState = &shadersState.depthStencilState;
createInfo.layout = pipelineLayout.getHandle();
}
if (hasShadersOrFragmentOutput)
{
initializePipelineSharedNonVertexInputState(context, &sharedNonVertexInputState,
&dynamicStateList);
createInfo.pMultisampleState = &sharedNonVertexInputState.multisampleState;
}
if (hasFragmentOutput)
{
initializePipelineFragmentOutputState(context, &fragmentOutputState, &dynamicStateList);
createInfo.pColorBlendState = &fragmentOutputState.blendState;
}
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateList.size());
dynamicState.pDynamicStates = dynamicStateList.data();
createInfo.pDynamicState = dynamicStateList.empty() ? nullptr : &dynamicState;
// If not a complete pipeline, specify which subset is being created
VkGraphicsPipelineLibraryCreateInfoEXT libraryInfo = {};
libraryInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT;
if (subset != GraphicsPipelineSubset::Complete)
{
switch (subset)
{
case GraphicsPipelineSubset::VertexInput:
libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT;
break;
case GraphicsPipelineSubset::Shaders:
libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT |
VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT;
break;
case GraphicsPipelineSubset::FragmentOutput:
libraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT;
break;
default:
UNREACHABLE();
break;
}
createInfo.flags |= VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
AddToPNextChain(&createInfo, &libraryInfo);
}
VkPipelineRobustnessCreateInfoEXT robustness = {};
robustness.sType = VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO_EXT;
// Enable robustness on the pipeline if needed. Note that the global robustBufferAccess feature
// must be disabled by default.
if ((hasVertexInput && mVertexInput.inputAssembly.bits.isRobustContext) ||
(hasShaders && mShaders.shaders.bits.isRobustContext))
{
ASSERT(context->getFeatures().supportsPipelineRobustness.enabled);
robustness.storageBuffers = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_EXT;
robustness.uniformBuffers = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_EXT;
robustness.vertexInputs = VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_EXT;
robustness.images = VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_DEVICE_DEFAULT_EXT;
AddToPNextChain(&createInfo, &robustness);
}
if ((hasVertexInput && mVertexInput.inputAssembly.bits.isProtectedContext) ||
(hasShaders && mShaders.shaders.bits.isProtectedContext) ||
(hasFragmentOutput && mFragmentOutput.blendMaskAndLogic.bits.isProtectedContext))
{
ASSERT(context->getFeatures().supportsPipelineProtectedAccess.enabled);
createInfo.flags |= VK_PIPELINE_CREATE_PROTECTED_ACCESS_ONLY_BIT_EXT;
}
else if (context->getFeatures().supportsPipelineProtectedAccess.enabled)
{
createInfo.flags |= VK_PIPELINE_CREATE_NO_PROTECTED_ACCESS_BIT_EXT;
}
VkPipelineCreationFeedback feedback = {};
gl::ShaderMap<VkPipelineCreationFeedback> perStageFeedback;
VkPipelineCreationFeedbackCreateInfo feedbackInfo = {};
feedbackInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO;
const bool supportsFeedback = context->getFeatures().supportsPipelineCreationFeedback.enabled;
if (supportsFeedback)
{
feedbackInfo.pPipelineCreationFeedback = &feedback;
// Provide some storage for per-stage data, even though it's not used. This first works
// around a VVL bug that doesn't allow `pipelineStageCreationFeedbackCount=0` despite the
// spec (See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/4161). Even
// with fixed VVL, several drivers crash when this storage is missing too.
feedbackInfo.pipelineStageCreationFeedbackCount = createInfo.stageCount;
feedbackInfo.pPipelineStageCreationFeedbacks = perStageFeedback.data();
AddToPNextChain(&createInfo, &feedbackInfo);
}
VkResult result = pipelineCache->createGraphicsPipeline(context, createInfo, pipelineOut);
if (supportsFeedback)
{
const bool cacheHit =
(feedback.flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT) !=
0;
*feedbackOut = cacheHit ? CacheLookUpFeedback::Hit : CacheLookUpFeedback::Miss;
ApplyPipelineCreationFeedback(context, feedback);
}
return result;
}
angle::FormatID patchVertexAttribComponentType(angle::FormatID format,
gl::ComponentType vsInputType)
{
const gl::VertexFormat &vertexFormat = gl::GetVertexFormatFromID(format);
// For normalized format, keep the same ?
EGLBoolean normalized = vertexFormat.normalized;
if (normalized)
{
return format;
}
gl::VertexAttribType attribType = gl::FromGLenum<gl::VertexAttribType>(vertexFormat.type);
if (vsInputType != gl::ComponentType::Float)
{
ASSERT(vsInputType == gl::ComponentType::Int ||
vsInputType == gl::ComponentType::UnsignedInt);
switch (attribType)
{
case gl::VertexAttribType::Float:
case gl::VertexAttribType::Fixed:
case gl::VertexAttribType::UnsignedInt:
case gl::VertexAttribType::Int:
attribType = vsInputType == gl::ComponentType::Int
? gl::VertexAttribType::Int
: gl::VertexAttribType::UnsignedInt;
break;
case gl::VertexAttribType::HalfFloat:
case gl::VertexAttribType::HalfFloatOES:
case gl::VertexAttribType::Short:
case gl::VertexAttribType::UnsignedShort:
attribType = vsInputType == gl::ComponentType::Int
? gl::VertexAttribType::Short
: gl::VertexAttribType::UnsignedShort;
break;
case gl::VertexAttribType::Byte:
case gl::VertexAttribType::UnsignedByte:
attribType = vsInputType == gl::ComponentType::Int
? gl::VertexAttribType::Byte
: gl::VertexAttribType::UnsignedByte;
break;
case gl::VertexAttribType::UnsignedInt2101010:
case gl::VertexAttribType::Int2101010:
attribType = vsInputType == gl::ComponentType::Int
? gl::VertexAttribType::Int2101010
: gl::VertexAttribType::UnsignedInt2101010;
break;
case gl::VertexAttribType::UnsignedInt1010102:
case gl::VertexAttribType::Int1010102:
attribType = vsInputType == gl::ComponentType::Int
? gl::VertexAttribType::Int1010102
: gl::VertexAttribType::UnsignedInt1010102;
break;
default:
ASSERT(0);
break;
}
}
return gl::GetVertexFormatID(attribType, vertexFormat.normalized, vertexFormat.components,
!vertexFormat.pureInteger);
}
VkFormat GraphicsPipelineDesc::getPipelineVertexInputStateFormat(
Context *context,
angle::FormatID formatID,
bool compressed,
const gl::ComponentType programAttribType,
uint32_t attribIndex)
{
// Get the corresponding VkFormat for the attrib's format.
const Format &format = context->getRenderer()->getFormat(formatID);
const angle::Format &intendedFormat = format.getIntendedFormat();
VkFormat vkFormat = format.getActualBufferVkFormat(compressed);
const gl::ComponentType attribType = GetVertexAttributeComponentType(
intendedFormat.isPureInt(), intendedFormat.vertexAttribType);
if (attribType != programAttribType)
{
VkFormat origVkFormat = vkFormat;
if (attribType == gl::ComponentType::Float || programAttribType == gl::ComponentType::Float)
{
angle::FormatID patchFormatID =
patchVertexAttribComponentType(formatID, programAttribType);
vkFormat = context->getRenderer()
->getFormat(patchFormatID)
.getActualBufferVkFormat(compressed);
}
else
{
// When converting from an unsigned to a signed format or vice versa, attempt to
// match the bit width.
angle::FormatID convertedFormatID = gl::ConvertFormatSignedness(intendedFormat);
const Format &convertedFormat = context->getRenderer()->getFormat(convertedFormatID);
ASSERT(intendedFormat.channelCount == convertedFormat.getIntendedFormat().channelCount);
ASSERT(intendedFormat.redBits == convertedFormat.getIntendedFormat().redBits);
ASSERT(intendedFormat.greenBits == convertedFormat.getIntendedFormat().greenBits);
ASSERT(intendedFormat.blueBits == convertedFormat.getIntendedFormat().blueBits);
ASSERT(intendedFormat.alphaBits == convertedFormat.getIntendedFormat().alphaBits);
vkFormat = convertedFormat.getActualBufferVkFormat(compressed);
}
const Format &origFormat =
context->getRenderer()->getFormat(GetFormatIDFromVkFormat(origVkFormat));
const Format &patchFormat =
context->getRenderer()->getFormat(GetFormatIDFromVkFormat(vkFormat));
ASSERT(origFormat.getIntendedFormat().pixelBytes ==
patchFormat.getIntendedFormat().pixelBytes);
ASSERT(context->getRenderer()->getNativeExtensions().relaxedVertexAttributeTypeANGLE);
}
return vkFormat;
}
void GraphicsPipelineDesc::initializePipelineVertexInputState(
Context *context,
GraphicsPipelineVertexInputVulkanStructs *stateOut,
GraphicsPipelineDynamicStateList *dynamicStateListOut) const
{
// TODO(jmadill): Possibly use different path for ES 3.1 split bindings/attribs.
uint32_t vertexAttribCount = 0;
stateOut->divisorState.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
stateOut->divisorState.pVertexBindingDivisors = stateOut->divisorDesc.data();
for (size_t attribIndexSizeT :
gl::AttributesMask(mVertexInput.inputAssembly.bits.programActiveAttributeLocations))
{
const uint32_t attribIndex = static_cast<uint32_t>(attribIndexSizeT);
VkVertexInputBindingDescription &bindingDesc = stateOut->bindingDescs[vertexAttribCount];
VkVertexInputAttributeDescription &attribDesc = stateOut->attributeDescs[vertexAttribCount];
const PackedAttribDesc &packedAttrib = mVertexInput.vertex.attribs[attribIndex];
bindingDesc.binding = attribIndex;
bindingDesc.stride = static_cast<uint32_t>(mVertexInput.vertex.strides[attribIndex]);
if (packedAttrib.divisor != 0)
{
bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_INSTANCE);
stateOut->divisorDesc[stateOut->divisorState.vertexBindingDivisorCount].binding =
bindingDesc.binding;
stateOut->divisorDesc[stateOut->divisorState.vertexBindingDivisorCount].divisor =
packedAttrib.divisor;
++stateOut->divisorState.vertexBindingDivisorCount;
}
else
{
bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_VERTEX);
}
// If using dynamic state for stride, the value for stride is unconditionally 0 here.
// |ContextVk::handleDirtyGraphicsVertexBuffers| implements the same fix when setting stride
// dynamically.
ASSERT(!context->getRenderer()->useVertexInputBindingStrideDynamicState() ||
bindingDesc.stride == 0);
// Get the corresponding VkFormat for the attrib's format.
angle::FormatID formatID = static_cast<angle::FormatID>(packedAttrib.format);
const gl::ComponentType programAttribType = gl::GetComponentTypeMask(
gl::ComponentTypeMask(mVertexInput.vertex.shaderAttribComponentType), attribIndex);
attribDesc.binding = attribIndex;
attribDesc.format = getPipelineVertexInputStateFormat(
context, formatID, packedAttrib.compressed, programAttribType, attribIndex);
attribDesc.location = static_cast<uint32_t>(attribIndex);
attribDesc.offset = packedAttrib.offset;
vertexAttribCount++;
}
// The binding descriptions are filled in at draw time.
stateOut->vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
stateOut->vertexInputState.flags = 0;
stateOut->vertexInputState.vertexBindingDescriptionCount = vertexAttribCount;
stateOut->vertexInputState.pVertexBindingDescriptions = stateOut->bindingDescs.data();
stateOut->vertexInputState.vertexAttributeDescriptionCount = vertexAttribCount;
stateOut->vertexInputState.pVertexAttributeDescriptions = stateOut->attributeDescs.data();
if (stateOut->divisorState.vertexBindingDivisorCount)
{
stateOut->vertexInputState.pNext = &stateOut->divisorState;
}
const PackedInputAssemblyState &inputAssembly = mVertexInput.inputAssembly;
// Primitive topology is filled in at draw time.
stateOut->inputAssemblyState.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
stateOut->inputAssemblyState.flags = 0;
stateOut->inputAssemblyState.topology =
static_cast<VkPrimitiveTopology>(inputAssembly.bits.topology);
stateOut->inputAssemblyState.primitiveRestartEnable =
static_cast<VkBool32>(inputAssembly.bits.primitiveRestartEnable);
// Dynamic state
if (context->getRenderer()->useVertexInputBindingStrideDynamicState() && vertexAttribCount > 0)
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE);
}
if (context->getRenderer()->usePrimitiveRestartEnableDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE);
}
if (context->getFeatures().supportsVertexInputDynamicState.enabled)
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
}
}
void GraphicsPipelineDesc::initializePipelineShadersState(
Context *context,
const ShaderModuleMap &shaders,
const SpecializationConstants &specConsts,
GraphicsPipelineShadersVulkanStructs *stateOut,
GraphicsPipelineDynamicStateList *dynamicStateListOut) const
{
InitializeSpecializationInfo(specConsts, &stateOut->specializationEntries,
&stateOut->specializationInfo);
// Vertex shader is always expected to be present.
const ShaderModule &vertexModule = shaders[gl::ShaderType::Vertex].get();
ASSERT(vertexModule.valid());
VkPipelineShaderStageCreateInfo vertexStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_VERTEX_BIT, vertexModule.getHandle(),
stateOut->specializationInfo, &vertexStage);
stateOut->shaderStages.push_back(vertexStage);
const ShaderModulePointer &tessControlPointer = shaders[gl::ShaderType::TessControl];
if (tessControlPointer.valid())
{
const ShaderModule &tessControlModule = tessControlPointer.get();
VkPipelineShaderStageCreateInfo tessControlStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
tessControlModule.getHandle(), stateOut->specializationInfo,
&tessControlStage);
stateOut->shaderStages.push_back(tessControlStage);
}
const ShaderModulePointer &tessEvaluationPointer = shaders[gl::ShaderType::TessEvaluation];
if (tessEvaluationPointer.valid())
{
const ShaderModule &tessEvaluationModule = tessEvaluationPointer.get();
VkPipelineShaderStageCreateInfo tessEvaluationStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
tessEvaluationModule.getHandle(), stateOut->specializationInfo,
&tessEvaluationStage);
stateOut->shaderStages.push_back(tessEvaluationStage);
}
const ShaderModulePointer &geometryPointer = shaders[gl::ShaderType::Geometry];
if (geometryPointer.valid())
{
const ShaderModule &geometryModule = geometryPointer.get();
VkPipelineShaderStageCreateInfo geometryStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_GEOMETRY_BIT, geometryModule.getHandle(),
stateOut->specializationInfo, &geometryStage);
stateOut->shaderStages.push_back(geometryStage);
}
// Fragment shader is optional.
const ShaderModulePointer &fragmentPointer = shaders[gl::ShaderType::Fragment];
if (fragmentPointer.valid() && !mShaders.shaders.bits.rasterizerDiscardEnable)
{
const ShaderModule &fragmentModule = fragmentPointer.get();
VkPipelineShaderStageCreateInfo fragmentStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_FRAGMENT_BIT, fragmentModule.getHandle(),
stateOut->specializationInfo, &fragmentStage);
stateOut->shaderStages.push_back(fragmentStage);
}
// Set initial viewport and scissor state.
stateOut->viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
stateOut->viewportState.flags = 0;
stateOut->viewportState.viewportCount = 1;
stateOut->viewportState.pViewports = nullptr;
stateOut->viewportState.scissorCount = 1;
stateOut->viewportState.pScissors = nullptr;
if (context->getFeatures().supportsDepthClipControl.enabled)
{
stateOut->depthClipControl.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT;
stateOut->depthClipControl.negativeOneToOne =
static_cast<VkBool32>(mShaders.shaders.bits.viewportNegativeOneToOne);
stateOut->viewportState.pNext = &stateOut->depthClipControl;
}
// Rasterizer state.
stateOut->rasterState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
stateOut->rasterState.flags = 0;
stateOut->rasterState.depthClampEnable =
static_cast<VkBool32>(mShaders.shaders.bits.depthClampEnable);
stateOut->rasterState.rasterizerDiscardEnable =
static_cast<VkBool32>(mShaders.shaders.bits.rasterizerDiscardEnable);
stateOut->rasterState.polygonMode =
static_cast<VkPolygonMode>(mShaders.shaders.bits.polygonMode);
stateOut->rasterState.cullMode = static_cast<VkCullModeFlags>(mShaders.shaders.bits.cullMode);
stateOut->rasterState.frontFace = static_cast<VkFrontFace>(mShaders.shaders.bits.frontFace);
stateOut->rasterState.depthBiasEnable =
static_cast<VkBool32>(mShaders.shaders.bits.depthBiasEnable);
stateOut->rasterState.lineWidth = 0;
const void **pNextPtr = &stateOut->rasterState.pNext;
const PackedMultisampleAndSubpassState &multisample = mSharedNonVertexInput.multisample;
stateOut->rasterLineState.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT;
// Enable Bresenham line rasterization if available and the following conditions are met:
// 1.) not multisampling
// 2.) VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766:
// The Vulkan spec states: If the lineRasterizationMode member of a
// VkPipelineRasterizationLineStateCreateInfoEXT structure included in the pNext chain of
// pRasterizationState is VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT or
// VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT and if rasterization is enabled, then the
// alphaToCoverageEnable, alphaToOneEnable, and sampleShadingEnable members of pMultisampleState
// must all be VK_FALSE.
if (multisample.bits.rasterizationSamplesMinusOne == 0 &&
!mShaders.shaders.bits.rasterizerDiscardEnable && !multisample.bits.alphaToCoverageEnable &&
!multisample.bits.alphaToOneEnable && !multisample.bits.sampleShadingEnable &&
context->getFeatures().bresenhamLineRasterization.enabled)
{
stateOut->rasterLineState.lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT;
*pNextPtr = &stateOut->rasterLineState;
pNextPtr = &stateOut->rasterLineState.pNext;
}
// Always set provoking vertex mode to last if available.
if (context->getFeatures().provokingVertex.enabled)
{
stateOut->provokingVertexState.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT;
stateOut->provokingVertexState.provokingVertexMode =
VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
*pNextPtr = &stateOut->provokingVertexState;
pNextPtr = &stateOut->provokingVertexState.pNext;
}
if (context->getFeatures().supportsGeometryStreamsCapability.enabled)
{
stateOut->rasterStreamState.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT;
stateOut->rasterStreamState.rasterizationStream = 0;
*pNextPtr = &stateOut->rasterStreamState;
pNextPtr = &stateOut->rasterStreamState.pNext;
}
// Depth/stencil state.
stateOut->depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
stateOut->depthStencilState.flags = 0;
stateOut->depthStencilState.depthTestEnable =
static_cast<VkBool32>(mShaders.shaders.bits.depthTest);
stateOut->depthStencilState.depthWriteEnable =
static_cast<VkBool32>(mShaders.shaders.bits.depthWrite);
stateOut->depthStencilState.depthCompareOp =
static_cast<VkCompareOp>(mShaders.shaders.bits.depthCompareOp);
stateOut->depthStencilState.depthBoundsTestEnable =
static_cast<VkBool32>(mShaders.shaders.bits.depthBoundsTest);
stateOut->depthStencilState.stencilTestEnable =
static_cast<VkBool32>(mShaders.shaders.bits.stencilTest);
UnpackStencilState(mShaders.shaders.front, &stateOut->depthStencilState.front,
mShaders.shaders.bits.nonZeroStencilWriteMaskWorkaround);
UnpackStencilState(mShaders.shaders.back, &stateOut->depthStencilState.back,
mShaders.shaders.bits.nonZeroStencilWriteMaskWorkaround);
stateOut->depthStencilState.minDepthBounds = 0;
stateOut->depthStencilState.maxDepthBounds = 0;
// tessellation State
if (tessControlPointer.valid() && tessEvaluationPointer.valid())
{
stateOut->domainOriginState.sType =
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
stateOut->domainOriginState.pNext = NULL;
stateOut->domainOriginState.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
stateOut->tessellationState.sType =
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
stateOut->tessellationState.flags = 0;
stateOut->tessellationState.pNext = &stateOut->domainOriginState;
stateOut->tessellationState.patchControlPoints =
static_cast<uint32_t>(mShaders.shaders.bits.patchVertices);
}
// Dynamic state
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_VIEWPORT);
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_SCISSOR);
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_LINE_WIDTH);
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
if (context->getRenderer()->useCullModeDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_CULL_MODE_EXT);
}
if (context->getRenderer()->useFrontFaceDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_FRONT_FACE_EXT);
}
if (context->getRenderer()->useDepthTestEnableDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE);
}
if (context->getRenderer()->useDepthWriteEnableDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE);
}
if (context->getRenderer()->useDepthCompareOpDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_DEPTH_COMPARE_OP);
}
if (context->getRenderer()->useStencilTestEnableDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE);
}
if (context->getRenderer()->useStencilOpDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_STENCIL_OP);
}
if (context->getRenderer()->useRasterizerDiscardEnableDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE);
}
if (context->getRenderer()->useDepthBiasEnableDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE);
}
if (context->getFeatures().supportsFragmentShadingRate.enabled)
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR);
}
}
void GraphicsPipelineDesc::initializePipelineSharedNonVertexInputState(
Context *context,
GraphicsPipelineSharedNonVertexInputVulkanStructs *stateOut,
GraphicsPipelineDynamicStateList *dynamicStateListOut) const
{
const PackedMultisampleAndSubpassState &multisample = mSharedNonVertexInput.multisample;
stateOut->sampleMask = multisample.bits.sampleMask;
// Multisample state.
stateOut->multisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
stateOut->multisampleState.flags = 0;
stateOut->multisampleState.rasterizationSamples =
gl_vk::GetSamples(multisample.bits.rasterizationSamplesMinusOne + 1,
context->getFeatures().limitSampleCountTo2.enabled);
stateOut->multisampleState.sampleShadingEnable =
static_cast<VkBool32>(multisample.bits.sampleShadingEnable);
stateOut->multisampleState.minSampleShading =
static_cast<float>(multisample.bits.minSampleShading) / kMinSampleShadingScale;
stateOut->multisampleState.pSampleMask = &stateOut->sampleMask;
stateOut->multisampleState.alphaToCoverageEnable =
static_cast<VkBool32>(multisample.bits.alphaToCoverageEnable);
stateOut->multisampleState.alphaToOneEnable =
static_cast<VkBool32>(multisample.bits.alphaToOneEnable);
}
void GraphicsPipelineDesc::initializePipelineFragmentOutputState(
Context *context,
GraphicsPipelineFragmentOutputVulkanStructs *stateOut,
GraphicsPipelineDynamicStateList *dynamicStateListOut) const
{
stateOut->blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
stateOut->blendState.flags = 0;
stateOut->blendState.logicOpEnable =
static_cast<VkBool32>(mFragmentOutput.blendMaskAndLogic.bits.logicOpEnable);
stateOut->blendState.logicOp =
static_cast<VkLogicOp>(mFragmentOutput.blendMaskAndLogic.bits.logicOp);
stateOut->blendState.attachmentCount =
static_cast<uint32_t>(mSharedNonVertexInput.renderPass.colorAttachmentRange());
stateOut->blendState.pAttachments = stateOut->blendAttachmentState.data();
// If this graphics pipeline is for the unresolve operation, correct the color attachment count
// for that subpass.
if ((mSharedNonVertexInput.renderPass.getColorUnresolveAttachmentMask().any() ||
mSharedNonVertexInput.renderPass.hasDepthStencilUnresolveAttachment()) &&
mSharedNonVertexInput.multisample.bits.subpass == 0)
{
stateOut->blendState.attachmentCount = static_cast<uint32_t>(
mSharedNonVertexInput.renderPass.getColorUnresolveAttachmentMask().count());
}
// Specify rasterization order for color when available and there is
// framebuffer fetch. This allows implementation of coherent framebuffer
// fetch / advanced blend.
//
// We can do better by setting the bit only when there is coherent
// framebuffer fetch, but getRenderPassFramebufferFetchMode does not
// distinguish coherent / non-coherent yet. Also, once an app uses
// framebufer fetch, we treat all render passes as if they use framebuffer
// fetch. This check is not very effective.
if (context->getFeatures().supportsRasterizationOrderAttachmentAccess.enabled &&
getRenderPassFramebufferFetchMode())
{
stateOut->blendState.flags |=
VK_PIPELINE_COLOR_BLEND_STATE_CREATE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_BIT_EXT;
}
const gl::DrawBufferMask blendEnableMask(
mFragmentOutput.blendMaskAndLogic.bits.blendEnableMask);
// Zero-init all states.
stateOut->blendAttachmentState = {};
const PackedColorBlendState &colorBlend = mFragmentOutput.blend;
for (uint32_t colorIndexGL = 0; colorIndexGL < stateOut->blendState.attachmentCount;
++colorIndexGL)
{
VkPipelineColorBlendAttachmentState &state = stateOut->blendAttachmentState[colorIndexGL];
if (blendEnableMask[colorIndexGL])
{
// To avoid triggering valid usage error, blending must be disabled for formats that do
// not have VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT feature bit set.
// From OpenGL ES clients, this means disabling blending for integer formats.
if (!angle::Format::Get(mSharedNonVertexInput.renderPass[colorIndexGL]).isInt())
{
ASSERT(!context->getRenderer()
->getFormat(mSharedNonVertexInput.renderPass[colorIndexGL])
.getActualRenderableImageFormat()
.isInt());
// The blend fixed-function is enabled with normal blend as well as advanced blend
// when the Vulkan extension is present. When emulating advanced blend in the
// shader, the blend fixed-function must be disabled.
const PackedColorBlendAttachmentState &packedBlendState =
colorBlend.attachments[colorIndexGL];
if (packedBlendState.colorBlendOp <= static_cast<uint8_t>(VK_BLEND_OP_MAX) ||
context->getFeatures().supportsBlendOperationAdvanced.enabled)
{
state.blendEnable = VK_TRUE;
UnpackBlendAttachmentState(packedBlendState, &state);
}
}
}
ASSERT(context->getRenderer()->getNativeExtensions().robustFragmentShaderOutputANGLE);
if ((mFragmentOutput.blendMaskAndLogic.bits.missingOutputsMask >> colorIndexGL & 1) != 0)
{
state.colorWriteMask = 0;
}
else
{
state.colorWriteMask =
Int4Array_Get<VkColorComponentFlags>(colorBlend.colorWriteMaskBits, colorIndexGL);
}
}
// Dynamic state
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
if (context->getRenderer()->useLogicOpDynamicState())
{
dynamicStateListOut->push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
}
void GraphicsPipelineDesc::updateVertexInput(ContextVk *contextVk,
GraphicsPipelineTransitionBits *transition,
uint32_t attribIndex,
GLuint stride,
GLuint divisor,
angle::FormatID format,
bool compressed,
GLuint relativeOffset)
{
PackedAttribDesc &packedAttrib = mVertexInput.vertex.attribs[attribIndex];
SetBitField(packedAttrib.divisor, divisor);
if (format == angle::FormatID::NONE)
{
UNIMPLEMENTED();
}
SetBitField(packedAttrib.format, format);
SetBitField(packedAttrib.compressed, compressed);
SetBitField(packedAttrib.offset, relativeOffset);
constexpr size_t kAttribBits = kPackedAttribDescSize * kBitsPerByte;
const size_t kBit =
ANGLE_GET_INDEXED_TRANSITION_BIT(mVertexInput.vertex.attribs, attribIndex, kAttribBits);
// Each attribute is 4 bytes, so only one transition bit needs to be set.
static_assert(kPackedAttribDescSize == kGraphicsPipelineDirtyBitBytes,
"Adjust transition bits");
transition->set(kBit);
if (!contextVk->getRenderer()->useVertexInputBindingStrideDynamicState())
{
SetBitField(mVertexInput.vertex.strides[attribIndex], stride);
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(
mVertexInput.vertex.strides, attribIndex,
sizeof(mVertexInput.vertex.strides[0]) * kBitsPerByte));
}
}
void GraphicsPipelineDesc::setVertexShaderComponentTypes(gl::AttributesMask activeAttribLocations,
gl::ComponentTypeMask componentTypeMask)
{
SetBitField(mVertexInput.inputAssembly.bits.programActiveAttributeLocations,
activeAttribLocations.bits());
const gl::ComponentTypeMask activeComponentTypeMask =
componentTypeMask & gl::GetActiveComponentTypeMask(activeAttribLocations);
SetBitField(mVertexInput.vertex.shaderAttribComponentType, activeComponentTypeMask.bits());
}
void GraphicsPipelineDesc::updateVertexShaderComponentTypes(
GraphicsPipelineTransitionBits *transition,
gl::AttributesMask activeAttribLocations,
gl::ComponentTypeMask componentTypeMask)
{
if (mVertexInput.inputAssembly.bits.programActiveAttributeLocations !=
activeAttribLocations.bits())
{
SetBitField(mVertexInput.inputAssembly.bits.programActiveAttributeLocations,
activeAttribLocations.bits());
transition->set(ANGLE_GET_TRANSITION_BIT(mVertexInput.inputAssembly.bits));
}
const gl::ComponentTypeMask activeComponentTypeMask =
componentTypeMask & gl::GetActiveComponentTypeMask(activeAttribLocations);
if (mVertexInput.vertex.shaderAttribComponentType != activeComponentTypeMask.bits())
{
SetBitField(mVertexInput.vertex.shaderAttribComponentType, activeComponentTypeMask.bits());
transition->set(ANGLE_GET_TRANSITION_BIT(mVertexInput.vertex.shaderAttribComponentType));
}
}
void GraphicsPipelineDesc::setTopology(gl::PrimitiveMode drawMode)
{
VkPrimitiveTopology vkTopology = gl_vk::GetPrimitiveTopology(drawMode);
SetBitField(mVertexInput.inputAssembly.bits.topology, vkTopology);
}
void GraphicsPipelineDesc::updateTopology(GraphicsPipelineTransitionBits *transition,
gl::PrimitiveMode drawMode)
{
setTopology(drawMode);
transition->set(ANGLE_GET_TRANSITION_BIT(mVertexInput.inputAssembly.bits));
}
void GraphicsPipelineDesc::updateDepthClipControl(GraphicsPipelineTransitionBits *transition,
bool negativeOneToOne)
{
SetBitField(mShaders.shaders.bits.viewportNegativeOneToOne, negativeOneToOne);
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updatePrimitiveRestartEnabled(GraphicsPipelineTransitionBits *transition,
bool primitiveRestartEnabled)
{
mVertexInput.inputAssembly.bits.primitiveRestartEnable =
static_cast<uint16_t>(primitiveRestartEnabled);
transition->set(ANGLE_GET_TRANSITION_BIT(mVertexInput.inputAssembly.bits));
}
void GraphicsPipelineDesc::updatePolygonMode(GraphicsPipelineTransitionBits *transition,
gl::PolygonMode polygonMode)
{
mShaders.shaders.bits.polygonMode = gl_vk::GetPolygonMode(polygonMode);
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateCullMode(GraphicsPipelineTransitionBits *transition,
const gl::RasterizerState &rasterState)
{
SetBitField(mShaders.shaders.bits.cullMode, gl_vk::GetCullMode(rasterState));
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateFrontFace(GraphicsPipelineTransitionBits *transition,
const gl::RasterizerState &rasterState,
bool invertFrontFace)
{
SetBitField(mShaders.shaders.bits.frontFace,
gl_vk::GetFrontFace(rasterState.frontFace, invertFrontFace));
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateRasterizerDiscardEnabled(
GraphicsPipelineTransitionBits *transition,
bool rasterizerDiscardEnabled)
{
mShaders.shaders.bits.rasterizerDiscardEnable = static_cast<uint32_t>(rasterizerDiscardEnabled);
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
uint32_t GraphicsPipelineDesc::getRasterizationSamples() const
{
return mSharedNonVertexInput.multisample.bits.rasterizationSamplesMinusOne + 1;
}
void GraphicsPipelineDesc::setRasterizationSamples(uint32_t rasterizationSamples)
{
ASSERT(rasterizationSamples > 0);
mSharedNonVertexInput.multisample.bits.rasterizationSamplesMinusOne = rasterizationSamples - 1;
}
void GraphicsPipelineDesc::updateRasterizationSamples(GraphicsPipelineTransitionBits *transition,
uint32_t rasterizationSamples)
{
setRasterizationSamples(rasterizationSamples);
transition->set(ANGLE_GET_TRANSITION_BIT(mSharedNonVertexInput.multisample.bits));
}
void GraphicsPipelineDesc::updateAlphaToCoverageEnable(GraphicsPipelineTransitionBits *transition,
bool enable)
{
mSharedNonVertexInput.multisample.bits.alphaToCoverageEnable = enable;
transition->set(ANGLE_GET_TRANSITION_BIT(mSharedNonVertexInput.multisample.bits));
}
void GraphicsPipelineDesc::updateAlphaToOneEnable(GraphicsPipelineTransitionBits *transition,
bool enable)
{
mSharedNonVertexInput.multisample.bits.alphaToOneEnable = enable;
transition->set(ANGLE_GET_TRANSITION_BIT(mSharedNonVertexInput.multisample.bits));
}
void GraphicsPipelineDesc::updateSampleMask(GraphicsPipelineTransitionBits *transition,
uint32_t maskNumber,
uint32_t mask)
{
ASSERT(maskNumber == 0);
SetBitField(mSharedNonVertexInput.multisample.bits.sampleMask, mask);
transition->set(ANGLE_GET_TRANSITION_BIT(mSharedNonVertexInput.multisample.bits));
}
void GraphicsPipelineDesc::updateSampleShading(GraphicsPipelineTransitionBits *transition,
bool enable,
float value)
{
mSharedNonVertexInput.multisample.bits.sampleShadingEnable = enable;
if (enable)
{
SetBitField(mSharedNonVertexInput.multisample.bits.minSampleShading,
static_cast<uint16_t>(value * kMinSampleShadingScale));
}
else
{
mSharedNonVertexInput.multisample.bits.minSampleShading = kMinSampleShadingScale;
}
transition->set(ANGLE_GET_TRANSITION_BIT(mSharedNonVertexInput.multisample.bits));
}
void GraphicsPipelineDesc::setSingleBlend(uint32_t colorIndexGL,
bool enabled,
VkBlendOp op,
VkBlendFactor srcFactor,
VkBlendFactor dstFactor)
{
mFragmentOutput.blendMaskAndLogic.bits.blendEnableMask |=
static_cast<uint8_t>(1 << colorIndexGL);
PackedColorBlendAttachmentState &blendAttachmentState =
mFragmentOutput.blend.attachments[colorIndexGL];
SetBitField(blendAttachmentState.colorBlendOp, op);
SetBitField(blendAttachmentState.alphaBlendOp, op);
SetBitField(blendAttachmentState.srcColorBlendFactor, srcFactor);
SetBitField(blendAttachmentState.dstColorBlendFactor, dstFactor);
SetBitField(blendAttachmentState.srcAlphaBlendFactor, VK_BLEND_FACTOR_ZERO);
SetBitField(blendAttachmentState.dstAlphaBlendFactor, VK_BLEND_FACTOR_ONE);
}
void GraphicsPipelineDesc::updateBlendEnabled(GraphicsPipelineTransitionBits *transition,
gl::DrawBufferMask blendEnabledMask)
{
SetBitField(mFragmentOutput.blendMaskAndLogic.bits.blendEnableMask, blendEnabledMask.bits());
transition->set(ANGLE_GET_TRANSITION_BIT(mFragmentOutput.blendMaskAndLogic.bits));
}
void GraphicsPipelineDesc::updateBlendEquations(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt,
gl::DrawBufferMask attachmentMask)
{
constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex : attachmentMask)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mFragmentOutput.blend.attachments[attachmentIndex];
blendAttachmentState.colorBlendOp =
PackGLBlendOp(blendStateExt.getEquationColorIndexed(attachmentIndex));
blendAttachmentState.alphaBlendOp =
PackGLBlendOp(blendStateExt.getEquationAlphaIndexed(attachmentIndex));
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mFragmentOutput.blend.attachments,
attachmentIndex, kSizeBits));
}
}
void GraphicsPipelineDesc::updateBlendFuncs(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt,
gl::DrawBufferMask attachmentMask)
{
constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex : attachmentMask)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mFragmentOutput.blend.attachments[attachmentIndex];
blendAttachmentState.srcColorBlendFactor =
PackGLBlendFactor(blendStateExt.getSrcColorIndexed(attachmentIndex));
blendAttachmentState.dstColorBlendFactor =
PackGLBlendFactor(blendStateExt.getDstColorIndexed(attachmentIndex));
blendAttachmentState.srcAlphaBlendFactor =
PackGLBlendFactor(blendStateExt.getSrcAlphaIndexed(attachmentIndex));
blendAttachmentState.dstAlphaBlendFactor =
PackGLBlendFactor(blendStateExt.getDstAlphaIndexed(attachmentIndex));
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mFragmentOutput.blend.attachments,
attachmentIndex, kSizeBits));
}
}
void GraphicsPipelineDesc::resetBlendFuncsAndEquations(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt,
gl::DrawBufferMask previousAttachmentsMask,
gl::DrawBufferMask newAttachmentsMask)
{
// A framebuffer with attachments in P was bound, and now one with attachments in N is bound.
// We need to clear blend funcs and equations for attachments in P that are not in N. That is
// attachments in P&~N.
const gl::DrawBufferMask attachmentsToClear = previousAttachmentsMask & ~newAttachmentsMask;
// We also need to restore blend funcs and equations for attachments in N that are not in P.
const gl::DrawBufferMask attachmentsToAdd = newAttachmentsMask & ~previousAttachmentsMask;
constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex : attachmentsToClear)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mFragmentOutput.blend.attachments[attachmentIndex];
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mFragmentOutput.blend.attachments,
attachmentIndex, kSizeBits));
}
if (attachmentsToAdd.any())
{
updateBlendFuncs(transition, blendStateExt, attachmentsToAdd);
updateBlendEquations(transition, blendStateExt, attachmentsToAdd);
}
}
void GraphicsPipelineDesc::setColorWriteMasks(gl::BlendStateExt::ColorMaskStorage::Type colorMasks,
const gl::DrawBufferMask &alphaMask,
const gl::DrawBufferMask &enabledDrawBuffers)
{
for (uint32_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
colorIndexGL++)
{
uint8_t colorMask =
gl::BlendStateExt::ColorMaskStorage::GetValueIndexed(colorIndexGL, colorMasks);
uint8_t mask = 0;
if (enabledDrawBuffers.test(colorIndexGL))
{
mask = alphaMask[colorIndexGL] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask;
}
Int4Array_Set(mFragmentOutput.blend.colorWriteMaskBits, colorIndexGL, mask);
}
}
void GraphicsPipelineDesc::setSingleColorWriteMask(uint32_t colorIndexGL,
VkColorComponentFlags colorComponentFlags)
{
uint8_t colorMask = static_cast<uint8_t>(colorComponentFlags);
Int4Array_Set(mFragmentOutput.blend.colorWriteMaskBits, colorIndexGL, colorMask);
}
void GraphicsPipelineDesc::updateColorWriteMasks(
GraphicsPipelineTransitionBits *transition,
gl::BlendStateExt::ColorMaskStorage::Type colorMasks,
const gl::DrawBufferMask &alphaMask,
const gl::DrawBufferMask &enabledDrawBuffers)
{
setColorWriteMasks(colorMasks, alphaMask, enabledDrawBuffers);
for (size_t colorIndexGL = 0; colorIndexGL < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS;
colorIndexGL++)
{
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mFragmentOutput.blend.colorWriteMaskBits,
colorIndexGL, 4));
}
}
void GraphicsPipelineDesc::updateMissingOutputsMask(GraphicsPipelineTransitionBits *transition,
gl::DrawBufferMask missingOutputsMask)
{
if (mFragmentOutput.blendMaskAndLogic.bits.missingOutputsMask != missingOutputsMask.bits())
{
SetBitField(mFragmentOutput.blendMaskAndLogic.bits.missingOutputsMask,
missingOutputsMask.bits());
transition->set(ANGLE_GET_TRANSITION_BIT(mFragmentOutput.blendMaskAndLogic.bits));
}
}
void GraphicsPipelineDesc::updateLogicOpEnabled(GraphicsPipelineTransitionBits *transition,
bool enable)
{
mFragmentOutput.blendMaskAndLogic.bits.logicOpEnable = enable;
transition->set(ANGLE_GET_TRANSITION_BIT(mFragmentOutput.blendMaskAndLogic.bits));
}
void GraphicsPipelineDesc::updateLogicOp(GraphicsPipelineTransitionBits *transition,
VkLogicOp logicOp)
{
SetBitField(mFragmentOutput.blendMaskAndLogic.bits.logicOp, logicOp);
transition->set(ANGLE_GET_TRANSITION_BIT(mFragmentOutput.blendMaskAndLogic.bits));
}
void GraphicsPipelineDesc::setDepthTestEnabled(bool enabled)
{
mShaders.shaders.bits.depthTest = enabled;
}
void GraphicsPipelineDesc::setDepthWriteEnabled(bool enabled)
{
mShaders.shaders.bits.depthWrite = enabled;
}
void GraphicsPipelineDesc::setDepthFunc(VkCompareOp op)
{
SetBitField(mShaders.shaders.bits.depthCompareOp, op);
}
void GraphicsPipelineDesc::setDepthClampEnabled(bool enabled)
{
mShaders.shaders.bits.depthClampEnable = enabled;
}
void GraphicsPipelineDesc::setStencilTestEnabled(bool enabled)
{
mShaders.shaders.bits.stencilTest = enabled;
}
void GraphicsPipelineDesc::setStencilFrontFuncs(VkCompareOp compareOp)
{
SetBitField(mShaders.shaders.front.compare, compareOp);
}
void GraphicsPipelineDesc::setStencilBackFuncs(VkCompareOp compareOp)
{
SetBitField(mShaders.shaders.back.compare, compareOp);
}
void GraphicsPipelineDesc::setStencilFrontOps(VkStencilOp failOp,
VkStencilOp passOp,
VkStencilOp depthFailOp)
{
SetBitField(mShaders.shaders.front.fail, failOp);
SetBitField(mShaders.shaders.front.pass, passOp);
SetBitField(mShaders.shaders.front.depthFail, depthFailOp);
}
void GraphicsPipelineDesc::setStencilBackOps(VkStencilOp failOp,
VkStencilOp passOp,
VkStencilOp depthFailOp)
{
SetBitField(mShaders.shaders.back.fail, failOp);
SetBitField(mShaders.shaders.back.pass, passOp);
SetBitField(mShaders.shaders.back.depthFail, depthFailOp);
}
void GraphicsPipelineDesc::updateDepthTestEnabled(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState,
const gl::Framebuffer *drawFramebuffer)
{
// Only enable the depth test if the draw framebuffer has a depth buffer. It's possible that
// we're emulating a stencil-only buffer with a depth-stencil buffer
setDepthTestEnabled(depthStencilState.depthTest && drawFramebuffer->hasDepth());
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateDepthFunc(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setDepthFunc(gl_vk::GetCompareOp(depthStencilState.depthFunc));
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateDepthClampEnabled(GraphicsPipelineTransitionBits *transition,
bool enabled)
{
setDepthClampEnabled(enabled);
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateSurfaceRotation(GraphicsPipelineTransitionBits *transition,
bool isRotatedAspectRatio)
{
SetBitField(mShaders.shaders.bits.surfaceRotation, isRotatedAspectRatio);
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateDepthWriteEnabled(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState,
const gl::Framebuffer *drawFramebuffer)
{
// Don't write to depth buffers that should not exist
const bool depthWriteEnabled =
drawFramebuffer->hasDepth() && depthStencilState.depthTest && depthStencilState.depthMask;
if (static_cast<bool>(mShaders.shaders.bits.depthWrite) != depthWriteEnabled)
{
setDepthWriteEnabled(depthWriteEnabled);
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
}
void GraphicsPipelineDesc::updateStencilTestEnabled(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState,
const gl::Framebuffer *drawFramebuffer)
{
// Only enable the stencil test if the draw framebuffer has a stencil buffer. It's possible
// that we're emulating a depth-only buffer with a depth-stencil buffer
setStencilTestEnabled(depthStencilState.stencilTest && drawFramebuffer->hasStencil());
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateStencilFrontFuncs(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setStencilFrontFuncs(gl_vk::GetCompareOp(depthStencilState.stencilFunc));
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.front));
}
void GraphicsPipelineDesc::updateStencilBackFuncs(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setStencilBackFuncs(gl_vk::GetCompareOp(depthStencilState.stencilBackFunc));
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.back));
}
void GraphicsPipelineDesc::updateStencilFrontOps(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setStencilFrontOps(gl_vk::GetStencilOp(depthStencilState.stencilFail),
gl_vk::GetStencilOp(depthStencilState.stencilPassDepthPass),
gl_vk::GetStencilOp(depthStencilState.stencilPassDepthFail));
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.front));
}
void GraphicsPipelineDesc::updateStencilBackOps(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setStencilBackOps(gl_vk::GetStencilOp(depthStencilState.stencilBackFail),
gl_vk::GetStencilOp(depthStencilState.stencilBackPassDepthPass),
gl_vk::GetStencilOp(depthStencilState.stencilBackPassDepthFail));
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.back));
}
void GraphicsPipelineDesc::updatePolygonOffsetEnabled(GraphicsPipelineTransitionBits *transition,
bool enabled)
{
mShaders.shaders.bits.depthBiasEnable = enabled;
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::setRenderPassDesc(const RenderPassDesc &renderPassDesc)
{
mSharedNonVertexInput.renderPass = renderPassDesc;
}
void GraphicsPipelineDesc::updateSubpass(GraphicsPipelineTransitionBits *transition,
uint32_t subpass)
{
if (mSharedNonVertexInput.multisample.bits.subpass != subpass)
{
SetBitField(mSharedNonVertexInput.multisample.bits.subpass, subpass);
transition->set(ANGLE_GET_TRANSITION_BIT(mSharedNonVertexInput.multisample.bits));
}
}
void GraphicsPipelineDesc::updatePatchVertices(GraphicsPipelineTransitionBits *transition,
GLuint value)
{
SetBitField(mShaders.shaders.bits.patchVertices, value);
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::resetSubpass(GraphicsPipelineTransitionBits *transition)
{
updateSubpass(transition, 0);
}
void GraphicsPipelineDesc::nextSubpass(GraphicsPipelineTransitionBits *transition)
{
updateSubpass(transition, mSharedNonVertexInput.multisample.bits.subpass + 1);
}
void GraphicsPipelineDesc::setSubpass(uint32_t subpass)
{
SetBitField(mSharedNonVertexInput.multisample.bits.subpass, subpass);
}
uint32_t GraphicsPipelineDesc::getSubpass() const
{
return mSharedNonVertexInput.multisample.bits.subpass;
}
void GraphicsPipelineDesc::updateEmulatedDitherControl(GraphicsPipelineTransitionBits *transition,
uint16_t value)
{
// Make sure we don't waste time resetting this to zero in the common no-dither case.
ASSERT(value != 0 || mShaders.shaders.emulatedDitherControl != 0);
mShaders.shaders.emulatedDitherControl = value;
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.emulatedDitherControl));
}
void GraphicsPipelineDesc::updateNonZeroStencilWriteMaskWorkaround(
GraphicsPipelineTransitionBits *transition,
bool enabled)
{
mShaders.shaders.bits.nonZeroStencilWriteMaskWorkaround = enabled;
transition->set(ANGLE_GET_TRANSITION_BIT(mShaders.shaders.bits));
}
void GraphicsPipelineDesc::updateRenderPassDesc(GraphicsPipelineTransitionBits *transition,
const RenderPassDesc &renderPassDesc)
{
setRenderPassDesc(renderPassDesc);
// The RenderPass is a special case where it spans multiple bits but has no member.
constexpr size_t kFirstBit =
offsetof(GraphicsPipelineDesc, mSharedNonVertexInput.renderPass) >> kTransitionByteShift;
constexpr size_t kBitCount = kRenderPassDescSize >> kTransitionByteShift;
for (size_t bit = 0; bit < kBitCount; ++bit)
{
transition->set(kFirstBit + bit);
}
}
void GraphicsPipelineDesc::setRenderPassSampleCount(GLint samples)
{
mSharedNonVertexInput.renderPass.setSamples(samples);
}
void GraphicsPipelineDesc::setRenderPassFramebufferFetchMode(bool hasFramebufferFetch)
{
mSharedNonVertexInput.renderPass.setFramebufferFetchMode(hasFramebufferFetch);
}
void GraphicsPipelineDesc::setRenderPassColorAttachmentFormat(size_t colorIndexGL,
angle::FormatID formatID)
{
mSharedNonVertexInput.renderPass.packColorAttachment(colorIndexGL, formatID);
}
void GraphicsPipelineDesc::setRenderPassFoveation(bool isFoveated)
{
mSharedNonVertexInput.renderPass.setFragmentShadingAttachment(isFoveated);
}
// AttachmentOpsArray implementation.
AttachmentOpsArray::AttachmentOpsArray()
{
memset(&mOps, 0, sizeof(PackedAttachmentOpsDesc) * mOps.size());
}
AttachmentOpsArray::~AttachmentOpsArray() = default;
AttachmentOpsArray::AttachmentOpsArray(const AttachmentOpsArray &other)
{
memcpy(&mOps, &other.mOps, sizeof(PackedAttachmentOpsDesc) * mOps.size());
}
AttachmentOpsArray &AttachmentOpsArray::operator=(const AttachmentOpsArray &other)
{
memcpy(&mOps, &other.mOps, sizeof(PackedAttachmentOpsDesc) * mOps.size());
return *this;
}
const PackedAttachmentOpsDesc &AttachmentOpsArray::operator[](PackedAttachmentIndex index) const
{
return mOps[index.get()];
}
PackedAttachmentOpsDesc &AttachmentOpsArray::operator[](PackedAttachmentIndex index)
{
return mOps[index.get()];
}
void AttachmentOpsArray::initWithLoadStore(PackedAttachmentIndex index,
ImageLayout initialLayout,
ImageLayout finalLayout)
{
setLayouts(index, initialLayout, finalLayout);
setOps(index, RenderPassLoadOp::Load, RenderPassStoreOp::Store);
setStencilOps(index, RenderPassLoadOp::Load, RenderPassStoreOp::Store);
}
void AttachmentOpsArray::setLayouts(PackedAttachmentIndex index,
ImageLayout initialLayout,
ImageLayout finalLayout)
{
PackedAttachmentOpsDesc &ops = mOps[index.get()];
SetBitField(ops.initialLayout, initialLayout);
SetBitField(ops.finalLayout, finalLayout);
}
void AttachmentOpsArray::setOps(PackedAttachmentIndex index,
RenderPassLoadOp loadOp,
RenderPassStoreOp storeOp)
{
PackedAttachmentOpsDesc &ops = mOps[index.get()];
SetBitField(ops.loadOp, loadOp);
SetBitField(ops.storeOp, storeOp);
ops.isInvalidated = false;
}
void AttachmentOpsArray::setStencilOps(PackedAttachmentIndex index,
RenderPassLoadOp loadOp,
RenderPassStoreOp storeOp)
{
PackedAttachmentOpsDesc &ops = mOps[index.get()];
SetBitField(ops.stencilLoadOp, loadOp);
SetBitField(ops.stencilStoreOp, storeOp);
ops.isStencilInvalidated = false;
}
void AttachmentOpsArray::setClearOp(PackedAttachmentIndex index)
{
PackedAttachmentOpsDesc &ops = mOps[index.get()];
SetBitField(ops.loadOp, RenderPassLoadOp::Clear);
}
void AttachmentOpsArray::setClearStencilOp(PackedAttachmentIndex index)
{
PackedAttachmentOpsDesc &ops = mOps[index.get()];
SetBitField(ops.stencilLoadOp, RenderPassLoadOp::Clear);
}
size_t AttachmentOpsArray::hash() const
{
return angle::ComputeGenericHash(mOps);
}
bool operator==(const AttachmentOpsArray &lhs, const AttachmentOpsArray &rhs)
{
return memcmp(&lhs, &rhs, sizeof(AttachmentOpsArray)) == 0;
}
// DescriptorSetLayoutDesc implementation.
DescriptorSetLayoutDesc::DescriptorSetLayoutDesc()
: mPackedDescriptorSetLayout{}, mValidDescriptorSetLayoutIndexMask()
{
mImmutableSamplers.fill(VK_NULL_HANDLE);
}
DescriptorSetLayoutDesc::~DescriptorSetLayoutDesc() = default;
DescriptorSetLayoutDesc::DescriptorSetLayoutDesc(const DescriptorSetLayoutDesc &other) = default;
DescriptorSetLayoutDesc &DescriptorSetLayoutDesc::operator=(const DescriptorSetLayoutDesc &other) =
default;
size_t DescriptorSetLayoutDesc::hash() const
{
size_t genericHash = angle::ComputeGenericHash(mValidDescriptorSetLayoutIndexMask);
for (size_t bindingIndex : mValidDescriptorSetLayoutIndexMask)
{
genericHash ^= angle::ComputeGenericHash(mPackedDescriptorSetLayout[bindingIndex]) ^
angle::ComputeGenericHash(mImmutableSamplers[bindingIndex]);
}
return genericHash;
}
bool DescriptorSetLayoutDesc::operator==(const DescriptorSetLayoutDesc &other) const
{
return memcmp(&mPackedDescriptorSetLayout, &other.mPackedDescriptorSetLayout,
sizeof(mPackedDescriptorSetLayout)) == 0 &&
memcmp(&mImmutableSamplers, &other.mImmutableSamplers, sizeof(mImmutableSamplers)) == 0;
}
void DescriptorSetLayoutDesc::update(uint32_t bindingIndex,
VkDescriptorType descriptorType,
uint32_t count,
VkShaderStageFlags stages,
const Sampler *immutableSampler)
{
ASSERT(static_cast<size_t>(descriptorType) < std::numeric_limits<uint16_t>::max());
ASSERT(count < std::numeric_limits<uint16_t>::max());
PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
SetBitField(packedBinding.type, descriptorType);
SetBitField(packedBinding.count, count);
SetBitField(packedBinding.stages, stages);
if (immutableSampler)
{
ASSERT(count == 1);
ASSERT(bindingIndex < gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES);
mImmutableSamplers[bindingIndex] = immutableSampler->getHandle();
}
mValidDescriptorSetLayoutIndexMask.set(bindingIndex, count > 0);
}
void DescriptorSetLayoutDesc::unpackBindings(DescriptorSetLayoutBindingVector *bindings) const
{
for (size_t bindingIndex : mValidDescriptorSetLayoutIndexMask)
{
const PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
ASSERT(packedBinding.count != 0);
VkDescriptorSetLayoutBinding binding = {};
binding.binding = static_cast<uint32_t>(bindingIndex);
binding.descriptorCount = packedBinding.count;
binding.descriptorType = static_cast<VkDescriptorType>(packedBinding.type);
binding.stageFlags = static_cast<VkShaderStageFlags>(packedBinding.stages);
if (mImmutableSamplers[bindingIndex] != VK_NULL_HANDLE)
{
ASSERT(packedBinding.count == 1);
ASSERT(bindingIndex < gl::IMPLEMENTATION_MAX_ACTIVE_TEXTURES);
binding.pImmutableSamplers = &mImmutableSamplers[bindingIndex];
}
bindings->push_back(binding);
}
}
// PipelineLayoutDesc implementation.
PipelineLayoutDesc::PipelineLayoutDesc()
: mDescriptorSetLayouts{}, mPushConstantRange{}, mPadding(0)
{}
PipelineLayoutDesc::~PipelineLayoutDesc() = default;
PipelineLayoutDesc::PipelineLayoutDesc(const PipelineLayoutDesc &other) = default;
PipelineLayoutDesc &PipelineLayoutDesc::operator=(const PipelineLayoutDesc &rhs)
{
mDescriptorSetLayouts = rhs.mDescriptorSetLayouts;
mPushConstantRange = rhs.mPushConstantRange;
return *this;
}
size_t PipelineLayoutDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool PipelineLayoutDesc::operator==(const PipelineLayoutDesc &other) const
{
return memcmp(this, &other, sizeof(PipelineLayoutDesc)) == 0;
}
void PipelineLayoutDesc::updateDescriptorSetLayout(DescriptorSetIndex setIndex,
const DescriptorSetLayoutDesc &desc)
{
mDescriptorSetLayouts[setIndex] = desc;
}
void PipelineLayoutDesc::updatePushConstantRange(VkShaderStageFlags stageMask,
uint32_t offset,
uint32_t size)
{
SetBitField(mPushConstantRange.offset, offset);
SetBitField(mPushConstantRange.size, size);
SetBitField(mPushConstantRange.stageMask, stageMask);
}
// CreateMonolithicPipelineTask implementation.
CreateMonolithicPipelineTask::CreateMonolithicPipelineTask(
Renderer *renderer,
const PipelineCacheAccess &pipelineCache,
const PipelineLayout &pipelineLayout,
const ShaderModuleMap &shaders,
const SpecializationConstants &specConsts,
const GraphicsPipelineDesc &desc)
: Context(renderer),
mPipelineCache(pipelineCache),
mCompatibleRenderPass(nullptr),
mPipelineLayout(pipelineLayout),
mShaders(shaders),
mSpecConsts(specConsts),
mDesc(desc),
mResult(VK_NOT_READY),
mFeedback(CacheLookUpFeedback::None)
{}
void CreateMonolithicPipelineTask::setCompatibleRenderPass(const RenderPass *compatibleRenderPass)
{
mCompatibleRenderPass = compatibleRenderPass;
}
void CreateMonolithicPipelineTask::operator()()
{
ANGLE_TRACE_EVENT0("gpu.angle", "CreateMonolithicPipelineTask");
mResult = mDesc.initializePipeline(this, &mPipelineCache, vk::GraphicsPipelineSubset::Complete,
*mCompatibleRenderPass, mPipelineLayout, mShaders,
mSpecConsts, &mPipeline, &mFeedback);
if (mRenderer->getFeatures().slowDownMonolithicPipelineCreationForTesting.enabled)
{
constexpr double kSlowdownTime = 0.05;
double startTime = angle::GetCurrentSystemTime();
while (angle::GetCurrentSystemTime() - startTime < kSlowdownTime)
{
// Busy waiting
}
}
}
void CreateMonolithicPipelineTask::handleError(VkResult result,
const char *file,
const char *function,
unsigned int line)
{
UNREACHABLE();
}
// WaitableMonolithicPipelineCreationTask implementation
WaitableMonolithicPipelineCreationTask::~WaitableMonolithicPipelineCreationTask()
{
ASSERT(!mWaitableEvent);
ASSERT(!mTask);
}
// PipelineHelper implementation.
PipelineHelper::PipelineHelper() = default;
PipelineHelper::~PipelineHelper() = default;
void PipelineHelper::destroy(VkDevice device)
{
mPipeline.destroy(device);
mLinkedPipelineToRelease.destroy(device);
// If there is a pending task, wait for it before destruction.
if (mMonolithicPipelineCreationTask.isValid())
{
if (mMonolithicPipelineCreationTask.isPosted())
{
mMonolithicPipelineCreationTask.wait();
mMonolithicPipelineCreationTask.getTask()->getPipeline().destroy(device);
}
mMonolithicPipelineCreationTask.reset();
}
reset();
}
void PipelineHelper::release(Context *context)
{
Renderer *renderer = context->getRenderer();
renderer->collectGarbage(mUse, &mPipeline);
renderer->collectGarbage(mUse, &mLinkedPipelineToRelease);
// If there is a pending task, wait for it before release.
if (mMonolithicPipelineCreationTask.isValid())
{
if (mMonolithicPipelineCreationTask.isPosted())
{
mMonolithicPipelineCreationTask.wait();
renderer->collectGarbage(mUse,
&mMonolithicPipelineCreationTask.getTask()->getPipeline());
}
mMonolithicPipelineCreationTask.reset();
}
reset();
}
void PipelineHelper::reset()
{
mCacheLookUpFeedback = CacheLookUpFeedback::None;
mMonolithicCacheLookUpFeedback = CacheLookUpFeedback::None;
mLinkedShaders = nullptr;
}
angle::Result PipelineHelper::getPreferredPipeline(ContextVk *contextVk,
const Pipeline **pipelineOut)
{
if (mMonolithicPipelineCreationTask.isValid())
{
// If there is a monolithic task pending, attempt to post it if not already. Once the task
// is done, retrieve the results and replace the pipeline.
if (!mMonolithicPipelineCreationTask.isPosted())
{
ANGLE_TRY(contextVk->getShareGroup()->scheduleMonolithicPipelineCreationTask(
contextVk, &mMonolithicPipelineCreationTask));
}
else if (mMonolithicPipelineCreationTask.isReady())
{
CreateMonolithicPipelineTask *task = &*mMonolithicPipelineCreationTask.getTask();
ANGLE_VK_TRY(contextVk, task->getResult());
mMonolithicCacheLookUpFeedback = task->getFeedback();
// The pipeline will not be used anymore. Every context that has used this pipeline has
// already updated the serial.
mLinkedPipelineToRelease = std::move(mPipeline);
// Replace it with the monolithic one.
mPipeline = std::move(task->getPipeline());
mLinkedShaders = nullptr;
mMonolithicPipelineCreationTask.reset();
++contextVk->getPerfCounters().monolithicPipelineCreation;
}
}
*pipelineOut = &mPipeline;
return angle::Result::Continue;
}
void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits,
const GraphicsPipelineDesc *desc,
PipelineHelper *pipeline)
{
mTransitions.emplace_back(bits, desc, pipeline);
}
void PipelineHelper::setLinkedLibraryReferences(vk::PipelineHelper *shadersPipeline)
{
mLinkedShaders = shadersPipeline;
}
void PipelineHelper::retainInRenderPass(RenderPassCommandBufferHelper *renderPassCommands)
{
renderPassCommands->retainResource(this);
// Keep references to the linked libraries alive. Note that currently only need to do this for
// the shaders library, as the vertex and fragment libraries live in the context until
// destruction.
if (mLinkedShaders != nullptr)
{
renderPassCommands->retainResource(mLinkedShaders);
}
}
// FramebufferHelper implementation.
FramebufferHelper::FramebufferHelper() = default;
FramebufferHelper::~FramebufferHelper() = default;
FramebufferHelper::FramebufferHelper(FramebufferHelper &&other) : Resource(std::move(other))
{
mFramebuffer = std::move(other.mFramebuffer);
}
FramebufferHelper &FramebufferHelper::operator=(FramebufferHelper &&other)
{
Resource::operator=(std::move(other));
std::swap(mFramebuffer, other.mFramebuffer);
return *this;
}
angle::Result FramebufferHelper::init(Context *context, const VkFramebufferCreateInfo &createInfo)
{
ANGLE_VK_TRY(context, mFramebuffer.init(context->getDevice(), createInfo));
return angle::Result::Continue;
}
void FramebufferHelper::destroy(Renderer *renderer)
{
mFramebuffer.destroy(renderer->getDevice());
}
void FramebufferHelper::release(ContextVk *contextVk)
{
contextVk->addGarbage(&mFramebuffer);
}
// DescriptorSetDesc implementation.
size_t DescriptorSetDesc::hash() const
{
if (mDescriptorInfos.empty())
{
return 0;
}
return angle::ComputeGenericHash(mDescriptorInfos.data(),
sizeof(mDescriptorInfos[0]) * mDescriptorInfos.size());
}
// FramebufferDesc implementation.
FramebufferDesc::FramebufferDesc()
{
reset();
}
FramebufferDesc::~FramebufferDesc() = default;
FramebufferDesc::FramebufferDesc(const FramebufferDesc &other) = default;
FramebufferDesc &FramebufferDesc::operator=(const FramebufferDesc &other) = default;
void FramebufferDesc::update(uint32_t index, ImageOrBufferViewSubresourceSerial serial)
{
static_assert(kMaxFramebufferAttachments + 1 < std::numeric_limits<uint8_t>::max(),
"mMaxIndex size is too small");
ASSERT(index < kMaxFramebufferAttachments);
mSerials[index] = serial;
if (serial.viewSerial.valid())
{
SetBitField(mMaxIndex, std::max(mMaxIndex, static_cast<uint16_t>(index + 1)));
}
}
void FramebufferDesc::updateColor(uint32_t index, ImageOrBufferViewSubresourceSerial serial)
{
update(kFramebufferDescColorIndexOffset + index, serial);
}
void FramebufferDesc::updateColorResolve(uint32_t index, ImageOrBufferViewSubresourceSerial serial)
{
update(kFramebufferDescColorResolveIndexOffset + index, serial);
}
void FramebufferDesc::updateUnresolveMask(FramebufferNonResolveAttachmentMask unresolveMask)
{
SetBitField(mUnresolveAttachmentMask, unresolveMask.bits());
}
void FramebufferDesc::updateDepthStencil(ImageOrBufferViewSubresourceSerial serial)
{
update(kFramebufferDescDepthStencilIndex, serial);
}
void FramebufferDesc::updateDepthStencilResolve(ImageOrBufferViewSubresourceSerial serial)
{
update(kFramebufferDescDepthStencilResolveIndexOffset, serial);
}
void FramebufferDesc::updateFragmentShadingRate(ImageOrBufferViewSubresourceSerial serial)
{
update(kFramebufferDescFragmentShadingRateAttachmentIndexOffset, serial);
}
bool FramebufferDesc::hasFragmentShadingRateAttachment() const
{
return mSerials[kFramebufferDescFragmentShadingRateAttachmentIndexOffset].viewSerial.valid();
}
size_t FramebufferDesc::hash() const
{
return angle::ComputeGenericHash(&mSerials, sizeof(mSerials[0]) * mMaxIndex) ^
mHasFramebufferFetch << 26 ^ mIsRenderToTexture << 25 ^ mLayerCount << 16 ^
mUnresolveAttachmentMask;
}
void FramebufferDesc::reset()
{
mMaxIndex = 0;
mHasFramebufferFetch = false;
mLayerCount = 0;
mSrgbWriteControlMode = 0;
mUnresolveAttachmentMask = 0;
mIsRenderToTexture = 0;
memset(&mSerials, 0, sizeof(mSerials));
}
bool FramebufferDesc::operator==(const FramebufferDesc &other) const
{
if (mMaxIndex != other.mMaxIndex || mLayerCount != other.mLayerCount ||
mUnresolveAttachmentMask != other.mUnresolveAttachmentMask ||
mHasFramebufferFetch != other.mHasFramebufferFetch ||
mSrgbWriteControlMode != other.mSrgbWriteControlMode ||
mIsRenderToTexture != other.mIsRenderToTexture)
{
return false;
}
size_t validRegionSize = sizeof(mSerials[0]) * mMaxIndex;
return memcmp(&mSerials, &other.mSerials, validRegionSize) == 0;
}
uint32_t FramebufferDesc::attachmentCount() const
{
uint32_t count = 0;
for (const ImageOrBufferViewSubresourceSerial &serial : mSerials)
{
if (serial.viewSerial.valid())
{
count++;
}
}
return count;
}
FramebufferNonResolveAttachmentMask FramebufferDesc::getUnresolveAttachmentMask() const
{
return FramebufferNonResolveAttachmentMask(mUnresolveAttachmentMask);
}
void FramebufferDesc::updateLayerCount(uint32_t layerCount)
{
SetBitField(mLayerCount, layerCount);
}
void FramebufferDesc::setFramebufferFetchMode(bool hasFramebufferFetch)
{
SetBitField(mHasFramebufferFetch, hasFramebufferFetch);
}
void FramebufferDesc::updateRenderToTexture(bool isRenderToTexture)
{
SetBitField(mIsRenderToTexture, isRenderToTexture);
}
// YcbcrConversionDesc implementation
YcbcrConversionDesc::YcbcrConversionDesc()
{
reset();
}
YcbcrConversionDesc::~YcbcrConversionDesc() = default;
YcbcrConversionDesc::YcbcrConversionDesc(const YcbcrConversionDesc &other) = default;
YcbcrConversionDesc &YcbcrConversionDesc::operator=(const YcbcrConversionDesc &rhs) = default;
size_t YcbcrConversionDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool YcbcrConversionDesc::operator==(const YcbcrConversionDesc &other) const
{
return memcmp(this, &other, sizeof(YcbcrConversionDesc)) == 0;
}
void YcbcrConversionDesc::reset()
{
mExternalOrVkFormat = 0;
mIsExternalFormat = 0;
mConversionModel = 0;
mColorRange = 0;
mXChromaOffset = 0;
mYChromaOffset = 0;
mChromaFilter = 0;
mRSwizzle = 0;
mGSwizzle = 0;
mBSwizzle = 0;
mASwizzle = 0;
mLinearFilterSupported = 0;
mPadding = 0;
mReserved = 0;
}
void YcbcrConversionDesc::update(Renderer *renderer,
uint64_t externalFormat,
VkSamplerYcbcrModelConversion conversionModel,
VkSamplerYcbcrRange colorRange,
VkChromaLocation xChromaOffset,
VkChromaLocation yChromaOffset,
VkFilter chromaFilter,
VkComponentMapping components,
angle::FormatID intendedFormatID,
YcbcrLinearFilterSupport linearFilterSupported)
{
const vk::Format &vkFormat = renderer->getFormat(intendedFormatID);
ASSERT(externalFormat != 0 || vkFormat.getIntendedFormat().isYUV);
SetBitField(mIsExternalFormat, (externalFormat) ? 1 : 0);
SetBitField(mLinearFilterSupported,
linearFilterSupported == YcbcrLinearFilterSupport::Supported);
mExternalOrVkFormat = (externalFormat)
? externalFormat
: vkFormat.getActualImageVkFormat(vk::ImageAccess::SampleOnly);
updateChromaFilter(renderer, chromaFilter);
SetBitField(mConversionModel, conversionModel);
SetBitField(mColorRange, colorRange);
SetBitField(mXChromaOffset, xChromaOffset);
SetBitField(mYChromaOffset, yChromaOffset);
SetBitField(mRSwizzle, components.r);
SetBitField(mGSwizzle, components.g);
SetBitField(mBSwizzle, components.b);
SetBitField(mASwizzle, components.a);
}
bool YcbcrConversionDesc::updateChromaFilter(Renderer *renderer, VkFilter filter)
{
// The app has requested a specific min/mag filter, reconcile that with the filter
// requested by preferLinearFilterForYUV feature.
//
// preferLinearFilterForYUV enforces linear filter while forceNearestFiltering and
// forceNearestMipFiltering enforces nearest filter, enabling one precludes the other.
ASSERT(!renderer->getFeatures().preferLinearFilterForYUV.enabled ||
(!renderer->getFeatures().forceNearestFiltering.enabled &&
!renderer->getFeatures().forceNearestMipFiltering.enabled));
VkFilter preferredChromaFilter = renderer->getPreferredFilterForYUV(filter);
ASSERT(preferredChromaFilter == VK_FILTER_LINEAR || preferredChromaFilter == VK_FILTER_NEAREST);
if (preferredChromaFilter == VK_FILTER_LINEAR && !mLinearFilterSupported)
{
// Vulkan implementations may not support linear filtering in all cases. If not supported,
// use nearest filtering instead.
preferredChromaFilter = VK_FILTER_NEAREST;
}
if (getChromaFilter() != preferredChromaFilter)
{
SetBitField(mChromaFilter, preferredChromaFilter);
return true;
}
return false;
}
void YcbcrConversionDesc::updateConversionModel(VkSamplerYcbcrModelConversion conversionModel)
{
SetBitField(mConversionModel, conversionModel);
}
angle::Result YcbcrConversionDesc::init(Context *context,
SamplerYcbcrConversion *conversionOut) const
{
// Create the VkSamplerYcbcrConversion
VkSamplerYcbcrConversionCreateInfo samplerYcbcrConversionInfo = {};
samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
samplerYcbcrConversionInfo.format =
getExternalFormat() == 0 ? static_cast<VkFormat>(mExternalOrVkFormat) : VK_FORMAT_UNDEFINED;
samplerYcbcrConversionInfo.xChromaOffset = static_cast<VkChromaLocation>(mXChromaOffset);
samplerYcbcrConversionInfo.yChromaOffset = static_cast<VkChromaLocation>(mYChromaOffset);
samplerYcbcrConversionInfo.ycbcrModel =
static_cast<VkSamplerYcbcrModelConversion>(mConversionModel);
samplerYcbcrConversionInfo.ycbcrRange = static_cast<VkSamplerYcbcrRange>(mColorRange);
samplerYcbcrConversionInfo.chromaFilter = static_cast<VkFilter>(mChromaFilter);
samplerYcbcrConversionInfo.components = {
static_cast<VkComponentSwizzle>(mRSwizzle), static_cast<VkComponentSwizzle>(mGSwizzle),
static_cast<VkComponentSwizzle>(mBSwizzle), static_cast<VkComponentSwizzle>(mASwizzle)};
#ifdef VK_USE_PLATFORM_ANDROID_KHR
VkExternalFormatANDROID externalFormat = {};
if (getExternalFormat() != 0)
{
externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
externalFormat.externalFormat = mExternalOrVkFormat;
samplerYcbcrConversionInfo.pNext = &externalFormat;
}
#else
// We do not support external format for any platform other than Android.
ASSERT(mIsExternalFormat == 0);
#endif // VK_USE_PLATFORM_ANDROID_KHR
ANGLE_VK_TRY(context, conversionOut->init(context->getDevice(), samplerYcbcrConversionInfo));
return angle::Result::Continue;
}
// SamplerDesc implementation.
SamplerDesc::SamplerDesc()
{
reset();
}
SamplerDesc::~SamplerDesc() = default;
SamplerDesc::SamplerDesc(const SamplerDesc &other) = default;
SamplerDesc &SamplerDesc::operator=(const SamplerDesc &rhs) = default;
SamplerDesc::SamplerDesc(ContextVk *contextVk,
const gl::SamplerState &samplerState,
bool stencilMode,
const YcbcrConversionDesc *ycbcrConversionDesc,
angle::FormatID intendedFormatID)
{
update(contextVk, samplerState, stencilMode, ycbcrConversionDesc, intendedFormatID);
}
void SamplerDesc::reset()
{
mMipLodBias = 0.0f;
mMaxAnisotropy = 0.0f;
mMinLod = 0.0f;
mMaxLod = 0.0f;
mYcbcrConversionDesc.reset();
mMagFilter = 0;
mMinFilter = 0;
mMipmapMode = 0;
mAddressModeU = 0;
mAddressModeV = 0;
mAddressModeW = 0;
mCompareEnabled = 0;
mCompareOp = 0;
mPadding = 0;
mBorderColorType = 0;
mBorderColor.red = 0.0f;
mBorderColor.green = 0.0f;
mBorderColor.blue = 0.0f;
mBorderColor.alpha = 0.0f;
mReserved = 0;
}
void SamplerDesc::update(ContextVk *contextVk,
const gl::SamplerState &samplerState,
bool stencilMode,
const YcbcrConversionDesc *ycbcrConversionDesc,
angle::FormatID intendedFormatID)
{
const angle::FeaturesVk &featuresVk = contextVk->getFeatures();
mMipLodBias = 0.0f;
if (featuresVk.forceTextureLodOffset1.enabled)
{
mMipLodBias = 1.0f;
}
else if (featuresVk.forceTextureLodOffset2.enabled)
{
mMipLodBias = 2.0f;
}
else if (featuresVk.forceTextureLodOffset3.enabled)
{
mMipLodBias = 3.0f;
}
else if (featuresVk.forceTextureLodOffset4.enabled)
{
mMipLodBias = 4.0f;
}
mMaxAnisotropy = samplerState.getMaxAnisotropy();
mMinLod = samplerState.getMinLod();
mMaxLod = samplerState.getMaxLod();
GLenum minFilter = samplerState.getMinFilter();
GLenum magFilter = samplerState.getMagFilter();
if (ycbcrConversionDesc && ycbcrConversionDesc->valid())
{
// Update the SamplerYcbcrConversionCache key
mYcbcrConversionDesc = *ycbcrConversionDesc;
// Reconcile chroma filter and min/mag filters.
//
// VUID-VkSamplerCreateInfo-minFilter-01645
// If sampler YCBCR conversion is enabled and the potential format features of the
// sampler YCBCR conversion do not support
// VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT,
// minFilter and magFilter must be equal to the sampler YCBCR conversions chromaFilter.
//
// For simplicity assume external formats do not support that feature bit.
ASSERT((mYcbcrConversionDesc.getExternalFormat() != 0) ||
(angle::Format::Get(intendedFormatID).isYUV));
const bool filtersMustMatch =
(mYcbcrConversionDesc.getExternalFormat() != 0) ||
!contextVk->getRenderer()->hasImageFormatFeatureBits(
intendedFormatID,
VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT);
if (filtersMustMatch)
{
GLenum glFilter = (mYcbcrConversionDesc.getChromaFilter() == VK_FILTER_LINEAR)
? GL_LINEAR
: GL_NEAREST;
minFilter = glFilter;
magFilter = glFilter;
}
}
bool compareEnable = samplerState.getCompareMode() == GL_COMPARE_REF_TO_TEXTURE;
VkCompareOp compareOp = gl_vk::GetCompareOp(samplerState.getCompareFunc());
// When sampling from stencil, deqp tests expect texture compare to have no effect
// dEQP - GLES31.functional.stencil_texturing.misc.compare_mode_effect
// states: NOTE: Texture compare mode has no effect when reading stencil values.
if (stencilMode)
{
compareEnable = VK_FALSE;
compareOp = VK_COMPARE_OP_ALWAYS;
}
if (featuresVk.forceNearestFiltering.enabled)
{
magFilter = gl::ConvertToNearestFilterMode(magFilter);
minFilter = gl::ConvertToNearestFilterMode(minFilter);
}
if (featuresVk.forceNearestMipFiltering.enabled)
{
minFilter = gl::ConvertToNearestMipFilterMode(minFilter);
}
SetBitField(mMagFilter, gl_vk::GetFilter(magFilter));
SetBitField(mMinFilter, gl_vk::GetFilter(minFilter));
SetBitField(mMipmapMode, gl_vk::GetSamplerMipmapMode(samplerState.getMinFilter()));
SetBitField(mAddressModeU, gl_vk::GetSamplerAddressMode(samplerState.getWrapS()));
SetBitField(mAddressModeV, gl_vk::GetSamplerAddressMode(samplerState.getWrapT()));
SetBitField(mAddressModeW, gl_vk::GetSamplerAddressMode(samplerState.getWrapR()));
SetBitField(mCompareEnabled, compareEnable);
SetBitField(mCompareOp, compareOp);
if (!gl::IsMipmapFiltered(minFilter))
{
// Per the Vulkan spec, GL_NEAREST and GL_LINEAR do not map directly to Vulkan, so
// they must be emulated (See "Mapping of OpenGL to Vulkan filter modes")
SetBitField(mMipmapMode, VK_SAMPLER_MIPMAP_MODE_NEAREST);
mMinLod = 0.0f;
mMaxLod = 0.25f;
}
mPadding = 0;
mBorderColorType =
(samplerState.getBorderColor().type == angle::ColorGeneric::Type::Float) ? 0 : 1;
// Adjust border color according to intended format
const vk::Format &vkFormat = contextVk->getRenderer()->getFormat(intendedFormatID);
gl::ColorGeneric adjustedBorderColor =
AdjustBorderColor(samplerState.getBorderColor(), vkFormat.getIntendedFormat(), stencilMode);
mBorderColor = adjustedBorderColor.colorF;
mReserved = 0;
}
angle::Result SamplerDesc::init(ContextVk *contextVk, Sampler *sampler) const
{
const gl::Extensions &extensions = contextVk->getExtensions();
bool anisotropyEnable = extensions.textureFilterAnisotropicEXT && mMaxAnisotropy > 1.0f;
VkSamplerCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
createInfo.flags = 0;
createInfo.magFilter = static_cast<VkFilter>(mMagFilter);
createInfo.minFilter = static_cast<VkFilter>(mMinFilter);
createInfo.mipmapMode = static_cast<VkSamplerMipmapMode>(mMipmapMode);
createInfo.addressModeU = static_cast<VkSamplerAddressMode>(mAddressModeU);
createInfo.addressModeV = static_cast<VkSamplerAddressMode>(mAddressModeV);
createInfo.addressModeW = static_cast<VkSamplerAddressMode>(mAddressModeW);
createInfo.mipLodBias = mMipLodBias;
createInfo.anisotropyEnable = anisotropyEnable;
createInfo.maxAnisotropy = mMaxAnisotropy;
createInfo.compareEnable = mCompareEnabled ? VK_TRUE : VK_FALSE;
createInfo.compareOp = static_cast<VkCompareOp>(mCompareOp);
createInfo.minLod = mMinLod;
createInfo.maxLod = mMaxLod;
createInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
createInfo.unnormalizedCoordinates = VK_FALSE;
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {};
if (mYcbcrConversionDesc.valid())
{
ASSERT((contextVk->getRenderer()->getFeatures().supportsYUVSamplerConversion.enabled));
samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO;
samplerYcbcrConversionInfo.pNext = nullptr;
ANGLE_TRY(contextVk->getRenderer()->getYuvConversionCache().getSamplerYcbcrConversion(
contextVk, mYcbcrConversionDesc, &samplerYcbcrConversionInfo.conversion));
AddToPNextChain(&createInfo, &samplerYcbcrConversionInfo);
// Vulkan spec requires these settings:
createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
createInfo.anisotropyEnable = VK_FALSE;
createInfo.unnormalizedCoordinates = VK_FALSE;
}
VkSamplerCustomBorderColorCreateInfoEXT customBorderColorInfo = {};
if (createInfo.addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
createInfo.addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
createInfo.addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)
{
ASSERT((contextVk->getRenderer()->getFeatures().supportsCustomBorderColor.enabled));
customBorderColorInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT;
customBorderColorInfo.customBorderColor.float32[0] = mBorderColor.red;
customBorderColorInfo.customBorderColor.float32[1] = mBorderColor.green;
customBorderColorInfo.customBorderColor.float32[2] = mBorderColor.blue;
customBorderColorInfo.customBorderColor.float32[3] = mBorderColor.alpha;
if (mBorderColorType == static_cast<uint32_t>(angle::ColorGeneric::Type::Float))
{
createInfo.borderColor = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;
}
else
{
createInfo.borderColor = VK_BORDER_COLOR_INT_CUSTOM_EXT;
}
vk::AddToPNextChain(&createInfo, &customBorderColorInfo);
}
ANGLE_VK_TRY(contextVk, sampler->init(contextVk->getDevice(), createInfo));
return angle::Result::Continue;
}
size_t SamplerDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool SamplerDesc::operator==(const SamplerDesc &other) const
{
return memcmp(this, &other, sizeof(SamplerDesc)) == 0;
}
// SamplerHelper implementation.
SamplerHelper::SamplerHelper(ContextVk *contextVk)
: mSamplerSerial(contextVk->getRenderer()->getResourceSerialFactory().generateSamplerSerial())
{}
SamplerHelper::~SamplerHelper() {}
SamplerHelper::SamplerHelper(SamplerHelper &&samplerHelper)
{
*this = std::move(samplerHelper);
}
SamplerHelper &SamplerHelper::operator=(SamplerHelper &&rhs)
{
std::swap(mSampler, rhs.mSampler);
std::swap(mSamplerSerial, rhs.mSamplerSerial);
return *this;
}
// RenderPassHelper implementation.
RenderPassHelper::RenderPassHelper() : mPerfCounters{} {}
RenderPassHelper::~RenderPassHelper() = default;
RenderPassHelper::RenderPassHelper(RenderPassHelper &&other)
{
*this = std::move(other);
}
RenderPassHelper &RenderPassHelper::operator=(RenderPassHelper &&other)
{
mRenderPass = std::move(other.mRenderPass);
mPerfCounters = std::move(other.mPerfCounters);
return *this;
}
void RenderPassHelper::destroy(VkDevice device)
{
mRenderPass.destroy(device);
}
void RenderPassHelper::release(ContextVk *contextVk)
{
contextVk->addGarbage(&mRenderPass);
}
const RenderPass &RenderPassHelper::getRenderPass() const
{
return mRenderPass;
}
RenderPass &RenderPassHelper::getRenderPass()
{
return mRenderPass;
}
const RenderPassPerfCounters &RenderPassHelper::getPerfCounters() const
{
return mPerfCounters;
}
RenderPassPerfCounters &RenderPassHelper::getPerfCounters()
{
return mPerfCounters;
}
// WriteDescriptorDescs implementation.
void WriteDescriptorDescs::updateWriteDesc(uint32_t bindingIndex,
VkDescriptorType descriptorType,
uint32_t descriptorCount)
{
if (hasWriteDescAtIndex(bindingIndex))
{
uint32_t infoIndex = mDescs[bindingIndex].descriptorInfoIndex;
uint32_t oldDescriptorCount = mDescs[bindingIndex].descriptorCount;
if (descriptorCount != oldDescriptorCount)
{
ASSERT(infoIndex + oldDescriptorCount == mCurrentInfoIndex);
ASSERT(descriptorCount > oldDescriptorCount);
uint32_t additionalDescriptors = descriptorCount - oldDescriptorCount;
incrementDescriptorCount(bindingIndex, additionalDescriptors);
mCurrentInfoIndex += additionalDescriptors;
}
}
else
{
WriteDescriptorDesc &writeDesc = mDescs[bindingIndex];
SetBitField(writeDesc.binding, bindingIndex);
SetBitField(writeDesc.descriptorCount, descriptorCount);
SetBitField(writeDesc.descriptorType, descriptorType);
SetBitField(writeDesc.descriptorInfoIndex, mCurrentInfoIndex);
mCurrentInfoIndex += descriptorCount;
ASSERT(writeDesc.descriptorCount > 0);
}
}
void WriteDescriptorDescs::updateShaderBuffers(
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType)
{
// Initialize the descriptor writes in a first pass. This ensures we can pack the structures
// corresponding to array elements tightly.
for (uint32_t blockIndex = 0; blockIndex < blocks.size(); ++blockIndex)
{
const gl::InterfaceBlock &block = blocks[blockIndex];
if (block.activeShaders().none())
{
continue;
}
const gl::ShaderType firstShaderType = block.getFirstActiveShaderType();
const ShaderInterfaceVariableInfo &info =
variableInfoMap.getVariableById(firstShaderType, block.getId(firstShaderType));
if (block.pod.isArray && block.pod.arrayElement > 0)
{
incrementDescriptorCount(info.binding, 1);
mCurrentInfoIndex++;
}
else
{
updateWriteDesc(info.binding, descriptorType, 1);
}
}
}
void WriteDescriptorDescs::updateAtomicCounters(
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers)
{
if (atomicCounterBuffers.empty())
{
return;
}
static_assert(!IsDynamicDescriptor(kStorageBufferDescriptorType),
"This method needs an update to handle dynamic descriptors");
uint32_t binding = variableInfoMap.getAtomicCounterBufferBinding(
atomicCounterBuffers[0].getFirstActiveShaderType(), 0);
updateWriteDesc(binding, kStorageBufferDescriptorType,
gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS);
}
void WriteDescriptorDescs::updateImages(const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap)
{
const std::vector<gl::ImageBinding> &imageBindings = executable.getImageBindings();
const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms();
if (imageBindings.empty())
{
return;
}
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
if (imageUniform.activeShaders().none())
{
continue;
}
const gl::ShaderType firstShaderType = imageUniform.getFirstActiveShaderType();
const ShaderInterfaceVariableInfo &info =
variableInfoMap.getVariableById(firstShaderType, imageUniform.getId(firstShaderType));
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
uint32_t descriptorCount = arraySize * imageUniform.getOuterArraySizeProduct();
VkDescriptorType descriptorType = (imageBinding.textureType == gl::TextureType::Buffer)
? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
updateWriteDesc(info.binding, descriptorType, descriptorCount);
}
}
void WriteDescriptorDescs::updateInputAttachments(
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
FramebufferVk *framebufferVk)
{
if (!executable.usesFramebufferFetch())
{
return;
}
const uint32_t firstInputAttachment =
static_cast<uint32_t>(executable.getFragmentInoutIndices().first());
const ShaderInterfaceVariableInfo &baseInfo = variableInfoMap.getVariableById(
gl::ShaderType::Fragment, sh::vk::spirv::kIdInputAttachment0 + firstInputAttachment);
const uint32_t baseBinding = baseInfo.binding - firstInputAttachment;
for (size_t colorIndex : framebufferVk->getState().getColorAttachmentsMask())
{
uint32_t binding = baseBinding + static_cast<uint32_t>(colorIndex);
updateWriteDesc(binding, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1);
}
}
void WriteDescriptorDescs::updateExecutableActiveTextures(
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::ProgramExecutable &executable)
{
const std::vector<gl::SamplerBinding> &samplerBindings = executable.getSamplerBindings();
const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms();
for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
{
const gl::SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
uint32_t uniformIndex = executable.getUniformIndexFromSamplerIndex(samplerIndex);
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
if (samplerUniform.activeShaders().none())
{
continue;
}
const gl::ShaderType firstShaderType = samplerUniform.getFirstActiveShaderType();
const ShaderInterfaceVariableInfo &info =
variableInfoMap.getVariableById(firstShaderType, samplerUniform.getId(firstShaderType));
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.textureUnitsCount);
uint32_t descriptorCount = arraySize * samplerUniform.getOuterArraySizeProduct();
VkDescriptorType descriptorType = (samplerBinding.textureType == gl::TextureType::Buffer)
? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
updateWriteDesc(info.binding, descriptorType, descriptorCount);
}
}
void WriteDescriptorDescs::updateDefaultUniform(
gl::ShaderBitSet shaderTypes,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::ProgramExecutable &executable)
{
for (const gl::ShaderType shaderType : shaderTypes)
{
uint32_t binding = variableInfoMap.getDefaultUniformBinding(shaderType);
updateWriteDesc(binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1);
}
}
void WriteDescriptorDescs::updateTransformFeedbackWrite(
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::ProgramExecutable &executable)
{
uint32_t xfbBufferCount = static_cast<uint32_t>(executable.getTransformFeedbackBufferCount());
updateWriteDesc(variableInfoMap.getEmulatedXfbBufferBinding(0),
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, xfbBufferCount);
}
void WriteDescriptorDescs::updateDynamicDescriptorsCount()
{
mDynamicDescriptorSetCount = 0;
for (uint32_t index = 0; index < mDescs.size(); ++index)
{
const WriteDescriptorDesc &writeDesc = mDescs[index];
if (IsDynamicDescriptor(static_cast<VkDescriptorType>(writeDesc.descriptorType)))
{
mDynamicDescriptorSetCount += writeDesc.descriptorCount;
}
}
}
void WriteDescriptorDescs::streamOut(std::ostream &ostr) const
{
ostr << mDescs.size() << " write descriptor descs:\n";
for (uint32_t index = 0; index < mDescs.size(); ++index)
{
const WriteDescriptorDesc &writeDesc = mDescs[index];
ostr << static_cast<int>(writeDesc.binding) << ": "
<< static_cast<int>(writeDesc.descriptorCount) << " "
<< kDescriptorTypeNameMap[writeDesc.descriptorType] << " descriptors: ";
ostr << "\n";
}
}
// DescriptorSetDesc implementation.
void DescriptorSetDesc::updateDescriptorSet(Context *context,
const WriteDescriptorDescs &writeDescriptorDescs,
UpdateDescriptorSetsBuilder *updateBuilder,
const DescriptorDescHandles *handles,
VkDescriptorSet descriptorSet) const
{
for (uint32_t writeIndex = 0; writeIndex < writeDescriptorDescs.size(); ++writeIndex)
{
const WriteDescriptorDesc &writeDesc = writeDescriptorDescs[writeIndex];
if (writeDesc.descriptorCount == 0)
{
continue;
}
VkWriteDescriptorSet &writeSet = updateBuilder->allocWriteDescriptorSet();
writeSet.descriptorCount = writeDesc.descriptorCount;
writeSet.descriptorType = static_cast<VkDescriptorType>(writeDesc.descriptorType);
writeSet.dstArrayElement = 0;
writeSet.dstBinding = writeIndex;
writeSet.dstSet = descriptorSet;
writeSet.pBufferInfo = nullptr;
writeSet.pImageInfo = nullptr;
writeSet.pNext = nullptr;
writeSet.pTexelBufferView = nullptr;
writeSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
uint32_t infoDescIndex = writeDesc.descriptorInfoIndex;
switch (writeSet.descriptorType)
{
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
{
ASSERT(writeDesc.descriptorCount == 1);
VkBufferView &bufferView = updateBuilder->allocBufferView();
bufferView = handles[infoDescIndex].bufferView;
writeSet.pTexelBufferView = &bufferView;
break;
}
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
{
VkDescriptorBufferInfo *writeBuffers =
updateBuilder->allocDescriptorBufferInfos(writeSet.descriptorCount);
for (uint32_t arrayElement = 0; arrayElement < writeSet.descriptorCount;
++arrayElement)
{
const DescriptorInfoDesc &infoDesc =
mDescriptorInfos[infoDescIndex + arrayElement];
VkDescriptorBufferInfo &bufferInfo = writeBuffers[arrayElement];
bufferInfo.buffer = handles[infoDescIndex + arrayElement].buffer;
bufferInfo.offset = infoDesc.imageViewSerialOrOffset;
bufferInfo.range = infoDesc.imageLayoutOrRange;
}
writeSet.pBufferInfo = writeBuffers;
break;
}
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
{
VkDescriptorImageInfo *writeImages =
updateBuilder->allocDescriptorImageInfos(writeSet.descriptorCount);
for (uint32_t arrayElement = 0; arrayElement < writeSet.descriptorCount;
++arrayElement)
{
const DescriptorInfoDesc &infoDesc =
mDescriptorInfos[infoDescIndex + arrayElement];
VkDescriptorImageInfo &imageInfo = writeImages[arrayElement];
ImageLayout imageLayout = static_cast<ImageLayout>(infoDesc.imageLayoutOrRange);
imageInfo.imageLayout = ConvertImageLayoutToVkImageLayout(context, imageLayout);
imageInfo.imageView = handles[infoDescIndex + arrayElement].imageView;
imageInfo.sampler = handles[infoDescIndex + arrayElement].sampler;
}
writeSet.pImageInfo = writeImages;
break;
}
default:
UNREACHABLE();
break;
}
}
}
void DescriptorSetDesc::streamOut(std::ostream &ostr) const
{
ostr << mDescriptorInfos.size() << " descriptor descs:\n";
for (uint32_t index = 0; index < mDescriptorInfos.size(); ++index)
{
const DescriptorInfoDesc &infoDesc = mDescriptorInfos[index];
ostr << "{" << infoDesc.imageLayoutOrRange << ", " << infoDesc.imageSubresourceRange << ", "
<< infoDesc.imageViewSerialOrOffset << ", " << infoDesc.samplerOrBufferSerial << "}";
ostr << "\n";
}
}
// DescriptorSetDescBuilder implementation.
DescriptorSetDescBuilder::DescriptorSetDescBuilder() = default;
DescriptorSetDescBuilder::DescriptorSetDescBuilder(size_t descriptorCount)
{
resize(descriptorCount);
}
DescriptorSetDescBuilder::~DescriptorSetDescBuilder() {}
DescriptorSetDescBuilder::DescriptorSetDescBuilder(const DescriptorSetDescBuilder &other)
: mDesc(other.mDesc), mHandles(other.mHandles), mDynamicOffsets(other.mDynamicOffsets)
{}
DescriptorSetDescBuilder &DescriptorSetDescBuilder::operator=(const DescriptorSetDescBuilder &other)
{
mDesc = other.mDesc;
mHandles = other.mHandles;
mDynamicOffsets = other.mDynamicOffsets;
return *this;
}
void DescriptorSetDescBuilder::updateUniformBuffer(uint32_t bindingIndex,
const WriteDescriptorDescs &writeDescriptorDescs,
const BufferHelper &bufferHelper,
VkDeviceSize bufferRange)
{
uint32_t infoIndex = writeDescriptorDescs[bindingIndex].descriptorInfoIndex;
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoIndex);
infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue();
infoDesc.imageViewSerialOrOffset = 0;
SetBitField(infoDesc.imageLayoutOrRange, bufferRange);
infoDesc.imageSubresourceRange = 0;
mHandles[infoIndex].buffer = bufferHelper.getBuffer().getHandle();
}
void DescriptorSetDescBuilder::updateTransformFeedbackBuffer(
const Context *context,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const WriteDescriptorDescs &writeDescriptorDescs,
uint32_t xfbBufferIndex,
const BufferHelper &bufferHelper,
VkDeviceSize bufferOffset,
VkDeviceSize bufferRange)
{
const uint32_t baseBinding = variableInfoMap.getEmulatedXfbBufferBinding(0);
Renderer *renderer = context->getRenderer();
VkDeviceSize offsetAlignment =
renderer->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
// Set the offset as close as possible to the requested offset while remaining aligned.
VkDeviceSize alignedOffset = (bufferOffset / offsetAlignment) * offsetAlignment;
VkDeviceSize adjustedRange = bufferRange + (bufferOffset - alignedOffset);
uint32_t infoIndex = writeDescriptorDescs[baseBinding].descriptorInfoIndex + xfbBufferIndex;
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoIndex);
infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue();
SetBitField(infoDesc.imageViewSerialOrOffset, alignedOffset);
SetBitField(infoDesc.imageLayoutOrRange, adjustedRange);
infoDesc.imageSubresourceRange = 0;
mHandles[infoIndex].buffer = bufferHelper.getBuffer().getHandle();
}
void DescriptorSetDescBuilder::updateUniformsAndXfb(
Context *context,
const gl::ProgramExecutable &executable,
const WriteDescriptorDescs &writeDescriptorDescs,
const BufferHelper *currentUniformBuffer,
const BufferHelper &emptyBuffer,
bool activeUnpaused,
TransformFeedbackVk *transformFeedbackVk)
{
const ProgramExecutableVk *executableVk = vk::GetImpl(&executable);
gl::ShaderBitSet linkedStages = executable.getLinkedShaderStages();
const ShaderInterfaceVariableInfoMap &variableInfoMap = executableVk->getVariableInfoMap();
for (const gl::ShaderType shaderType : linkedStages)
{
uint32_t binding = variableInfoMap.getDefaultUniformBinding(shaderType);
VkDeviceSize bufferRange = executableVk->getDefaultUniformAlignedSize(context, shaderType);
if (bufferRange == 0)
{
updateUniformBuffer(binding, writeDescriptorDescs, emptyBuffer, emptyBuffer.getSize());
}
else
{
ASSERT(currentUniformBuffer);
updateUniformBuffer(binding, writeDescriptorDescs, *currentUniformBuffer, bufferRange);
}
if (transformFeedbackVk && shaderType == gl::ShaderType::Vertex &&
context->getRenderer()->getFeatures().emulateTransformFeedback.enabled)
{
transformFeedbackVk->updateTransformFeedbackDescriptorDesc(
context, executable, variableInfoMap, writeDescriptorDescs, emptyBuffer,
activeUnpaused, this);
}
}
}
void UpdatePreCacheActiveTextures(const gl::ProgramExecutable &executable,
const std::vector<gl::SamplerBinding> &samplerBindings,
const gl::ActiveTextureMask &activeTextures,
const gl::ActiveTextureArray<TextureVk *> &textures,
const gl::SamplerBindingVector &samplers,
DescriptorSetDesc *desc)
{
const ProgramExecutableVk *executableVk = vk::GetImpl(&executable);
desc->resize(executableVk->getTextureWriteDescriptorDescs().getTotalDescriptorCount());
const WriteDescriptorDescs &writeDescriptorDescs =
executableVk->getTextureWriteDescriptorDescs();
const ShaderInterfaceVariableInfoMap &variableInfoMap = executableVk->getVariableInfoMap();
const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms();
for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
{
const gl::SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
uint16_t arraySize = samplerBinding.textureUnitsCount;
bool isSamplerExternalY2Y = samplerBinding.samplerType == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT;
uint32_t uniformIndex = executable.getUniformIndexFromSamplerIndex(samplerIndex);
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
if (samplerUniform.activeShaders().none())
{
continue;
}
const gl::ShaderType firstShaderType = samplerUniform.getFirstActiveShaderType();
const ShaderInterfaceVariableInfo &info =
variableInfoMap.getVariableById(firstShaderType, samplerUniform.getId(firstShaderType));
for (uint16_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint textureUnit = samplerBinding.getTextureUnit(
executable.getSamplerBoundTextureUnits(), arrayElement);
if (!activeTextures.test(textureUnit))
continue;
TextureVk *textureVk = textures[textureUnit];
uint32_t infoIndex = writeDescriptorDescs[info.binding].descriptorInfoIndex +
arrayElement + samplerUniform.getOuterArrayOffset();
DescriptorInfoDesc &infoDesc = desc->getInfoDesc(infoIndex);
if (textureVk->getState().getType() == gl::TextureType::Buffer)
{
ImageOrBufferViewSubresourceSerial imageViewSerial =
textureVk->getBufferViewSerial();
infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue();
infoDesc.imageLayoutOrRange = 0;
infoDesc.samplerOrBufferSerial = 0;
infoDesc.imageSubresourceRange = 0;
}
else
{
gl::Sampler *sampler = samplers[textureUnit].get();
const SamplerVk *samplerVk = sampler ? vk::GetImpl(sampler) : nullptr;
const SamplerHelper &samplerHelper =
samplerVk ? samplerVk->getSampler()
: textureVk->getSampler(isSamplerExternalY2Y);
const gl::SamplerState &samplerState =
sampler ? sampler->getSamplerState() : textureVk->getState().getSamplerState();
ImageOrBufferViewSubresourceSerial imageViewSerial =
textureVk->getImageViewSubresourceSerial(samplerState);
ImageLayout imageLayout = textureVk->getImage().getCurrentImageLayout();
SetBitField(infoDesc.imageLayoutOrRange, imageLayout);
infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue();
infoDesc.samplerOrBufferSerial = samplerHelper.getSamplerSerial().getValue();
memcpy(&infoDesc.imageSubresourceRange, &imageViewSerial.subresource,
sizeof(uint32_t));
}
}
}
}
angle::Result DescriptorSetDescBuilder::updateFullActiveTextures(
Context *context,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const WriteDescriptorDescs &writeDescriptorDescs,
const gl::ProgramExecutable &executable,
const gl::ActiveTextureArray<TextureVk *> &textures,
const gl::SamplerBindingVector &samplers,
bool emulateSeamfulCubeMapSampling,
PipelineType pipelineType,
const SharedDescriptorSetCacheKey &sharedCacheKey)
{
const std::vector<gl::SamplerBinding> &samplerBindings = executable.getSamplerBindings();
const std::vector<GLuint> &samplerBoundTextureUnits = executable.getSamplerBoundTextureUnits();
const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms();
const gl::ActiveTextureTypeArray &textureTypes = executable.getActiveSamplerTypes();
for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex)
{
const gl::SamplerBinding &samplerBinding = samplerBindings[samplerIndex];
uint32_t uniformIndex = executable.getUniformIndexFromSamplerIndex(samplerIndex);
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
if (samplerUniform.activeShaders().none())
{
continue;
}
const gl::ShaderType firstShaderType = samplerUniform.getFirstActiveShaderType();
const ShaderInterfaceVariableInfo &info =
variableInfoMap.getVariableById(firstShaderType, samplerUniform.getId(firstShaderType));
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.textureUnitsCount);
bool isSamplerExternalY2Y = samplerBinding.samplerType == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT;
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint textureUnit =
samplerBinding.getTextureUnit(samplerBoundTextureUnits, arrayElement);
TextureVk *textureVk = textures[textureUnit];
uint32_t infoIndex = writeDescriptorDescs[info.binding].descriptorInfoIndex +
arrayElement + samplerUniform.getOuterArrayOffset();
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoIndex);
if (textureTypes[textureUnit] == gl::TextureType::Buffer)
{
ImageOrBufferViewSubresourceSerial imageViewSerial =
textureVk->getBufferViewSerial();
infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue();
infoDesc.imageLayoutOrRange = 0;
infoDesc.samplerOrBufferSerial = 0;
infoDesc.imageSubresourceRange = 0;
textureVk->onNewDescriptorSet(sharedCacheKey);
const BufferView *view = nullptr;
ANGLE_TRY(textureVk->getBufferViewAndRecordUse(context, nullptr, &samplerBinding,
false, &view));
mHandles[infoIndex].bufferView = view->getHandle();
}
else
{
gl::Sampler *sampler = samplers[textureUnit].get();
const SamplerVk *samplerVk = sampler ? vk::GetImpl(sampler) : nullptr;
const SamplerHelper &samplerHelper =
samplerVk ? samplerVk->getSampler()
: textureVk->getSampler(isSamplerExternalY2Y);
const gl::SamplerState &samplerState =
sampler ? sampler->getSamplerState() : textureVk->getState().getSamplerState();
ImageOrBufferViewSubresourceSerial imageViewSerial =
textureVk->getImageViewSubresourceSerial(samplerState);
textureVk->onNewDescriptorSet(sharedCacheKey);
ImageLayout imageLayout = textureVk->getImage().getCurrentImageLayout();
SetBitField(infoDesc.imageLayoutOrRange, imageLayout);
infoDesc.imageViewSerialOrOffset = imageViewSerial.viewSerial.getValue();
infoDesc.samplerOrBufferSerial = samplerHelper.getSamplerSerial().getValue();
memcpy(&infoDesc.imageSubresourceRange, &imageViewSerial.subresource,
sizeof(uint32_t));
mHandles[infoIndex].sampler = samplerHelper.get().getHandle();
// __samplerExternal2DY2YEXT cannot be used with
// emulateSeamfulCubeMapSampling because that's only enabled in GLES == 2.
// Use the read image view here anyway.
if (emulateSeamfulCubeMapSampling && !isSamplerExternalY2Y)
{
// If emulating seamful cube mapping, use the fetch image view. This is
// basically the same image view as read, except it's a 2DArray view for
// cube maps.
const ImageView &imageView =
textureVk->getFetchImageView(context, samplerState.getSRGBDecode(),
samplerUniform.isTexelFetchStaticUse());
mHandles[infoIndex].imageView = imageView.getHandle();
}
else
{
const ImageView &imageView = textureVk->getReadImageView(
context, samplerState.getSRGBDecode(),
samplerUniform.isTexelFetchStaticUse(), isSamplerExternalY2Y);
mHandles[infoIndex].imageView = imageView.getHandle();
}
}
}
}
return angle::Result::Continue;
}
void DescriptorSetDescBuilder::setEmptyBuffer(uint32_t infoDescIndex,
VkDescriptorType descriptorType,
const BufferHelper &emptyBuffer)
{
DescriptorInfoDesc &emptyDesc = mDesc.getInfoDesc(infoDescIndex);
SetBitField(emptyDesc.imageLayoutOrRange, emptyBuffer.getSize());
emptyDesc.imageViewSerialOrOffset = 0;
emptyDesc.samplerOrBufferSerial = emptyBuffer.getBlockSerial().getValue();
mHandles[infoDescIndex].buffer = emptyBuffer.getBuffer().getHandle();
if (IsDynamicDescriptor(descriptorType))
{
mDynamicOffsets[infoDescIndex] = 0;
}
}
template <typename CommandBufferT>
void DescriptorSetDescBuilder::updateOneShaderBuffer(
ContextVk *contextVk,
CommandBufferT *commandBufferHelper,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const gl::InterfaceBlock &block,
uint32_t bufferIndex,
VkDescriptorType descriptorType,
VkDeviceSize maxBoundBufferRange,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs)
{
if (block.activeShaders().none())
{
return;
}
const gl::ShaderType firstShaderType = block.getFirstActiveShaderType();
const ShaderInterfaceVariableInfo &info =
variableInfoMap.getVariableById(firstShaderType, block.getId(firstShaderType));
uint32_t binding = info.binding;
uint32_t arrayElement = block.pod.isArray ? block.pod.arrayElement : 0;
uint32_t infoDescIndex = writeDescriptorDescs[binding].descriptorInfoIndex + arrayElement;
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding = buffers[bufferIndex];
if (bufferBinding.get() == nullptr)
{
setEmptyBuffer(infoDescIndex, descriptorType, emptyBuffer);
return;
}
// Limit bound buffer size to maximum resource binding size.
GLsizeiptr boundBufferSize = gl::GetBoundBufferAvailableSize(bufferBinding);
VkDeviceSize size = std::min<VkDeviceSize>(boundBufferSize, maxBoundBufferRange);
// Make sure there's no possible under/overflow with binding size.
static_assert(sizeof(VkDeviceSize) >= sizeof(bufferBinding.getSize()),
"VkDeviceSize too small");
ASSERT(bufferBinding.getSize() >= 0);
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
BufferHelper &bufferHelper = bufferVk->getBuffer();
const bool isUniformBuffer = descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
if (isUniformBuffer)
{
commandBufferHelper->bufferRead(contextVk, VK_ACCESS_UNIFORM_READ_BIT,
block.activeShaders(), &bufferHelper);
}
else
{
ASSERT(descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
if (block.pod.isReadOnly)
{
// Avoid unnecessary barriers for readonly SSBOs by making sure the buffers are
// marked read-only. This also helps BufferVk make better decisions during
// buffer data uploads and copies by knowing that the buffers are not actually
// being written to.
commandBufferHelper->bufferRead(contextVk, VK_ACCESS_SHADER_READ_BIT,
block.activeShaders(), &bufferHelper);
}
else
{
// We set the SHADER_READ_BIT to be conservative.
VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
for (const gl::ShaderType shaderType : block.activeShaders())
{
const vk::PipelineStage pipelineStage = vk::GetPipelineStage(shaderType);
commandBufferHelper->bufferWrite(contextVk, accessFlags, pipelineStage,
&bufferHelper);
}
}
}
VkDeviceSize offset = bufferBinding.getOffset() + bufferHelper.getOffset();
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoDescIndex);
infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue();
if (IsDynamicDescriptor(descriptorType))
{
SetBitField(mDynamicOffsets[infoDescIndex], offset);
infoDesc.imageViewSerialOrOffset = 0;
}
else
{
SetBitField(infoDesc.imageViewSerialOrOffset, offset);
}
SetBitField(infoDesc.imageLayoutOrRange, size);
infoDesc.imageSubresourceRange = 0;
mHandles[infoDescIndex].buffer = bufferHelper.getBuffer().getHandle();
}
template <typename CommandBufferT>
void DescriptorSetDescBuilder::updateShaderBuffers(
ContextVk *contextVk,
CommandBufferT *commandBufferHelper,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType,
VkDeviceSize maxBoundBufferRange,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs)
{
const bool isUniformBuffer = descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
// Now that we have the proper array elements counts, initialize the info structures.
for (uint32_t blockIndex = 0; blockIndex < blocks.size(); ++blockIndex)
{
const GLuint binding = isUniformBuffer
? executable.getUniformBlockBinding(blockIndex)
: executable.getShaderStorageBlockBinding(blockIndex);
updateOneShaderBuffer(contextVk, commandBufferHelper, variableInfoMap, buffers,
blocks[blockIndex], binding, descriptorType, maxBoundBufferRange,
emptyBuffer, writeDescriptorDescs);
}
}
template <typename CommandBufferT>
void DescriptorSetDescBuilder::updateAtomicCounters(
ContextVk *contextVk,
CommandBufferT *commandBufferHelper,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
const VkDeviceSize requiredOffsetAlignment,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs)
{
ASSERT(!atomicCounterBuffers.empty());
static_assert(!IsDynamicDescriptor(kStorageBufferDescriptorType),
"This method needs an update to handle dynamic descriptors");
if (atomicCounterBuffers.empty())
{
return;
}
uint32_t binding = variableInfoMap.getAtomicCounterBufferBinding(
atomicCounterBuffers[0].getFirstActiveShaderType(), 0);
uint32_t baseInfoIndex = writeDescriptorDescs[binding].descriptorInfoIndex;
// Bind the empty buffer to every array slot that's unused.
for (uint32_t arrayElement = 0;
arrayElement < gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS; ++arrayElement)
{
uint32_t infoIndex = baseInfoIndex + arrayElement;
setEmptyBuffer(infoIndex, kStorageBufferDescriptorType, emptyBuffer);
}
for (uint32_t bufferIndex = 0; bufferIndex < atomicCounterBuffers.size(); ++bufferIndex)
{
const gl::AtomicCounterBuffer &atomicCounterBuffer = atomicCounterBuffers[bufferIndex];
const GLuint arrayElement = executable.getAtomicCounterBufferBinding(bufferIndex);
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding = buffers[arrayElement];
uint32_t infoIndex = baseInfoIndex + arrayElement;
if (bufferBinding.get() == nullptr)
{
setEmptyBuffer(infoIndex, kStorageBufferDescriptorType, emptyBuffer);
continue;
}
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
for (const gl::ShaderType shaderType : atomicCounterBuffer.activeShaders())
{
const vk::PipelineStage pipelineStage = vk::GetPipelineStage(shaderType);
commandBufferHelper->bufferWrite(contextVk,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
pipelineStage, &bufferHelper);
}
VkDeviceSize offset = bufferBinding.getOffset() + bufferHelper.getOffset();
VkDeviceSize alignedOffset = (offset / requiredOffsetAlignment) * requiredOffsetAlignment;
VkDeviceSize offsetDiff = offset - alignedOffset;
offset = alignedOffset;
VkDeviceSize range = gl::GetBoundBufferAvailableSize(bufferBinding) + offsetDiff;
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoIndex);
SetBitField(infoDesc.imageLayoutOrRange, range);
SetBitField(infoDesc.imageViewSerialOrOffset, offset);
infoDesc.samplerOrBufferSerial = bufferHelper.getBlockSerial().getValue();
infoDesc.imageSubresourceRange = 0;
mHandles[infoIndex].buffer = bufferHelper.getBuffer().getHandle();
}
}
// Explicit instantiation
template void DescriptorSetDescBuilder::updateOneShaderBuffer<vk::RenderPassCommandBufferHelper>(
ContextVk *contextVk,
RenderPassCommandBufferHelper *commandBufferHelper,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const gl::InterfaceBlock &block,
uint32_t bufferIndex,
VkDescriptorType descriptorType,
VkDeviceSize maxBoundBufferRange,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs);
template void DescriptorSetDescBuilder::updateOneShaderBuffer<OutsideRenderPassCommandBufferHelper>(
ContextVk *contextVk,
OutsideRenderPassCommandBufferHelper *commandBufferHelper,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const gl::InterfaceBlock &block,
uint32_t bufferIndex,
VkDescriptorType descriptorType,
VkDeviceSize maxBoundBufferRange,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs);
template void DescriptorSetDescBuilder::updateShaderBuffers<OutsideRenderPassCommandBufferHelper>(
ContextVk *contextVk,
OutsideRenderPassCommandBufferHelper *commandBufferHelper,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType,
VkDeviceSize maxBoundBufferRange,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs);
template void DescriptorSetDescBuilder::updateShaderBuffers<RenderPassCommandBufferHelper>(
ContextVk *contextVk,
RenderPassCommandBufferHelper *commandBufferHelper,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descriptorType,
VkDeviceSize maxBoundBufferRange,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs);
template void DescriptorSetDescBuilder::updateAtomicCounters<OutsideRenderPassCommandBufferHelper>(
ContextVk *contextVk,
OutsideRenderPassCommandBufferHelper *commandBufferHelper,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
const VkDeviceSize requiredOffsetAlignment,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs);
template void DescriptorSetDescBuilder::updateAtomicCounters<RenderPassCommandBufferHelper>(
ContextVk *contextVk,
RenderPassCommandBufferHelper *commandBufferHelper,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::BufferVector &buffers,
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
const VkDeviceSize requiredOffsetAlignment,
const BufferHelper &emptyBuffer,
const WriteDescriptorDescs &writeDescriptorDescs);
angle::Result DescriptorSetDescBuilder::updateImages(
Context *context,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const gl::ActiveTextureArray<TextureVk *> &activeImages,
const std::vector<gl::ImageUnit> &imageUnits,
const WriteDescriptorDescs &writeDescriptorDescs)
{
Renderer *renderer = context->getRenderer();
const std::vector<gl::ImageBinding> &imageBindings = executable.getImageBindings();
const std::vector<gl::LinkedUniform> &uniforms = executable.getUniforms();
if (imageBindings.empty())
{
return angle::Result::Continue;
}
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
if (imageUniform.activeShaders().none())
{
continue;
}
const gl::ShaderType firstShaderType = imageUniform.getFirstActiveShaderType();
const ShaderInterfaceVariableInfo &info =
variableInfoMap.getVariableById(firstShaderType, imageUniform.getId(firstShaderType));
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
// Texture buffers use buffer views, so they are especially handled.
if (imageBinding.textureType == gl::TextureType::Buffer)
{
// Handle format reinterpretation by looking for a view with the format specified in
// the shader (if any, instead of the format specified to glTexBuffer).
const vk::Format *format = nullptr;
if (imageUniform.getImageUnitFormat() != GL_NONE)
{
format = &renderer->getFormat(imageUniform.getImageUnitFormat());
}
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint imageUnit = imageBinding.boundImageUnits[arrayElement];
TextureVk *textureVk = activeImages[imageUnit];
uint32_t infoIndex = writeDescriptorDescs[info.binding].descriptorInfoIndex +
arrayElement + imageUniform.getOuterArrayOffset();
const vk::BufferView *view = nullptr;
ANGLE_TRY(
textureVk->getBufferViewAndRecordUse(context, format, nullptr, true, &view));
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoIndex);
infoDesc.imageViewSerialOrOffset =
textureVk->getBufferViewSerial().viewSerial.getValue();
infoDesc.imageLayoutOrRange = 0;
infoDesc.imageSubresourceRange = 0;
infoDesc.samplerOrBufferSerial = 0;
mHandles[infoIndex].bufferView = view->getHandle();
}
}
else
{
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint imageUnit = imageBinding.boundImageUnits[arrayElement];
const gl::ImageUnit &binding = imageUnits[imageUnit];
TextureVk *textureVk = activeImages[imageUnit];
vk::ImageHelper *image = &textureVk->getImage();
const vk::ImageView *imageView = nullptr;
vk::ImageOrBufferViewSubresourceSerial serial =
textureVk->getStorageImageViewSerial(binding);
ANGLE_TRY(textureVk->getStorageImageView(context, binding, &imageView));
uint32_t infoIndex = writeDescriptorDescs[info.binding].descriptorInfoIndex +
arrayElement + imageUniform.getOuterArrayOffset();
// Note: binding.access is unused because it is implied by the shader.
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoIndex);
SetBitField(infoDesc.imageLayoutOrRange, image->getCurrentImageLayout());
memcpy(&infoDesc.imageSubresourceRange, &serial.subresource, sizeof(uint32_t));
infoDesc.imageViewSerialOrOffset = serial.viewSerial.getValue();
infoDesc.samplerOrBufferSerial = 0;
mHandles[infoIndex].imageView = imageView->getHandle();
}
}
}
return angle::Result::Continue;
}
angle::Result DescriptorSetDescBuilder::updateInputAttachments(
vk::Context *context,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
FramebufferVk *framebufferVk,
const WriteDescriptorDescs &writeDescriptorDescs)
{
if (!executable.usesFramebufferFetch())
{
return angle::Result::Continue;
}
const uint32_t firstInputAttachment =
static_cast<uint32_t>(executable.getFragmentInoutIndices().first());
const ShaderInterfaceVariableInfo &baseInfo = variableInfoMap.getVariableById(
gl::ShaderType::Fragment, sh::vk::spirv::kIdInputAttachment0 + firstInputAttachment);
const uint32_t baseBinding = baseInfo.binding - firstInputAttachment;
for (size_t colorIndex : framebufferVk->getState().getColorAttachmentsMask())
{
uint32_t binding = baseBinding + static_cast<uint32_t>(colorIndex);
RenderTargetVk *renderTargetVk = framebufferVk->getColorDrawRenderTarget(colorIndex);
const vk::ImageView *imageView = nullptr;
ANGLE_TRY(renderTargetVk->getImageView(context, &imageView));
uint32_t infoIndex = writeDescriptorDescs[binding].descriptorInfoIndex;
DescriptorInfoDesc &infoDesc = mDesc.getInfoDesc(infoIndex);
// We just need any layout that represents GENERAL. The serial is also not totally precise.
ImageOrBufferViewSubresourceSerial serial = renderTargetVk->getDrawSubresourceSerial();
SetBitField(infoDesc.imageLayoutOrRange, ImageLayout::FragmentShaderWrite);
infoDesc.imageViewSerialOrOffset = serial.viewSerial.getValue();
memcpy(&infoDesc.imageSubresourceRange, &serial.subresource, sizeof(uint32_t));
infoDesc.samplerOrBufferSerial = 0;
mHandles[infoIndex].imageView = imageView->getHandle();
}
return angle::Result::Continue;
}
void DescriptorSetDescBuilder::updateDescriptorSet(Context *context,
const WriteDescriptorDescs &writeDescriptorDescs,
UpdateDescriptorSetsBuilder *updateBuilder,
VkDescriptorSet descriptorSet) const
{
mDesc.updateDescriptorSet(context, writeDescriptorDescs, updateBuilder, mHandles.data(),
descriptorSet);
}
// SharedCacheKeyManager implementation.
template <class SharedCacheKeyT>
void SharedCacheKeyManager<SharedCacheKeyT>::addKey(const SharedCacheKeyT &key)
{
// If there is invalid key in the array, use it instead of keep expanding the array
for (SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
{
if (*sharedCacheKey.get() == nullptr)
{
sharedCacheKey = key;
return;
}
}
mSharedCacheKeys.emplace_back(key);
}
template <class SharedCacheKeyT>
void SharedCacheKeyManager<SharedCacheKeyT>::releaseKeys(ContextVk *contextVk)
{
for (SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
{
if (*sharedCacheKey.get() != nullptr)
{
// Immediate destroy the cached object and the key itself when first releaseRef call is
// made
ReleaseCachedObject(contextVk, *(*sharedCacheKey.get()));
*sharedCacheKey.get() = nullptr;
}
}
mSharedCacheKeys.clear();
}
template <class SharedCacheKeyT>
void SharedCacheKeyManager<SharedCacheKeyT>::releaseKeys(Renderer *renderer)
{
for (SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
{
if (*sharedCacheKey.get() != nullptr)
{
// Immediate destroy the cached object and the key itself when first releaseKeys call is
// made
ReleaseCachedObject(renderer, *(*sharedCacheKey.get()));
*sharedCacheKey.get() = nullptr;
}
}
mSharedCacheKeys.clear();
}
template <class SharedCacheKeyT>
void SharedCacheKeyManager<SharedCacheKeyT>::destroyKeys(Renderer *renderer)
{
for (SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
{
// destroy the cache key
if (*sharedCacheKey.get() != nullptr)
{
// Immediate destroy the cached object and the key
DestroyCachedObject(renderer, *(*sharedCacheKey.get()));
*sharedCacheKey.get() = nullptr;
}
}
mSharedCacheKeys.clear();
}
template <class SharedCacheKeyT>
void SharedCacheKeyManager<SharedCacheKeyT>::clear()
{
// Caller must have already freed all caches
assertAllEntriesDestroyed();
mSharedCacheKeys.clear();
}
template <class SharedCacheKeyT>
bool SharedCacheKeyManager<SharedCacheKeyT>::containsKey(const SharedCacheKeyT &key) const
{
for (const SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
{
if (key == sharedCacheKey)
{
return true;
}
}
return false;
}
template <class SharedCacheKeyT>
void SharedCacheKeyManager<SharedCacheKeyT>::assertAllEntriesDestroyed()
{
// Caller must have already freed all caches
for (SharedCacheKeyT &sharedCacheKey : mSharedCacheKeys)
{
ASSERT(*sharedCacheKey.get() == nullptr);
}
}
// Explict instantiate for FramebufferCacheManager
template class SharedCacheKeyManager<SharedFramebufferCacheKey>;
// Explict instantiate for DescriptorSetCacheManager
template class SharedCacheKeyManager<SharedDescriptorSetCacheKey>;
// PipelineCacheAccess implementation.
std::unique_lock<std::mutex> PipelineCacheAccess::getLock()
{
if (mMutex == nullptr)
{
return std::unique_lock<std::mutex>();
}
return std::unique_lock<std::mutex>(*mMutex);
}
VkResult PipelineCacheAccess::createGraphicsPipeline(vk::Context *context,
const VkGraphicsPipelineCreateInfo &createInfo,
vk::Pipeline *pipelineOut)
{
std::unique_lock<std::mutex> lock = getLock();
return pipelineOut->initGraphics(context->getDevice(), createInfo, *mPipelineCache);
}
VkResult PipelineCacheAccess::createComputePipeline(vk::Context *context,
const VkComputePipelineCreateInfo &createInfo,
vk::Pipeline *pipelineOut)
{
std::unique_lock<std::mutex> lock = getLock();
return pipelineOut->initCompute(context->getDevice(), createInfo, *mPipelineCache);
}
void PipelineCacheAccess::merge(Renderer *renderer, const vk::PipelineCache &pipelineCache)
{
ASSERT(isThreadSafe());
std::unique_lock<std::mutex> lock = getLock();
mPipelineCache->merge(renderer->getDevice(), 1, pipelineCache.ptr());
}
} // namespace vk
// UpdateDescriptorSetsBuilder implementation.
UpdateDescriptorSetsBuilder::UpdateDescriptorSetsBuilder()
{
// Reserve reasonable amount of spaces so that for majority of apps we don't need to grow at all
constexpr size_t kDescriptorBufferInfosInitialSize = 8;
constexpr size_t kDescriptorImageInfosInitialSize = 4;
constexpr size_t kDescriptorWriteInfosInitialSize =
kDescriptorBufferInfosInitialSize + kDescriptorImageInfosInitialSize;
constexpr size_t kDescriptorBufferViewsInitialSize = 0;
mDescriptorBufferInfos.reserve(kDescriptorBufferInfosInitialSize);
mDescriptorImageInfos.reserve(kDescriptorImageInfosInitialSize);
mWriteDescriptorSets.reserve(kDescriptorWriteInfosInitialSize);
mBufferViews.reserve(kDescriptorBufferViewsInitialSize);
}
UpdateDescriptorSetsBuilder::~UpdateDescriptorSetsBuilder() = default;
template <typename T, const T *VkWriteDescriptorSet::*pInfo>
void UpdateDescriptorSetsBuilder::growDescriptorCapacity(std::vector<T> *descriptorVector,
size_t newSize)
{
const T *const oldInfoStart = descriptorVector->empty() ? nullptr : &(*descriptorVector)[0];
size_t newCapacity = std::max(descriptorVector->capacity() << 1, newSize);
descriptorVector->reserve(newCapacity);
if (oldInfoStart)
{
// patch mWriteInfo with new BufferInfo/ImageInfo pointers
for (VkWriteDescriptorSet &set : mWriteDescriptorSets)
{
if (set.*pInfo)
{
size_t index = set.*pInfo - oldInfoStart;
set.*pInfo = &(*descriptorVector)[index];
}
}
}
}
template <typename T, const T *VkWriteDescriptorSet::*pInfo>
T *UpdateDescriptorSetsBuilder::allocDescriptorInfos(std::vector<T> *descriptorVector, size_t count)
{
size_t oldSize = descriptorVector->size();
size_t newSize = oldSize + count;
if (newSize > descriptorVector->capacity())
{
// If we have reached capacity, grow the storage and patch the descriptor set with new
// buffer info pointer
growDescriptorCapacity<T, pInfo>(descriptorVector, newSize);
}
descriptorVector->resize(newSize);
return &(*descriptorVector)[oldSize];
}
VkDescriptorBufferInfo *UpdateDescriptorSetsBuilder::allocDescriptorBufferInfos(size_t count)
{
return allocDescriptorInfos<VkDescriptorBufferInfo, &VkWriteDescriptorSet::pBufferInfo>(
&mDescriptorBufferInfos, count);
}
VkDescriptorImageInfo *UpdateDescriptorSetsBuilder::allocDescriptorImageInfos(size_t count)
{
return allocDescriptorInfos<VkDescriptorImageInfo, &VkWriteDescriptorSet::pImageInfo>(
&mDescriptorImageInfos, count);
}
VkWriteDescriptorSet *UpdateDescriptorSetsBuilder::allocWriteDescriptorSets(size_t count)
{
size_t oldSize = mWriteDescriptorSets.size();
size_t newSize = oldSize + count;
mWriteDescriptorSets.resize(newSize);
return &mWriteDescriptorSets[oldSize];
}
VkBufferView *UpdateDescriptorSetsBuilder::allocBufferViews(size_t count)
{
return allocDescriptorInfos<VkBufferView, &VkWriteDescriptorSet::pTexelBufferView>(
&mBufferViews, count);
}
uint32_t UpdateDescriptorSetsBuilder::flushDescriptorSetUpdates(VkDevice device)
{
if (mWriteDescriptorSets.empty())
{
ASSERT(mDescriptorBufferInfos.empty());
ASSERT(mDescriptorImageInfos.empty());
return 0;
}
vkUpdateDescriptorSets(device, static_cast<uint32_t>(mWriteDescriptorSets.size()),
mWriteDescriptorSets.data(), 0, nullptr);
uint32_t retVal = static_cast<uint32_t>(mWriteDescriptorSets.size());
mWriteDescriptorSets.clear();
mDescriptorBufferInfos.clear();
mDescriptorImageInfos.clear();
mBufferViews.clear();
return retVal;
}
// FramebufferCache implementation.
void FramebufferCache::destroy(vk::Renderer *renderer)
{
renderer->accumulateCacheStats(VulkanCacheType::Framebuffer, mCacheStats);
for (auto &entry : mPayload)
{
vk::FramebufferHelper &tmpFB = entry.second;
tmpFB.destroy(renderer);
}
mPayload.clear();
}
bool FramebufferCache::get(ContextVk *contextVk,
const vk::FramebufferDesc &desc,
vk::Framebuffer &framebuffer)
{
ASSERT(!contextVk->getFeatures().supportsImagelessFramebuffer.enabled);
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
framebuffer.setHandle(iter->second.getFramebuffer().getHandle());
mCacheStats.hit();
return true;
}
mCacheStats.miss();
return false;
}
void FramebufferCache::insert(ContextVk *contextVk,
const vk::FramebufferDesc &desc,
vk::FramebufferHelper &&framebufferHelper)
{
ASSERT(!contextVk->getFeatures().supportsImagelessFramebuffer.enabled);
mPayload.emplace(desc, std::move(framebufferHelper));
}
void FramebufferCache::erase(ContextVk *contextVk, const vk::FramebufferDesc &desc)
{
ASSERT(!contextVk->getFeatures().supportsImagelessFramebuffer.enabled);
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::FramebufferHelper &tmpFB = iter->second;
tmpFB.release(contextVk);
mPayload.erase(desc);
}
}
// RenderPassCache implementation.
RenderPassCache::RenderPassCache() = default;
RenderPassCache::~RenderPassCache()
{
ASSERT(mPayload.empty());
}
void RenderPassCache::destroy(ContextVk *contextVk)
{
vk::Renderer *renderer = contextVk->getRenderer();
renderer->accumulateCacheStats(VulkanCacheType::CompatibleRenderPass,
mCompatibleRenderPassCacheStats);
renderer->accumulateCacheStats(VulkanCacheType::RenderPassWithOps,
mRenderPassWithOpsCacheStats);
VkDevice device = renderer->getDevice();
// Make sure there are no jobs referencing the render pass cache.
contextVk->getShareGroup()->waitForCurrentMonolithicPipelineCreationTask();
for (auto &outerIt : mPayload)
{
for (auto &innerIt : outerIt.second)
{
innerIt.second.destroy(device);
}
}
mPayload.clear();
}
void RenderPassCache::clear(ContextVk *contextVk)
{
// Make sure there are no jobs referencing the render pass cache.
contextVk->getShareGroup()->waitForCurrentMonolithicPipelineCreationTask();
for (auto &outerIt : mPayload)
{
for (auto &innerIt : outerIt.second)
{
innerIt.second.release(contextVk);
}
}
mPayload.clear();
}
// static
void RenderPassCache::InitializeOpsForCompatibleRenderPass(const vk::RenderPassDesc &desc,
vk::AttachmentOpsArray *opsOut)
{
// This API is only used by getCompatibleRenderPass() to create a compatible render pass. The
// following does not participate in render pass compatibility, so could take any value:
//
// - Load and store ops
// - Attachment layouts
// - Existance of resolve attachment (if single subpass)
//
// The values chosen here are arbitrary.
vk::PackedAttachmentIndex colorIndexVk(0);
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
if (!desc.isColorAttachmentEnabled(colorIndexGL))
{
continue;
}
const vk::ImageLayout imageLayout = vk::ImageLayout::ColorWrite;
opsOut->initWithLoadStore(colorIndexVk, imageLayout, imageLayout);
++colorIndexVk;
}
if (desc.hasDepthStencilAttachment())
{
const vk::ImageLayout imageLayout = vk::ImageLayout::DepthWriteStencilWrite;
opsOut->initWithLoadStore(colorIndexVk, imageLayout, imageLayout);
}
}
angle::Result RenderPassCache::addCompatibleRenderPass(ContextVk *contextVk,
const vk::RenderPassDesc &desc,
const vk::RenderPass **renderPassOut)
{
vk::AttachmentOpsArray ops;
InitializeOpsForCompatibleRenderPass(desc, &ops);
return getRenderPassWithOpsImpl(contextVk, desc, ops, false, renderPassOut);
}
angle::Result RenderPassCache::getRenderPassWithOps(ContextVk *contextVk,
const vk::RenderPassDesc &desc,
const vk::AttachmentOpsArray &attachmentOps,
const vk::RenderPass **renderPassOut)
{
return getRenderPassWithOpsImpl(contextVk, desc, attachmentOps, true, renderPassOut);
}
angle::Result RenderPassCache::getRenderPassWithOpsImpl(ContextVk *contextVk,
const vk::RenderPassDesc &desc,
const vk::AttachmentOpsArray &attachmentOps,
bool updatePerfCounters,
const vk::RenderPass **renderPassOut)
{
auto outerIt = mPayload.find(desc);
if (outerIt != mPayload.end())
{
InnerCache &innerCache = outerIt->second;
auto innerIt = innerCache.find(attachmentOps);
if (innerIt != innerCache.end())
{
// TODO(jmadill): Could possibly use an MRU cache here.
vk::GetRenderPassAndUpdateCounters(contextVk, updatePerfCounters, &innerIt->second,
renderPassOut);
mRenderPassWithOpsCacheStats.hit();
return angle::Result::Continue;
}
}
else
{
auto emplaceResult = mPayload.emplace(desc, InnerCache());
outerIt = emplaceResult.first;
}
mRenderPassWithOpsCacheStats.missAndIncrementSize();
vk::RenderPassHelper newRenderPass;
ANGLE_TRY(MakeRenderPass(contextVk, desc, attachmentOps, &newRenderPass.getRenderPass(),
&newRenderPass.getPerfCounters()));
InnerCache &innerCache = outerIt->second;
auto insertPos = innerCache.emplace(attachmentOps, std::move(newRenderPass));
vk::GetRenderPassAndUpdateCounters(contextVk, updatePerfCounters, &insertPos.first->second,
renderPassOut);
// TODO(jmadill): Trim cache, and pre-populate with the most common RPs on startup.
return angle::Result::Continue;
}
// static
angle::Result RenderPassCache::MakeRenderPass(vk::Context *context,
const vk::RenderPassDesc &desc,
const vk::AttachmentOpsArray &ops,
vk::RenderPass *renderPass,
vk::RenderPassPerfCounters *renderPassCounters)
{
constexpr VkAttachmentReference2 kUnusedAttachment = {VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
nullptr, VK_ATTACHMENT_UNUSED,
VK_IMAGE_LAYOUT_UNDEFINED, 0};
const bool needInputAttachments = desc.hasFramebufferFetch();
const bool isRenderToTextureThroughExtension =
desc.isRenderToTexture() &&
(context->getFeatures().supportsMultisampledRenderToSingleSampled.enabled ||
context->getFeatures().supportsMultisampledRenderToSingleSampledGOOGLEX.enabled);
const bool isRenderToTextureThroughEmulation =
desc.isRenderToTexture() && !isRenderToTextureThroughExtension;
const uint8_t descSamples = desc.samples();
const uint8_t attachmentSamples = isRenderToTextureThroughExtension ? 1 : descSamples;
const uint8_t renderToTextureSamples = isRenderToTextureThroughExtension ? descSamples : 1;
// Unpack the packed and split representation into the format required by Vulkan.
gl::DrawBuffersVector<VkAttachmentReference2> colorAttachmentRefs;
gl::DrawBuffersVector<VkAttachmentReference2> colorResolveAttachmentRefs;
VkAttachmentReference2 depthStencilAttachmentRef = kUnusedAttachment;
VkAttachmentReference2 depthStencilResolveAttachmentRef = kUnusedAttachment;
VkAttachmentReference2 fragmentShadingRateAttachmentRef = kUnusedAttachment;
// The list of attachments includes all non-resolve and resolve attachments.
vk::FramebufferAttachmentArray<VkAttachmentDescription2> attachmentDescs;
// Track invalidated attachments so their resolve attachments can be invalidated as well.
// Resolve attachments can be removed in that case if the render pass has only one subpass
// (which is the case if there are no unresolve attachments).
gl::DrawBufferMask isMSRTTEmulationColorInvalidated;
bool isMSRTTEmulationDepthInvalidated = false;
bool isMSRTTEmulationStencilInvalidated = false;
const bool hasUnresolveAttachments =
desc.getColorUnresolveAttachmentMask().any() || desc.hasDepthStencilUnresolveAttachment();
const bool canRemoveResolveAttachments =
isRenderToTextureThroughEmulation && !hasUnresolveAttachments;
#if defined(ANGLE_PLATFORM_ANDROID)
// if yuv, we're going to chain this on to some VkAttachmentDescription2
VkExternalFormatANDROID externalFormat = {VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID, nullptr,
0};
#endif
// Pack color attachments
vk::PackedAttachmentIndex attachmentCount(0);
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
// Vulkan says:
//
// > Each element of the pColorAttachments array corresponds to an output location in the
// > shader, i.e. if the shader declares an output variable decorated with a Location value
// > of X, then it uses the attachment provided in pColorAttachments[X].
//
// This means that colorAttachmentRefs is indexed by colorIndexGL. Where the color
// attachment is disabled, a reference with VK_ATTACHMENT_UNUSED is given.
if (!desc.isColorAttachmentEnabled(colorIndexGL))
{
colorAttachmentRefs.push_back(kUnusedAttachment);
continue;
}
angle::FormatID attachmentFormatID = desc[colorIndexGL];
ASSERT(attachmentFormatID != angle::FormatID::NONE);
bool isYUVExternalFormat = vk::IsYUVExternalFormat(attachmentFormatID);
if (isYUVExternalFormat &&
context->getRenderer()->nullColorAttachmentWithExternalFormatResolve())
{
colorAttachmentRefs.push_back(kUnusedAttachment);
// temporary workaround for ARM driver assertion. Will remove once driver fix lands
colorAttachmentRefs.back().layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachmentRefs.back().aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
continue;
}
VkAttachmentReference2 colorRef = {};
colorRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
colorRef.attachment = attachmentCount.get();
colorRef.layout =
needInputAttachments
? VK_IMAGE_LAYOUT_GENERAL
: vk::ConvertImageLayoutToVkImageLayout(
context, static_cast<vk::ImageLayout>(ops[attachmentCount].initialLayout));
colorRef.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorAttachmentRefs.push_back(colorRef);
vk::UnpackAttachmentDesc(context, &attachmentDescs[attachmentCount.get()],
attachmentFormatID, attachmentSamples, ops[attachmentCount]);
// If this renderpass uses EXT_srgb_write_control, we need to override the format to its
// linear counterpart. Formats that cannot be reinterpreted are exempt from this
// requirement.
angle::FormatID linearFormat = rx::ConvertToLinear(attachmentFormatID);
if (linearFormat != angle::FormatID::NONE)
{
if (desc.getSRGBWriteControlMode() == gl::SrgbWriteControlMode::Linear)
{
attachmentFormatID = linearFormat;
}
}
if (isYUVExternalFormat)
{
const vk::ExternalYuvFormatInfo &externalFormatInfo =
context->getRenderer()->getExternalFormatTable()->getExternalFormatInfo(
attachmentFormatID);
attachmentDescs[attachmentCount.get()].format =
externalFormatInfo.colorAttachmentFormat;
}
else
{
attachmentDescs[attachmentCount.get()].format =
vk::GetVkFormatFromFormatID(attachmentFormatID);
}
ASSERT(attachmentDescs[attachmentCount.get()].format != VK_FORMAT_UNDEFINED);
// When multisampled-render-to-texture is used, invalidating an attachment invalidates both
// the multisampled and the resolve attachments. Otherwise, the resolve attachment is
// independent of the multisampled attachment, and is never invalidated.
// This is also the case for external format resolve
if (isRenderToTextureThroughEmulation)
{
isMSRTTEmulationColorInvalidated.set(colorIndexGL, ops[attachmentCount].isInvalidated);
}
++attachmentCount;
}
// Pack depth/stencil attachment, if any
if (desc.hasDepthStencilAttachment())
{
uint32_t depthStencilIndexGL = static_cast<uint32_t>(desc.depthStencilAttachmentIndex());
angle::FormatID attachmentFormatID = desc[depthStencilIndexGL];
ASSERT(attachmentFormatID != angle::FormatID::NONE);
depthStencilAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
depthStencilAttachmentRef.attachment = attachmentCount.get();
depthStencilAttachmentRef.layout = ConvertImageLayoutToVkImageLayout(
context, static_cast<vk::ImageLayout>(ops[attachmentCount].initialLayout));
depthStencilAttachmentRef.aspectMask =
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
vk::UnpackAttachmentDesc(context, &attachmentDescs[attachmentCount.get()],
attachmentFormatID, attachmentSamples, ops[attachmentCount]);
if (isRenderToTextureThroughEmulation)
{
isMSRTTEmulationDepthInvalidated = ops[attachmentCount].isInvalidated;
isMSRTTEmulationStencilInvalidated = ops[attachmentCount].isStencilInvalidated;
}
++attachmentCount;
}
// Pack fragment shading rate attachment, if any
if (desc.hasFragmentShadingAttachment())
{
vk::UnpackFragmentShadingRateAttachmentDesc(&attachmentDescs[attachmentCount.get()]);
fragmentShadingRateAttachmentRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
fragmentShadingRateAttachmentRef.attachment = attachmentCount.get();
fragmentShadingRateAttachmentRef.layout =
VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
++attachmentCount;
}
// Pack color resolve attachments
const uint32_t nonResolveAttachmentCount = attachmentCount.get();
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
if (!desc.hasColorResolveAttachment(colorIndexGL))
{
colorResolveAttachmentRefs.push_back(kUnusedAttachment);
continue;
}
ASSERT(desc.isColorAttachmentEnabled(colorIndexGL));
angle::FormatID attachmentFormatID = desc[colorIndexGL];
bool isYUVExternalFormat = vk::IsYUVExternalFormat(attachmentFormatID);
VkAttachmentReference2 colorRef = {};
colorRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
colorRef.attachment = attachmentCount.get();
colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorRef.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
// If color attachment is invalidated, try to remove its resolve attachment altogether.
if (canRemoveResolveAttachments && isMSRTTEmulationColorInvalidated.test(colorIndexGL))
{
colorResolveAttachmentRefs.push_back(kUnusedAttachment);
}
else
{
colorResolveAttachmentRefs.push_back(colorRef);
}
const bool isInvalidated = isMSRTTEmulationColorInvalidated.test(colorIndexGL);
if (isYUVExternalFormat &&
context->getRenderer()->nullColorAttachmentWithExternalFormatResolve())
{
vk::UnpackAttachmentDesc(context, &attachmentDescs[attachmentCount.get()],
attachmentFormatID, attachmentSamples, ops[attachmentCount]);
}
else
{
vk::UnpackColorResolveAttachmentDesc(
&attachmentDescs[attachmentCount.get()], attachmentFormatID,
{desc.hasColorUnresolveAttachment(colorIndexGL), isInvalidated, false});
}
#if defined(ANGLE_PLATFORM_ANDROID)
// For rendering to YUV, chain on the external format info to the resolve attachment
if (isYUVExternalFormat)
{
const vk::ExternalYuvFormatInfo &externalFormatInfo =
context->getRenderer()->getExternalFormatTable()->getExternalFormatInfo(
attachmentFormatID);
externalFormat.externalFormat = externalFormatInfo.externalFormat;
VkAttachmentDescription2 &attachment = attachmentDescs[attachmentCount.get()];
attachment.pNext = &externalFormat;
ASSERT(attachment.format == VK_FORMAT_UNDEFINED);
}
#endif
++attachmentCount;
}
// Pack depth/stencil resolve attachment, if any
if (desc.hasDepthStencilResolveAttachment())
{
ASSERT(desc.hasDepthStencilAttachment());
uint32_t depthStencilIndexGL = static_cast<uint32_t>(desc.depthStencilAttachmentIndex());
angle::FormatID attachmentFormatID = desc[depthStencilIndexGL];
const angle::Format &angleFormat = angle::Format::Get(attachmentFormatID);
bool isDepthUnused = false;
bool isStencilUnused = false;
// Treat missing aspect as invalidated for the purpose of the resolve attachment.
if (angleFormat.depthBits == 0)
{
isMSRTTEmulationDepthInvalidated = true;
}
else if (!desc.hasDepthResolveAttachment())
{
isDepthUnused = true;
}
if (angleFormat.stencilBits == 0)
{
isMSRTTEmulationStencilInvalidated = true;
}
else if (!desc.hasStencilResolveAttachment())
{
isStencilUnused = true;
}
depthStencilResolveAttachmentRef.attachment = attachmentCount.get();
depthStencilResolveAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthStencilResolveAttachmentRef.aspectMask = 0;
if (!isMSRTTEmulationDepthInvalidated && !isDepthUnused)
{
depthStencilResolveAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (!isMSRTTEmulationStencilInvalidated && !isStencilUnused)
{
depthStencilResolveAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
vk::UnpackDepthStencilResolveAttachmentDesc(
context, &attachmentDescs[attachmentCount.get()], attachmentFormatID,
{desc.hasDepthUnresolveAttachment(), isMSRTTEmulationDepthInvalidated, isDepthUnused},
{desc.hasStencilUnresolveAttachment(), isMSRTTEmulationStencilInvalidated,
isStencilUnused});
++attachmentCount;
}
vk::SubpassVector<VkSubpassDescription2> subpassDesc;
// If any attachment needs to be unresolved, create an initial subpass for this purpose. Note
// that the following arrays are used in initializing a VkSubpassDescription2 in subpassDesc,
// which is in turn used in VkRenderPassCreateInfo below. That is why they are declared in the
// same scope.
gl::DrawBuffersVector<VkAttachmentReference2> unresolveColorAttachmentRefs;
VkAttachmentReference2 unresolveDepthStencilAttachmentRef = kUnusedAttachment;
vk::FramebufferAttachmentsVector<VkAttachmentReference2> unresolveInputAttachmentRefs;
vk::FramebufferAttachmentsVector<uint32_t> unresolvePreserveAttachmentRefs;
if (hasUnresolveAttachments)
{
subpassDesc.push_back({});
vk::InitializeUnresolveSubpass(
desc, colorAttachmentRefs, colorResolveAttachmentRefs, depthStencilAttachmentRef,
depthStencilResolveAttachmentRef, &unresolveColorAttachmentRefs,
&unresolveDepthStencilAttachmentRef, &unresolveInputAttachmentRefs,
&unresolvePreserveAttachmentRefs, &subpassDesc.back());
}
subpassDesc.push_back({});
VkSubpassDescription2 *applicationSubpass = &subpassDesc.back();
applicationSubpass->sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2;
applicationSubpass->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
applicationSubpass->inputAttachmentCount =
needInputAttachments ? static_cast<uint32_t>(colorAttachmentRefs.size()) : 0;
applicationSubpass->pInputAttachments =
needInputAttachments ? colorAttachmentRefs.data() : nullptr;
applicationSubpass->colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
applicationSubpass->pColorAttachments = colorAttachmentRefs.data();
applicationSubpass->pResolveAttachments = attachmentCount.get() > nonResolveAttachmentCount
? colorResolveAttachmentRefs.data()
: nullptr;
applicationSubpass->pDepthStencilAttachment =
(depthStencilAttachmentRef.attachment != VK_ATTACHMENT_UNUSED ? &depthStencilAttachmentRef
: nullptr);
// Specify rasterization order for color on the subpass when available and
// there is framebuffer fetch. This is required when the corresponding
// flag is set on the pipeline.
if (context->getFeatures().supportsRasterizationOrderAttachmentAccess.enabled &&
desc.hasFramebufferFetch())
{
for (VkSubpassDescription2 &subpass : subpassDesc)
{
subpass.flags |=
VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_BIT_EXT;
}
}
if (context->getFeatures().supportsLegacyDithering.enabled && desc.isLegacyDitherEnabled())
{
subpassDesc.back().flags |= VK_SUBPASS_DESCRIPTION_ENABLE_LEGACY_DITHERING_BIT_EXT;
}
// If depth/stencil is to be resolved, add a VkSubpassDescriptionDepthStencilResolve to the
// pNext chain of the subpass description.
VkSubpassDescriptionDepthStencilResolve depthStencilResolve = {};
VkSubpassDescriptionDepthStencilResolve msrtssResolve = {};
VkMultisampledRenderToSingleSampledInfoEXT msrtss = {};
VkMultisampledRenderToSingleSampledInfoGOOGLEX msrtssGOOGLEX = {};
if (desc.hasDepthStencilResolveAttachment())
{
ASSERT(!isRenderToTextureThroughExtension);
uint32_t depthStencilIndexGL = static_cast<uint32_t>(desc.depthStencilAttachmentIndex());
const angle::Format &angleFormat = angle::Format::Get(desc[depthStencilIndexGL]);
depthStencilResolve.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE;
if (!context->getFeatures().supportsDepthStencilIndependentResolveNone.enabled)
{
// Assert that depth/stencil is not separately resolved without this feature
ASSERT(desc.hasDepthResolveAttachment() || angleFormat.depthBits == 0);
ASSERT(desc.hasStencilResolveAttachment() || angleFormat.stencilBits == 0);
depthStencilResolve.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
depthStencilResolve.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
}
else
{
depthStencilResolve.depthResolveMode =
desc.hasDepthResolveAttachment() && !isMSRTTEmulationDepthInvalidated
? VK_RESOLVE_MODE_SAMPLE_ZERO_BIT
: VK_RESOLVE_MODE_NONE;
depthStencilResolve.stencilResolveMode =
desc.hasStencilResolveAttachment() && !isMSRTTEmulationStencilInvalidated
? VK_RESOLVE_MODE_SAMPLE_ZERO_BIT
: VK_RESOLVE_MODE_NONE;
}
// If depth/stencil attachment is invalidated or is otherwise not really resolved, don't set
// it as the resolve attachment in the first place.
const bool isResolvingDepth = !isMSRTTEmulationDepthInvalidated &&
angleFormat.depthBits > 0 &&
depthStencilResolve.depthResolveMode != VK_RESOLVE_MODE_NONE;
const bool isResolvingStencil =
!isMSRTTEmulationStencilInvalidated && angleFormat.stencilBits > 0 &&
depthStencilResolve.stencilResolveMode != VK_RESOLVE_MODE_NONE;
if (isResolvingDepth || isResolvingStencil)
{
depthStencilResolve.pDepthStencilResolveAttachment = &depthStencilResolveAttachmentRef;
vk::AddToPNextChain(&subpassDesc.back(), &depthStencilResolve);
}
}
else if (isRenderToTextureThroughExtension)
{
ASSERT(subpassDesc.size() == 1);
vk::InitializeMSRTSS(context, renderToTextureSamples, &subpassDesc.back(), &msrtssResolve,
&msrtss, &msrtssGOOGLEX);
}
VkFragmentShadingRateAttachmentInfoKHR fragmentShadingRateAttachmentInfo = {};
if (desc.hasFragmentShadingAttachment())
{
fragmentShadingRateAttachmentInfo.sType =
VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR;
fragmentShadingRateAttachmentInfo.pFragmentShadingRateAttachment =
&fragmentShadingRateAttachmentRef;
fragmentShadingRateAttachmentInfo.shadingRateAttachmentTexelSize =
context->getRenderer()->getMaxFragmentShadingRateAttachmentTexelSize();
vk::AddToPNextChain(&subpassDesc.back(), &fragmentShadingRateAttachmentInfo);
}
std::vector<VkSubpassDependency2> subpassDependencies;
if (hasUnresolveAttachments)
{
vk::InitializeUnresolveSubpassDependencies(
subpassDesc, desc.getColorUnresolveAttachmentMask().any(),
desc.hasDepthStencilUnresolveAttachment(), &subpassDependencies);
}
const uint32_t drawSubpassIndex = static_cast<uint32_t>(subpassDesc.size()) - 1;
vk::InitializeDefaultSubpassSelfDependencies(context, desc, drawSubpassIndex,
&subpassDependencies);
VkRenderPassCreateInfo2 createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2;
createInfo.attachmentCount = attachmentCount.get();
createInfo.pAttachments = attachmentDescs.data();
createInfo.subpassCount = static_cast<uint32_t>(subpassDesc.size());
createInfo.pSubpasses = subpassDesc.data();
if (!subpassDependencies.empty())
{
createInfo.dependencyCount = static_cast<uint32_t>(subpassDependencies.size());
createInfo.pDependencies = subpassDependencies.data();
}
const uint32_t viewMask = angle::BitMask<uint32_t>(desc.viewCount());
if (desc.viewCount() > 0)
{
vk::SetRenderPassViewMask(context, &viewMask, &createInfo, &subpassDesc);
}
// If VK_KHR_create_renderpass2 is not supported, we must use core Vulkan 1.0. This is
// increasingly uncommon. Note that extensions that require chaining information to subpasses
// are automatically not used when this extension is not available.
if (!context->getFeatures().supportsRenderpass2.enabled)
{
ANGLE_TRY(vk::CreateRenderPass1(context, createInfo, desc.viewCount(), renderPass));
}
else
{
ANGLE_VK_TRY(context, renderPass->init2(context->getDevice(), createInfo));
}
if (renderPassCounters != nullptr)
{
// Calculate perf counters associated with this render pass, such as load/store ops,
// unresolve and resolve operations etc. This information is taken out of the render pass
// create info. Depth/stencil resolve attachment uses RenderPass2 structures, so it's
// passed in separately.
vk::UpdateRenderPassPerfCounters(desc, createInfo, depthStencilResolve, renderPassCounters);
}
return angle::Result::Continue;
}
// GraphicsPipelineCache implementation.
template <typename Hash>
void GraphicsPipelineCache<Hash>::destroy(vk::Context *context)
{
if (vk::ShouldDumpPipelineCacheGraph(context) && !mPayload.empty())
{
vk::DumpPipelineCacheGraph<Hash>(context, mPayload);
}
accumulateCacheStats(context->getRenderer());
VkDevice device = context->getDevice();
for (auto &item : mPayload)
{
vk::PipelineHelper &pipeline = item.second;
pipeline.destroy(device);
}
mPayload.clear();
}
template <typename Hash>
void GraphicsPipelineCache<Hash>::release(vk::Context *context)
{
if (vk::ShouldDumpPipelineCacheGraph(context) && !mPayload.empty())
{
vk::DumpPipelineCacheGraph<Hash>(context, mPayload);
}
for (auto &item : mPayload)
{
vk::PipelineHelper &pipeline = item.second;
pipeline.release(context);
}
mPayload.clear();
}
template <typename Hash>
angle::Result GraphicsPipelineCache<Hash>::createPipeline(
vk::Context *context,
vk::PipelineCacheAccess *pipelineCache,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const vk::ShaderModuleMap &shaders,
const vk::SpecializationConstants &specConsts,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut)
{
vk::Pipeline newPipeline;
vk::CacheLookUpFeedback feedback = vk::CacheLookUpFeedback::None;
// This "if" is left here for the benefit of VulkanPipelineCachePerfTest.
if (context != nullptr)
{
constexpr vk::GraphicsPipelineSubset kSubset =
GraphicsPipelineCacheTypeHelper<Hash>::kSubset;
ANGLE_VK_TRY(context, desc.initializePipeline(context, pipelineCache, kSubset,
compatibleRenderPass, pipelineLayout, shaders,
specConsts, &newPipeline, &feedback));
}
if (source == PipelineSource::WarmUp)
{
// Warm up task will pass in the placeholder PipelineHelper created in
// ProgramExecutableVk::getPipelineCacheWarmUpTasks. Update that with the newly created
// pipeline.
**pipelineOut =
vk::PipelineHelper(std::move(newPipeline), vk::CacheLookUpFeedback::WarmUpMiss);
}
else
{
addToCache(source, desc, std::move(newPipeline), feedback, descPtrOut, pipelineOut);
}
return angle::Result::Continue;
}
template <typename Hash>
angle::Result GraphicsPipelineCache<Hash>::linkLibraries(
vk::Context *context,
vk::PipelineCacheAccess *pipelineCache,
const vk::GraphicsPipelineDesc &desc,
const vk::PipelineLayout &pipelineLayout,
vk::PipelineHelper *vertexInputPipeline,
vk::PipelineHelper *shadersPipeline,
vk::PipelineHelper *fragmentOutputPipeline,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut)
{
vk::Pipeline newPipeline;
vk::CacheLookUpFeedback feedback = vk::CacheLookUpFeedback::None;
ANGLE_TRY(vk::InitializePipelineFromLibraries(
context, pipelineCache, pipelineLayout, *vertexInputPipeline, *shadersPipeline,
*fragmentOutputPipeline, &newPipeline, &feedback));
addToCache(PipelineSource::DrawLinked, desc, std::move(newPipeline), feedback, descPtrOut,
pipelineOut);
(*pipelineOut)->setLinkedLibraryReferences(shadersPipeline);
return angle::Result::Continue;
}
template <typename Hash>
void GraphicsPipelineCache<Hash>::addToCache(PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
vk::Pipeline &&pipeline,
vk::CacheLookUpFeedback feedback,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut)
{
ASSERT(mPayload.find(desc) == mPayload.end());
mCacheStats.missAndIncrementSize();
switch (source)
{
case PipelineSource::WarmUp:
feedback = feedback == vk::CacheLookUpFeedback::Hit
? vk::CacheLookUpFeedback::WarmUpHit
: vk::CacheLookUpFeedback::WarmUpMiss;
break;
case PipelineSource::DrawLinked:
feedback = feedback == vk::CacheLookUpFeedback::Hit
? vk::CacheLookUpFeedback::LinkedDrawHit
: vk::CacheLookUpFeedback::LinkedDrawMiss;
break;
case PipelineSource::Utils:
feedback = feedback == vk::CacheLookUpFeedback::Hit
? vk::CacheLookUpFeedback::UtilsHit
: vk::CacheLookUpFeedback::UtilsMiss;
break;
default:
break;
}
auto insertedItem = mPayload.emplace(std::piecewise_construct, std::forward_as_tuple(desc),
std::forward_as_tuple(std::move(pipeline), feedback));
*descPtrOut = &insertedItem.first->first;
*pipelineOut = &insertedItem.first->second;
}
template <typename Hash>
void GraphicsPipelineCache<Hash>::populate(const vk::GraphicsPipelineDesc &desc,
vk::Pipeline &&pipeline,
vk::PipelineHelper **pipelineHelperOut)
{
auto item = mPayload.find(desc);
if (item != mPayload.end())
{
return;
}
// This function is used by -
// 1. WarmUp tasks to insert placeholder pipelines
// 2. VulkanPipelineCachePerfTest
auto insertedItem =
mPayload.emplace(std::piecewise_construct, std::forward_as_tuple(desc),
std::forward_as_tuple(std::move(pipeline), vk::CacheLookUpFeedback::None));
if (pipelineHelperOut)
{
ASSERT(insertedItem.second);
*pipelineHelperOut = &insertedItem.first->second;
}
}
// Instantiate the pipeline cache functions
template void GraphicsPipelineCache<GraphicsPipelineDescCompleteHash>::destroy(
vk::Context *context);
template void GraphicsPipelineCache<GraphicsPipelineDescCompleteHash>::release(
vk::Context *context);
template angle::Result GraphicsPipelineCache<GraphicsPipelineDescCompleteHash>::createPipeline(
vk::Context *context,
vk::PipelineCacheAccess *pipelineCache,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const vk::ShaderModuleMap &shaders,
const vk::SpecializationConstants &specConsts,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
template angle::Result GraphicsPipelineCache<GraphicsPipelineDescCompleteHash>::linkLibraries(
vk::Context *context,
vk::PipelineCacheAccess *pipelineCache,
const vk::GraphicsPipelineDesc &desc,
const vk::PipelineLayout &pipelineLayout,
vk::PipelineHelper *vertexInputPipeline,
vk::PipelineHelper *shadersPipeline,
vk::PipelineHelper *fragmentOutputPipeline,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
template void GraphicsPipelineCache<GraphicsPipelineDescCompleteHash>::populate(
const vk::GraphicsPipelineDesc &desc,
vk::Pipeline &&pipeline,
vk::PipelineHelper **pipelineHelperOut);
template void GraphicsPipelineCache<GraphicsPipelineDescVertexInputHash>::destroy(
vk::Context *context);
template void GraphicsPipelineCache<GraphicsPipelineDescVertexInputHash>::release(
vk::Context *context);
template angle::Result GraphicsPipelineCache<GraphicsPipelineDescVertexInputHash>::createPipeline(
vk::Context *context,
vk::PipelineCacheAccess *pipelineCache,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const vk::ShaderModuleMap &shaders,
const vk::SpecializationConstants &specConsts,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
template void GraphicsPipelineCache<GraphicsPipelineDescVertexInputHash>::populate(
const vk::GraphicsPipelineDesc &desc,
vk::Pipeline &&pipeline,
vk::PipelineHelper **pipelineHelperOut);
template void GraphicsPipelineCache<GraphicsPipelineDescShadersHash>::destroy(vk::Context *context);
template void GraphicsPipelineCache<GraphicsPipelineDescShadersHash>::release(vk::Context *context);
template angle::Result GraphicsPipelineCache<GraphicsPipelineDescShadersHash>::createPipeline(
vk::Context *context,
vk::PipelineCacheAccess *pipelineCache,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const vk::ShaderModuleMap &shaders,
const vk::SpecializationConstants &specConsts,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
template void GraphicsPipelineCache<GraphicsPipelineDescShadersHash>::populate(
const vk::GraphicsPipelineDesc &desc,
vk::Pipeline &&pipeline,
vk::PipelineHelper **pipelineHelperOut);
template void GraphicsPipelineCache<GraphicsPipelineDescFragmentOutputHash>::destroy(
vk::Context *context);
template void GraphicsPipelineCache<GraphicsPipelineDescFragmentOutputHash>::release(
vk::Context *context);
template angle::Result
GraphicsPipelineCache<GraphicsPipelineDescFragmentOutputHash>::createPipeline(
vk::Context *context,
vk::PipelineCacheAccess *pipelineCache,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const vk::ShaderModuleMap &shaders,
const vk::SpecializationConstants &specConsts,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
template void GraphicsPipelineCache<GraphicsPipelineDescFragmentOutputHash>::populate(
const vk::GraphicsPipelineDesc &desc,
vk::Pipeline &&pipeline,
vk::PipelineHelper **pipelineHelperOut);
// DescriptorSetLayoutCache implementation.
DescriptorSetLayoutCache::DescriptorSetLayoutCache() = default;
DescriptorSetLayoutCache::~DescriptorSetLayoutCache()
{
ASSERT(mPayload.empty());
}
void DescriptorSetLayoutCache::destroy(vk::Renderer *renderer)
{
renderer->accumulateCacheStats(VulkanCacheType::DescriptorSetLayout, mCacheStats);
VkDevice device = renderer->getDevice();
for (auto &item : mPayload)
{
vk::RefCountedDescriptorSetLayout &layout = item.second;
ASSERT(!layout.isReferenced());
layout.get().destroy(device);
}
mPayload.clear();
}
angle::Result DescriptorSetLayoutCache::getDescriptorSetLayout(
vk::Context *context,
const vk::DescriptorSetLayoutDesc &desc,
vk::AtomicBindingPointer<vk::DescriptorSetLayout> *descriptorSetLayoutOut)
{
// Note: this function may be called without holding the share group lock.
std::unique_lock<std::mutex> lock(mMutex);
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::RefCountedDescriptorSetLayout &layout = iter->second;
descriptorSetLayoutOut->set(&layout);
mCacheStats.hit();
return angle::Result::Continue;
}
mCacheStats.missAndIncrementSize();
// We must unpack the descriptor set layout description.
vk::DescriptorSetLayoutBindingVector bindingVector;
desc.unpackBindings(&bindingVector);
VkDescriptorSetLayoutCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
createInfo.flags = 0;
createInfo.bindingCount = static_cast<uint32_t>(bindingVector.size());
createInfo.pBindings = bindingVector.data();
vk::DescriptorSetLayout newLayout;
ANGLE_VK_TRY(context, newLayout.init(context->getDevice(), createInfo));
auto insertedItem = mPayload.emplace(desc, std::move(newLayout));
vk::RefCountedDescriptorSetLayout &insertedLayout = insertedItem.first->second;
descriptorSetLayoutOut->set(&insertedLayout);
return angle::Result::Continue;
}
// PipelineLayoutCache implementation.
PipelineLayoutCache::PipelineLayoutCache() = default;
PipelineLayoutCache::~PipelineLayoutCache()
{
ASSERT(mPayload.empty());
}
void PipelineLayoutCache::destroy(vk::Renderer *renderer)
{
accumulateCacheStats(renderer);
VkDevice device = renderer->getDevice();
for (auto &item : mPayload)
{
vk::RefCountedPipelineLayout &layout = item.second;
layout.get().destroy(device);
}
mPayload.clear();
}
angle::Result PipelineLayoutCache::getPipelineLayout(
vk::Context *context,
const vk::PipelineLayoutDesc &desc,
const vk::DescriptorSetLayoutPointerArray &descriptorSetLayouts,
vk::AtomicBindingPointer<vk::PipelineLayout> *pipelineLayoutOut)
{
// Note: this function may be called without holding the share group lock.
std::unique_lock<std::mutex> lock(mMutex);
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::RefCountedPipelineLayout &layout = iter->second;
pipelineLayoutOut->set(&layout);
mCacheStats.hit();
return angle::Result::Continue;
}
mCacheStats.missAndIncrementSize();
// Note this does not handle gaps in descriptor set layouts gracefully.
angle::FixedVector<VkDescriptorSetLayout, vk::kMaxDescriptorSetLayouts> setLayoutHandles;
for (const vk::AtomicBindingPointer<vk::DescriptorSetLayout> &layoutPtr : descriptorSetLayouts)
{
if (layoutPtr.valid())
{
VkDescriptorSetLayout setLayout = layoutPtr.get().getHandle();
if (setLayout != VK_NULL_HANDLE)
{
setLayoutHandles.push_back(setLayout);
}
}
}
const vk::PackedPushConstantRange &descPushConstantRange = desc.getPushConstantRange();
VkPushConstantRange pushConstantRange;
pushConstantRange.stageFlags = descPushConstantRange.stageMask;
pushConstantRange.offset = descPushConstantRange.offset;
pushConstantRange.size = descPushConstantRange.size;
// No pipeline layout found. We must create a new one.
VkPipelineLayoutCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.flags = 0;
createInfo.setLayoutCount = static_cast<uint32_t>(setLayoutHandles.size());
createInfo.pSetLayouts = setLayoutHandles.data();
if (pushConstantRange.size > 0)
{
createInfo.pushConstantRangeCount = 1;
createInfo.pPushConstantRanges = &pushConstantRange;
}
vk::PipelineLayout newLayout;
ANGLE_VK_TRY(context, newLayout.init(context->getDevice(), createInfo));
auto insertedItem = mPayload.emplace(desc, std::move(newLayout));
vk::RefCountedPipelineLayout &insertedLayout = insertedItem.first->second;
pipelineLayoutOut->set(&insertedLayout);
return angle::Result::Continue;
}
// YuvConversionCache implementation
SamplerYcbcrConversionCache::SamplerYcbcrConversionCache() = default;
SamplerYcbcrConversionCache::~SamplerYcbcrConversionCache()
{
ASSERT(mExternalFormatPayload.empty() && mVkFormatPayload.empty());
}
void SamplerYcbcrConversionCache::destroy(vk::Renderer *renderer)
{
renderer->accumulateCacheStats(VulkanCacheType::SamplerYcbcrConversion, mCacheStats);
VkDevice device = renderer->getDevice();
for (auto &iter : mExternalFormatPayload)
{
vk::SamplerYcbcrConversion &samplerYcbcrConversion = iter.second;
samplerYcbcrConversion.destroy(device);
renderer->onDeallocateHandle(vk::HandleType::SamplerYcbcrConversion);
}
for (auto &iter : mVkFormatPayload)
{
vk::SamplerYcbcrConversion &samplerYcbcrConversion = iter.second;
samplerYcbcrConversion.destroy(device);
renderer->onDeallocateHandle(vk::HandleType::SamplerYcbcrConversion);
}
mExternalFormatPayload.clear();
mVkFormatPayload.clear();
}
angle::Result SamplerYcbcrConversionCache::getSamplerYcbcrConversion(
vk::Context *context,
const vk::YcbcrConversionDesc &ycbcrConversionDesc,
VkSamplerYcbcrConversion *vkSamplerYcbcrConversionOut)
{
ASSERT(ycbcrConversionDesc.valid());
ASSERT(vkSamplerYcbcrConversionOut);
SamplerYcbcrConversionMap &payload =
(ycbcrConversionDesc.getExternalFormat() != 0) ? mExternalFormatPayload : mVkFormatPayload;
const auto iter = payload.find(ycbcrConversionDesc);
if (iter != payload.end())
{
vk::SamplerYcbcrConversion &samplerYcbcrConversion = iter->second;
mCacheStats.hit();
*vkSamplerYcbcrConversionOut = samplerYcbcrConversion.getHandle();
return angle::Result::Continue;
}
mCacheStats.missAndIncrementSize();
// Create the VkSamplerYcbcrConversion
vk::SamplerYcbcrConversion wrappedSamplerYcbcrConversion;
ANGLE_TRY(ycbcrConversionDesc.init(context, &wrappedSamplerYcbcrConversion));
auto insertedItem = payload.emplace(
ycbcrConversionDesc, vk::SamplerYcbcrConversion(std::move(wrappedSamplerYcbcrConversion)));
vk::SamplerYcbcrConversion &insertedSamplerYcbcrConversion = insertedItem.first->second;
*vkSamplerYcbcrConversionOut = insertedSamplerYcbcrConversion.getHandle();
context->getRenderer()->onAllocateHandle(vk::HandleType::SamplerYcbcrConversion);
return angle::Result::Continue;
}
// SamplerCache implementation.
SamplerCache::SamplerCache() = default;
SamplerCache::~SamplerCache()
{
ASSERT(mPayload.empty());
}
void SamplerCache::destroy(vk::Renderer *renderer)
{
renderer->accumulateCacheStats(VulkanCacheType::Sampler, mCacheStats);
VkDevice device = renderer->getDevice();
for (auto &iter : mPayload)
{
vk::RefCountedSampler &sampler = iter.second;
ASSERT(!sampler.isReferenced());
sampler.get().get().destroy(device);
renderer->onDeallocateHandle(vk::HandleType::Sampler);
}
mPayload.clear();
}
angle::Result SamplerCache::getSampler(ContextVk *contextVk,
const vk::SamplerDesc &desc,
vk::SamplerBinding *samplerOut)
{
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::RefCountedSampler &sampler = iter->second;
samplerOut->set(&sampler);
mCacheStats.hit();
return angle::Result::Continue;
}
mCacheStats.missAndIncrementSize();
vk::SamplerHelper samplerHelper(contextVk);
ANGLE_TRY(desc.init(contextVk, &samplerHelper.get()));
vk::RefCountedSampler newSampler(std::move(samplerHelper));
auto insertedItem = mPayload.emplace(desc, std::move(newSampler));
vk::RefCountedSampler &insertedSampler = insertedItem.first->second;
samplerOut->set(&insertedSampler);
contextVk->getRenderer()->onAllocateHandle(vk::HandleType::Sampler);
return angle::Result::Continue;
}
} // namespace rx