blob: 2e0145963184367e580be010ec639d0132cb3003 [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/vulkan/vk_google_filtering_precision.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/RendererVk.h"
#include "libANGLE/renderer/vulkan/VertexArrayVk.h"
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
#include <type_traits>
namespace rx
{
namespace vk
{
namespace
{
uint8_t PackGLBlendOp(GLenum blendOp)
{
switch (blendOp)
{
case GL_FUNC_ADD:
return static_cast<uint8_t>(VK_BLEND_OP_ADD);
case GL_FUNC_SUBTRACT:
return static_cast<uint8_t>(VK_BLEND_OP_SUBTRACT);
case GL_FUNC_REVERSE_SUBTRACT:
return static_cast<uint8_t>(VK_BLEND_OP_REVERSE_SUBTRACT);
case GL_MIN:
return static_cast<uint8_t>(VK_BLEND_OP_MIN);
case GL_MAX:
return static_cast<uint8_t>(VK_BLEND_OP_MAX);
default:
UNREACHABLE();
return 0;
}
}
uint8_t PackGLBlendFactor(GLenum blendFactor)
{
switch (blendFactor)
{
case GL_ZERO:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ZERO);
case GL_ONE:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE);
case GL_SRC_COLOR:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_COLOR);
case GL_DST_COLOR:
return static_cast<uint8_t>(VK_BLEND_FACTOR_DST_COLOR);
case GL_ONE_MINUS_SRC_COLOR:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR);
case GL_SRC_ALPHA:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_ALPHA);
case GL_ONE_MINUS_SRC_ALPHA:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
case GL_DST_ALPHA:
return static_cast<uint8_t>(VK_BLEND_FACTOR_DST_ALPHA);
case GL_ONE_MINUS_DST_ALPHA:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA);
case GL_ONE_MINUS_DST_COLOR:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR);
case GL_SRC_ALPHA_SATURATE:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC_ALPHA_SATURATE);
case GL_CONSTANT_COLOR:
return static_cast<uint8_t>(VK_BLEND_FACTOR_CONSTANT_COLOR);
case GL_CONSTANT_ALPHA:
return static_cast<uint8_t>(VK_BLEND_FACTOR_CONSTANT_ALPHA);
case GL_ONE_MINUS_CONSTANT_COLOR:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR);
case GL_ONE_MINUS_CONSTANT_ALPHA:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA);
case GL_SRC1_COLOR_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_COLOR);
case GL_SRC1_ALPHA_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_ALPHA);
case GL_ONE_MINUS_SRC1_COLOR_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR);
case GL_ONE_MINUS_SRC1_ALPHA_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA);
default:
UNREACHABLE();
return 0;
}
}
VkStencilOp PackGLStencilOp(GLenum compareOp)
{
switch (compareOp)
{
case GL_KEEP:
return VK_STENCIL_OP_KEEP;
case GL_ZERO:
return VK_STENCIL_OP_ZERO;
case GL_REPLACE:
return VK_STENCIL_OP_REPLACE;
case GL_INCR:
return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case GL_DECR:
return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case GL_INCR_WRAP:
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
case GL_DECR_WRAP:
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
case GL_INVERT:
return VK_STENCIL_OP_INVERT;
default:
UNREACHABLE();
return VK_STENCIL_OP_KEEP;
}
}
VkCompareOp PackGLCompareFunc(GLenum compareFunc)
{
switch (compareFunc)
{
case GL_NEVER:
return VK_COMPARE_OP_NEVER;
case GL_ALWAYS:
return VK_COMPARE_OP_ALWAYS;
case GL_LESS:
return VK_COMPARE_OP_LESS;
case GL_LEQUAL:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case GL_EQUAL:
return VK_COMPARE_OP_EQUAL;
case GL_GREATER:
return VK_COMPARE_OP_GREATER;
case GL_GEQUAL:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case GL_NOTEQUAL:
return VK_COMPARE_OP_NOT_EQUAL;
default:
UNREACHABLE();
return VK_COMPARE_OP_NEVER;
}
}
void UnpackAttachmentDesc(VkAttachmentDescription *desc,
const Format &format,
uint8_t samples,
const PackedAttachmentOpsDesc &ops)
{
desc->flags = 0;
desc->format = format.actualImageVkFormat();
desc->samples = gl_vk::GetSamples(samples);
desc->loadOp = static_cast<VkAttachmentLoadOp>(ops.loadOp);
desc->storeOp =
ConvertRenderPassStoreOpToVkStoreOp(static_cast<RenderPassStoreOp>(ops.storeOp));
desc->stencilLoadOp = static_cast<VkAttachmentLoadOp>(ops.stencilLoadOp);
desc->stencilStoreOp =
ConvertRenderPassStoreOpToVkStoreOp(static_cast<RenderPassStoreOp>(ops.stencilStoreOp));
desc->initialLayout =
ConvertImageLayoutToVkImageLayout(static_cast<ImageLayout>(ops.initialLayout));
desc->finalLayout =
ConvertImageLayoutToVkImageLayout(static_cast<ImageLayout>(ops.finalLayout));
}
void UnpackColorResolveAttachmentDesc(VkAttachmentDescription *desc,
const Format &format,
bool usedAsInputAttachment,
bool isInvalidated)
{
desc->flags = 0;
desc->format = format.actualImageVkFormat();
// This function is for color resolve attachments.
const angle::Format &angleFormat = format.actualImageFormat();
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 =
usedAsInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->storeOp = 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(VkAttachmentDescription *desc,
const Format &format,
bool usedAsDepthInputAttachment,
bool usedAsStencilInputAttachment,
bool isDepthInvalidated,
bool isStencilInvalidated)
{
// There cannot be simultaneous usages of the depth/stencil resolve image, as depth/stencil
// resolve currently only comes from depth/stencil renderbuffers.
desc->flags = 0;
desc->format = format.actualImageVkFormat();
// This function is for depth/stencil resolve attachment.
const angle::Format &angleFormat = format.intendedFormat();
ASSERT(angleFormat.depthBits != 0 || angleFormat.stencilBits != 0);
// Missing aspects are folded in is*Invalidated parameters, so no need to double check.
ASSERT(angleFormat.depthBits > 0 || isDepthInvalidated);
ASSERT(angleFormat.stencilBits > 0 || isStencilInvalidated);
// 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;
desc->loadOp =
usedAsDepthInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->storeOp =
isDepthInvalidated ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
desc->stencilLoadOp =
usedAsStencilInputAttachment ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc->stencilStoreOp =
isStencilInvalidated ? 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 UnpackStencilState(const PackedStencilOpState &packedState,
uint8_t stencilReference,
VkStencilOpState *stateOut)
{
stateOut->failOp = static_cast<VkStencilOp>(packedState.ops.fail);
stateOut->passOp = static_cast<VkStencilOp>(packedState.ops.pass);
stateOut->depthFailOp = static_cast<VkStencilOp>(packedState.ops.depthFail);
stateOut->compareOp = static_cast<VkCompareOp>(packedState.ops.compare);
stateOut->compareMask = packedState.compareMask;
stateOut->writeMask = packedState.writeMask;
stateOut->reference = stencilReference;
}
void UnpackBlendAttachmentState(const PackedColorBlendAttachmentState &packedState,
VkPipelineColorBlendAttachmentState *stateOut)
{
stateOut->srcColorBlendFactor = static_cast<VkBlendFactor>(packedState.srcColorBlendFactor);
stateOut->dstColorBlendFactor = static_cast<VkBlendFactor>(packedState.dstColorBlendFactor);
stateOut->colorBlendOp = static_cast<VkBlendOp>(packedState.colorBlendOp);
stateOut->srcAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.srcAlphaBlendFactor);
stateOut->dstAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.dstAlphaBlendFactor);
stateOut->alphaBlendOp = static_cast<VkBlendOp>(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<VkAttachmentReference> &drawSubpassColorAttachmentRefs,
const gl::DrawBuffersVector<VkAttachmentReference> &drawSubpassResolveAttachmentRefs,
const VkAttachmentReference &depthStencilAttachmentRef,
const VkAttachmentReference2KHR &depthStencilResolveAttachmentRef,
gl::DrawBuffersVector<VkAttachmentReference> *unresolveColorAttachmentRefs,
VkAttachmentReference *unresolveDepthStencilAttachmentRef,
FramebufferAttachmentsVector<VkAttachmentReference> *unresolveInputAttachmentRefs,
FramebufferAttachmentsVector<uint32_t> *unresolvePreserveAttachmentRefs,
VkSubpassDescription *subpassDesc)
{
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
// 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:
//
// Subpass[0] Input[2] -> RP Attachment[9] = Subpass[1] Depth/Stencil Resolve
// Subpass[0] Color[2] -> 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.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;
}
if (desc.hasDepthStencilUnresolveAttachment())
{
ASSERT(desc.hasDepthStencilAttachment());
ASSERT(desc.hasDepthStencilResolveAttachment());
*unresolveDepthStencilAttachmentRef = depthStencilAttachmentRef;
VkAttachmentReference unresolveDepthStencilInputAttachmentRef = {};
unresolveDepthStencilInputAttachmentRef.attachment =
depthStencilResolveAttachmentRef.attachment;
unresolveDepthStencilInputAttachmentRef.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
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);
}
ASSERT(!unresolveColorAttachmentRefs->empty() ||
unresolveDepthStencilAttachmentRef->attachment != VK_ATTACHMENT_UNUSED);
ASSERT(unresolveColorAttachmentRefs->size() +
(desc.hasDepthStencilUnresolveAttachment() ? 1 : 0) ==
unresolveInputAttachmentRefs->size());
subpassDesc->flags = 0;
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->pResolveAttachments = nullptr;
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<VkSubpassDescription> &subpassDesc,
bool unresolveColor,
bool unresolveDepthStencil,
std::vector<VkSubpassDependency> *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->emplace_back();
VkSubpassDependency *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->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->emplace_back();
dependency = &subpassDependencies->back();
// Note again that depth/stencil resolve is considered to be done in the color output stage.
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;
}
void InitializeInputAttachmentSubpassDependencies(
std::vector<VkSubpassDependency> *subpassDependencies,
uint32_t subpassIndex)
{
subpassDependencies->emplace_back();
VkSubpassDependency *dependency = &subpassDependencies->back();
dependency->srcSubpass = subpassIndex;
dependency->dstSubpass = subpassIndex;
dependency->srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency->dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency->srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency->dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
dependency->dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
}
void ToAttachmentDesciption2(const VkAttachmentDescription &desc,
VkAttachmentDescription2KHR *desc2Out)
{
*desc2Out = {};
desc2Out->sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
desc2Out->flags = desc.flags;
desc2Out->format = desc.format;
desc2Out->samples = desc.samples;
desc2Out->loadOp = desc.loadOp;
desc2Out->storeOp = desc.storeOp;
desc2Out->stencilLoadOp = desc.stencilLoadOp;
desc2Out->stencilStoreOp = desc.stencilStoreOp;
desc2Out->initialLayout = desc.initialLayout;
desc2Out->finalLayout = desc.finalLayout;
}
void ToAttachmentReference2(const VkAttachmentReference &ref,
VkImageAspectFlags aspectMask,
VkAttachmentReference2KHR *ref2Out)
{
*ref2Out = {};
ref2Out->sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
ref2Out->attachment = ref.attachment;
ref2Out->layout = ref.layout;
ref2Out->aspectMask = aspectMask;
}
void ToSubpassDescription2(const VkSubpassDescription &desc,
const FramebufferAttachmentsVector<VkAttachmentReference2KHR> &inputRefs,
const gl::DrawBuffersVector<VkAttachmentReference2KHR> &colorRefs,
const gl::DrawBuffersVector<VkAttachmentReference2KHR> &resolveRefs,
const VkAttachmentReference2KHR &depthStencilRef,
uint32_t viewMask,
VkSubpassDescription2KHR *desc2Out)
{
*desc2Out = {};
desc2Out->sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
desc2Out->flags = desc.flags;
desc2Out->pipelineBindPoint = desc.pipelineBindPoint;
desc2Out->viewMask = viewMask;
desc2Out->inputAttachmentCount = static_cast<uint32_t>(inputRefs.size());
desc2Out->pInputAttachments = !inputRefs.empty() ? inputRefs.data() : nullptr;
desc2Out->colorAttachmentCount = static_cast<uint32_t>(colorRefs.size());
desc2Out->pColorAttachments = !colorRefs.empty() ? colorRefs.data() : nullptr;
desc2Out->pResolveAttachments = !resolveRefs.empty() ? resolveRefs.data() : nullptr;
desc2Out->pDepthStencilAttachment = desc.pDepthStencilAttachment ? &depthStencilRef : nullptr;
desc2Out->preserveAttachmentCount = desc.preserveAttachmentCount;
desc2Out->pPreserveAttachments = desc.pPreserveAttachments;
}
void ToSubpassDependency2(const VkSubpassDependency &dep, VkSubpassDependency2KHR *dep2Out)
{
*dep2Out = {};
dep2Out->sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR;
dep2Out->srcSubpass = dep.srcSubpass;
dep2Out->dstSubpass = dep.dstSubpass;
dep2Out->srcStageMask = dep.srcStageMask;
dep2Out->dstStageMask = dep.dstStageMask;
dep2Out->srcAccessMask = dep.srcAccessMask;
dep2Out->dstAccessMask = dep.dstAccessMask;
dep2Out->dependencyFlags = dep.dependencyFlags;
}
angle::Result CreateRenderPass2(Context *context,
const VkRenderPassCreateInfo &createInfo,
const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve,
const VkRenderPassMultiviewCreateInfo &multiviewInfo,
bool unresolveDepth,
bool unresolveStencil,
bool isRenderToTexture,
uint8_t renderToTextureSamples,
RenderPass *renderPass)
{
// Convert the attachments to VkAttachmentDescription2.
FramebufferAttachmentArray<VkAttachmentDescription2KHR> attachmentDescs;
for (uint32_t index = 0; index < createInfo.attachmentCount; ++index)
{
ToAttachmentDesciption2(createInfo.pAttachments[index], &attachmentDescs[index]);
}
// Convert subpass attachments to VkAttachmentReference2 and the subpass description to
// VkSubpassDescription2.
SubpassVector<FramebufferAttachmentsVector<VkAttachmentReference2KHR>>
subpassInputAttachmentRefs(createInfo.subpassCount);
SubpassVector<gl::DrawBuffersVector<VkAttachmentReference2KHR>> subpassColorAttachmentRefs(
createInfo.subpassCount);
SubpassVector<gl::DrawBuffersVector<VkAttachmentReference2KHR>> subpassResolveAttachmentRefs(
createInfo.subpassCount);
SubpassVector<VkAttachmentReference2KHR> subpassDepthStencilAttachmentRefs(
createInfo.subpassCount);
SubpassVector<VkSubpassDescription2KHR> subpassDescriptions(createInfo.subpassCount);
for (uint32_t subpass = 0; subpass < createInfo.subpassCount; ++subpass)
{
const VkSubpassDescription &desc = createInfo.pSubpasses[subpass];
FramebufferAttachmentsVector<VkAttachmentReference2KHR> &inputRefs =
subpassInputAttachmentRefs[subpass];
gl::DrawBuffersVector<VkAttachmentReference2KHR> &colorRefs =
subpassColorAttachmentRefs[subpass];
gl::DrawBuffersVector<VkAttachmentReference2KHR> &resolveRefs =
subpassResolveAttachmentRefs[subpass];
VkAttachmentReference2KHR &depthStencilRef = subpassDepthStencilAttachmentRefs[subpass];
inputRefs.resize(desc.inputAttachmentCount);
colorRefs.resize(desc.colorAttachmentCount);
// Convert subpass attachment references.
for (uint32_t index = 0; index < desc.inputAttachmentCount; ++index)
{
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
if (index >= desc.colorAttachmentCount)
{
// Set the aspect of the depth/stencil input attachment (of which there can be only
// one).
ASSERT(index + 1 == desc.inputAttachmentCount);
aspectMask = 0;
if (unresolveDepth)
{
aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (unresolveStencil)
{
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
ASSERT(aspectMask != 0);
}
ToAttachmentReference2(desc.pInputAttachments[index], aspectMask, &inputRefs[index]);
}
for (uint32_t index = 0; index < desc.colorAttachmentCount; ++index)
{
ToAttachmentReference2(desc.pColorAttachments[index], VK_IMAGE_ASPECT_COLOR_BIT,
&colorRefs[index]);
}
if (desc.pResolveAttachments)
{
resolveRefs.resize(desc.colorAttachmentCount);
for (uint32_t index = 0; index < desc.colorAttachmentCount; ++index)
{
ToAttachmentReference2(desc.pResolveAttachments[index], VK_IMAGE_ASPECT_COLOR_BIT,
&resolveRefs[index]);
}
}
if (desc.pDepthStencilAttachment)
{
ToAttachmentReference2(*desc.pDepthStencilAttachment,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
&depthStencilRef);
}
// Convert subpass itself.
ToSubpassDescription2(desc, inputRefs, colorRefs, resolveRefs, depthStencilRef,
multiviewInfo.pViewMasks[subpass], &subpassDescriptions[subpass]);
}
VkMultisampledRenderToSingleSampledInfoEXT renderToTextureInfo = {};
renderToTextureInfo.sType = VK_STRUCTURE_TYPE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_INFO_EXT;
renderToTextureInfo.multisampledRenderToSingleSampledEnable = true;
renderToTextureInfo.rasterizationSamples = gl_vk::GetSamples(renderToTextureSamples);
renderToTextureInfo.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
renderToTextureInfo.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
// Append the depth/stencil resolve attachment to the pNext chain of last subpass, if any.
if (depthStencilResolve.pDepthStencilResolveAttachment != nullptr)
{
ASSERT(!isRenderToTexture);
subpassDescriptions.back().pNext = &depthStencilResolve;
}
else
{
RendererVk *renderer = context->getRenderer();
ASSERT(isRenderToTexture);
ASSERT(renderer->getFeatures().supportsMultisampledRenderToSingleSampled.enabled);
ASSERT(subpassDescriptions.size() == 1);
subpassDescriptions.back().pNext = &renderToTextureInfo;
}
// Convert subpass dependencies to VkSubpassDependency2.
std::vector<VkSubpassDependency2KHR> subpassDependencies(createInfo.dependencyCount);
for (uint32_t index = 0; index < createInfo.dependencyCount; ++index)
{
ToSubpassDependency2(createInfo.pDependencies[index], &subpassDependencies[index]);
}
// Convert CreateInfo itself
VkRenderPassCreateInfo2KHR createInfo2 = {};
createInfo2.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
createInfo2.flags = createInfo.flags;
createInfo2.attachmentCount = createInfo.attachmentCount;
createInfo2.pAttachments = attachmentDescs.data();
createInfo2.subpassCount = createInfo.subpassCount;
createInfo2.pSubpasses = subpassDescriptions.data();
createInfo2.dependencyCount = static_cast<uint32_t>(subpassDependencies.size());
createInfo2.pDependencies = !subpassDependencies.empty() ? subpassDependencies.data() : nullptr;
createInfo2.correlatedViewMaskCount = multiviewInfo.correlationMaskCount;
createInfo2.pCorrelatedViewMasks = multiviewInfo.pCorrelationMasks;
// Initialize the render pass.
ANGLE_VK_TRY(context, renderPass->init2(context->getDevice(), createInfo2));
return angle::Result::Continue;
}
void UpdateRenderPassColorPerfCounters(const VkRenderPassCreateInfo &createInfo,
const VkSubpassDescription &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 VkRenderPassCreateInfo &createInfo,
size_t renderPassIndex,
RenderPassPerfCounters *countersOut)
{
ASSERT(renderPassIndex != VK_ATTACHMENT_UNUSED);
// Depth/stencil ops counters.
const VkAttachmentDescription &ds = createInfo.pAttachments[renderPassIndex];
countersOut->depthClears += ds.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->depthLoads += ds.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->depthStores +=
ds.storeOp == static_cast<uint16_t>(RenderPassStoreOp::Store) ? 1 : 0;
countersOut->stencilClears += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->stencilLoads += ds.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->stencilStores +=
ds.stencilStoreOp == static_cast<uint16_t>(RenderPassStoreOp::Store) ? 1 : 0;
// Depth/stencil read-only mode.
countersOut->readOnlyDepthStencil +=
ds.finalLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL ? 1 : 0;
}
void UpdateRenderPassDepthStencilResolvePerfCounters(
const VkRenderPassCreateInfo &createInfo,
const VkSubpassDescriptionDepthStencilResolve &depthStencilResolve,
RenderPassPerfCounters *countersOut)
{
if (depthStencilResolve.pDepthStencilResolveAttachment == nullptr)
{
return;
}
uint32_t resolveRenderPassIndex =
depthStencilResolve.pDepthStencilResolveAttachment->attachment;
if (resolveRenderPassIndex == VK_ATTACHMENT_UNUSED)
{
return;
}
const VkAttachmentDescription &dsResolve = createInfo.pAttachments[resolveRenderPassIndex];
// Resolve depth/stencil ops counters.
countersOut->depthClears += dsResolve.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->depthLoads += dsResolve.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->depthStores +=
dsResolve.storeOp == static_cast<uint16_t>(RenderPassStoreOp::Store) ? 1 : 0;
countersOut->stencilClears += dsResolve.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? 1 : 0;
countersOut->stencilLoads += dsResolve.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD ? 1 : 0;
countersOut->stencilStores +=
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 VkRenderPassCreateInfo &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 VkSubpassDescription &subpass = createInfo.pSubpasses[subpassIndex];
// Color counters. Note: currently there are no counters for load/store ops of color
// attachments, so there's no risk of double counting.
UpdateRenderPassColorPerfCounters(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);
}
}
}
// 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;
}
angle::Result InitializeRenderPassFromDesc(ContextVk *contextVk,
const RenderPassDesc &desc,
const AttachmentOpsArray &ops,
RenderPassHelper *renderPassHelper)
{
RendererVk *renderer = contextVk->getRenderer();
constexpr VkAttachmentReference kUnusedAttachment = {VK_ATTACHMENT_UNUSED,
VK_IMAGE_LAYOUT_UNDEFINED};
constexpr VkAttachmentReference2 kUnusedAttachment2 = {
VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR, nullptr, VK_ATTACHMENT_UNUSED,
VK_IMAGE_LAYOUT_UNDEFINED, 0};
const bool needInputAttachments = desc.getFramebufferFetchMode();
const bool isRenderToTexture = desc.isRenderToTexture();
const uint8_t descSamples = desc.samples();
const uint8_t attachmentSamples = isRenderToTexture ? 1 : descSamples;
const uint8_t renderToTextureSamples = isRenderToTexture ? descSamples : 1;
// Unpack the packed and split representation into the format required by Vulkan.
gl::DrawBuffersVector<VkAttachmentReference> colorAttachmentRefs;
gl::DrawBuffersVector<VkAttachmentReference> colorResolveAttachmentRefs;
VkAttachmentReference depthStencilAttachmentRef = kUnusedAttachment;
VkAttachmentReference2KHR depthStencilResolveAttachmentRef = kUnusedAttachment2;
// The list of attachments includes all non-resolve and resolve attachments.
FramebufferAttachmentArray<VkAttachmentDescription> 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 isColorInvalidated;
bool isDepthInvalidated = false;
bool isStencilInvalidated = false;
const bool hasUnresolveAttachments =
desc.getColorUnresolveAttachmentMask().any() || desc.hasDepthStencilUnresolveAttachment();
const bool canRemoveResolveAttachments = !hasUnresolveAttachments;
// Pack color attachments
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 formatID = desc[colorIndexGL];
ASSERT(formatID != angle::FormatID::NONE);
const Format &format = renderer->getFormat(formatID);
VkAttachmentReference colorRef;
colorRef.attachment = attachmentCount.get();
colorRef.layout = needInputAttachments
? VK_IMAGE_LAYOUT_GENERAL
: ConvertImageLayoutToVkImageLayout(
static_cast<ImageLayout>(ops[attachmentCount].initialLayout));
colorAttachmentRefs.push_back(colorRef);
UnpackAttachmentDesc(&attachmentDescs[attachmentCount.get()], format, attachmentSamples,
ops[attachmentCount]);
angle::FormatID attachmentFormat = format.actualImageFormatID;
// 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(attachmentFormat);
if (linearFormat != angle::FormatID::NONE)
{
if (desc.getSRGBWriteControlMode() == gl::SrgbWriteControlMode::Linear)
{
attachmentFormat = linearFormat;
}
}
attachmentDescs[attachmentCount.get()].format =
contextVk->getRenderer()->getFormat(attachmentFormat).actualImageVkFormat();
ASSERT(attachmentDescs[attachmentCount.get()].format != VK_FORMAT_UNDEFINED);
isColorInvalidated.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 formatID = desc[depthStencilIndexGL];
ASSERT(formatID != angle::FormatID::NONE);
const Format &format = renderer->getFormat(formatID);
depthStencilAttachmentRef.attachment = attachmentCount.get();
depthStencilAttachmentRef.layout = ConvertImageLayoutToVkImageLayout(
static_cast<ImageLayout>(ops[attachmentCount].initialLayout));
UnpackAttachmentDesc(&attachmentDescs[attachmentCount.get()], format, attachmentSamples,
ops[attachmentCount]);
isDepthInvalidated = ops[attachmentCount].isInvalidated;
isStencilInvalidated = ops[attachmentCount].isStencilInvalidated;
++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));
const Format &format = renderer->getFormat(desc[colorIndexGL]);
VkAttachmentReference colorRef;
colorRef.attachment = attachmentCount.get();
colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
// If color attachment is invalidated, try to remove its resolve attachment altogether.
if (canRemoveResolveAttachments && isColorInvalidated.test(colorIndexGL))
{
colorResolveAttachmentRefs.push_back(kUnusedAttachment);
}
else
{
colorResolveAttachmentRefs.push_back(colorRef);
}
UnpackColorResolveAttachmentDesc(&attachmentDescs[attachmentCount.get()], format,
desc.hasColorUnresolveAttachment(colorIndexGL),
isColorInvalidated.test(colorIndexGL));
++attachmentCount;
}
// Pack depth/stencil resolve attachment, if any
if (desc.hasDepthStencilResolveAttachment())
{
ASSERT(desc.hasDepthStencilAttachment());
uint32_t depthStencilIndexGL = static_cast<uint32_t>(desc.depthStencilAttachmentIndex());
const Format &format = renderer->getFormat(desc[depthStencilIndexGL]);
const angle::Format &angleFormat = format.intendedFormat();
// Treat missing aspect as invalidated for the purpose of the resolve attachment.
if (angleFormat.depthBits == 0)
{
isDepthInvalidated = true;
}
if (angleFormat.stencilBits == 0)
{
isStencilInvalidated = true;
}
depthStencilResolveAttachmentRef.attachment = attachmentCount.get();
depthStencilResolveAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthStencilResolveAttachmentRef.aspectMask = 0;
if (!isDepthInvalidated)
{
depthStencilResolveAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (!isStencilInvalidated)
{
depthStencilResolveAttachmentRef.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
UnpackDepthStencilResolveAttachmentDesc(
&attachmentDescs[attachmentCount.get()], format, desc.hasDepthUnresolveAttachment(),
desc.hasStencilUnresolveAttachment(), isDepthInvalidated, isStencilInvalidated);
++attachmentCount;
}
SubpassVector<VkSubpassDescription> 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 VkSubpassDescription in subpassDesc,
// which is in turn used in VkRenderPassCreateInfo below. That is why they are declared in the
// same scope.
gl::DrawBuffersVector<VkAttachmentReference> unresolveColorAttachmentRefs;
VkAttachmentReference unresolveDepthStencilAttachmentRef = kUnusedAttachment;
FramebufferAttachmentsVector<VkAttachmentReference> unresolveInputAttachmentRefs;
FramebufferAttachmentsVector<uint32_t> unresolvePreserveAttachmentRefs;
if (hasUnresolveAttachments)
{
subpassDesc.push_back({});
InitializeUnresolveSubpass(
desc, colorAttachmentRefs, colorResolveAttachmentRefs, depthStencilAttachmentRef,
depthStencilResolveAttachmentRef, &unresolveColorAttachmentRefs,
&unresolveDepthStencilAttachmentRef, &unresolveInputAttachmentRefs,
&unresolvePreserveAttachmentRefs, &subpassDesc.back());
}
subpassDesc.push_back({});
VkSubpassDescription *applicationSubpass = &subpassDesc.back();
applicationSubpass->flags = 0;
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);
applicationSubpass->preserveAttachmentCount = 0;
applicationSubpass->pPreserveAttachments = nullptr;
// If depth/stencil is to be resolved, add a VkSubpassDescriptionDepthStencilResolve to the
// pNext chain of the subpass description. Note that we need a VkSubpassDescription2KHR to have
// a pNext pointer. CreateRenderPass2 is called to convert the data structures here to those
// specified by VK_KHR_create_renderpass2 for this purpose.
VkSubpassDescriptionDepthStencilResolve depthStencilResolve = {};
if (desc.hasDepthStencilResolveAttachment())
{
depthStencilResolve.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE;
depthStencilResolve.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
depthStencilResolve.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
// If depth/stencil attachment is invalidated, try to remove its resolve attachment
// altogether.
const bool removeDepthStencilResolve =
canRemoveResolveAttachments && isDepthInvalidated && isStencilInvalidated;
if (!removeDepthStencilResolve)
{
depthStencilResolve.pDepthStencilResolveAttachment = &depthStencilResolveAttachmentRef;
}
}
std::vector<VkSubpassDependency> subpassDependencies;
if (hasUnresolveAttachments)
{
InitializeUnresolveSubpassDependencies(
subpassDesc, desc.getColorUnresolveAttachmentMask().any(),
desc.hasDepthStencilUnresolveAttachment(), &subpassDependencies);
}
if (needInputAttachments)
{
uint32_t drawSubpassIndex = static_cast<uint32_t>(subpassDesc.size()) - 1;
InitializeInputAttachmentSubpassDependencies(&subpassDependencies, drawSubpassIndex);
}
VkRenderPassCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
createInfo.flags = 0;
createInfo.attachmentCount = attachmentCount.get();
createInfo.pAttachments = attachmentDescs.data();
createInfo.subpassCount = static_cast<uint32_t>(subpassDesc.size());
createInfo.pSubpasses = subpassDesc.data();
createInfo.dependencyCount = 0;
createInfo.pDependencies = nullptr;
if (!subpassDependencies.empty())
{
createInfo.dependencyCount = static_cast<uint32_t>(subpassDependencies.size());
createInfo.pDependencies = subpassDependencies.data();
}
SubpassVector<uint32_t> viewMasks(subpassDesc.size(),
angle::BitMask<uint32_t>(desc.viewCount()));
VkRenderPassMultiviewCreateInfo multiviewInfo = {};
multiviewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
multiviewInfo.subpassCount = createInfo.subpassCount;
multiviewInfo.pViewMasks = viewMasks.data();
if (desc.viewCount() > 0)
{
// 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 (TODO: verify that extension has no hints), 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.
multiviewInfo.correlationMaskCount = 1;
multiviewInfo.pCorrelationMasks = viewMasks.data();
createInfo.pNext = &multiviewInfo;
}
// If depth/stencil resolve is used, we need to create the render pass with
// vkCreateRenderPass2KHR. Same when using the VK_EXT_multisampled_render_to_single_sampled
// extension.
if (depthStencilResolve.pDepthStencilResolveAttachment != nullptr || desc.isRenderToTexture())
{
ANGLE_TRY(CreateRenderPass2(contextVk, createInfo, depthStencilResolve, multiviewInfo,
desc.hasDepthUnresolveAttachment(),
desc.hasStencilUnresolveAttachment(), desc.isRenderToTexture(),
renderToTextureSamples, &renderPassHelper->getRenderPass()));
}
else
{
ANGLE_VK_TRY(contextVk,
renderPassHelper->getRenderPass().init(contextVk->getDevice(), createInfo));
}
// 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.
UpdateRenderPassPerfCounters(desc, createInfo, depthStencilResolve,
&renderPassHelper->getPerfCounters());
return angle::Result::Continue;
}
void GetRenderPassAndUpdateCounters(ContextVk *contextVk,
bool updatePerfCounters,
RenderPassHelper *renderPassHelper,
RenderPass **renderPassOut)
{
*renderPassOut = &renderPassHelper->getRenderPass();
if (updatePerfCounters)
{
PerfCounters &counters = contextVk->getPerfCounters();
const RenderPassPerfCounters &rpCounters = renderPassHelper->getPerfCounters();
counters.depthClears += rpCounters.depthClears;
counters.depthLoads += rpCounters.depthLoads;
counters.depthStores += rpCounters.depthStores;
counters.stencilClears += rpCounters.stencilClears;
counters.stencilLoads += rpCounters.stencilLoads;
counters.stencilStores += rpCounters.stencilStores;
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::LineRasterEmulation:
(*specializationEntriesOut)[id].offset =
offsetof(SpecializationConstants, lineRasterEmulation);
(*specializationEntriesOut)[id].size = sizeof(specConsts.lineRasterEmulation);
break;
case sh::vk::SpecializationConstantId::SurfaceRotation:
(*specializationEntriesOut)[id].offset =
offsetof(SpecializationConstants, surfaceRotation);
(*specializationEntriesOut)[id].size = sizeof(specConsts.surfaceRotation);
break;
case sh::vk::SpecializationConstantId::DrawableWidth:
(*specializationEntriesOut)[id].offset =
offsetof(vk::SpecializationConstants, drawableWidth);
(*specializationEntriesOut)[id].size = sizeof(specConsts.drawableWidth);
break;
case sh::vk::SpecializationConstantId::DrawableHeight:
(*specializationEntriesOut)[id].offset =
offsetof(vk::SpecializationConstants, drawableHeight);
(*specializationEntriesOut)[id].size = sizeof(specConsts.drawableHeight);
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
// and the offset of 'Field' within 'Member'. 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, Field) \
((offsetof(GraphicsPipelineDesc, Member) + offsetof(decltype(Member), Field)) >> \
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, Field, Index, BitWidth) \
(((BitWidth * Index) >> kTransitionBitShift) + ANGLE_GET_TRANSITION_BIT(Member, Field))
constexpr angle::PackedEnumMap<gl::ComponentType, VkFormat> kMismatchedComponentTypeMap = {{
{gl::ComponentType::Float, VK_FORMAT_R32G32B32A32_SFLOAT},
{gl::ComponentType::Int, VK_FORMAT_R32G32B32A32_SINT},
{gl::ComponentType::UnsignedInt, VK_FORMAT_R32G32B32A32_UINT},
}};
} // anonymous namespace
// 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::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::packDepthStencilResolveAttachment()
{
ASSERT(hasDepthStencilAttachment());
ASSERT(!hasDepthStencilResolveAttachment());
mResolveDepthStencil = true;
}
void RenderPassDesc::packDepthStencilUnresolveAttachment(bool unresolveDepth, bool unresolveStencil)
{
ASSERT(hasDepthStencilAttachment());
mUnresolveDepth = unresolveDepth;
mUnresolveStencil = unresolveStencil;
}
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::attachmentCount() 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;
}
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)
{
memcpy(this, &other, sizeof(GraphicsPipelineDesc));
}
GraphicsPipelineDesc &GraphicsPipelineDesc::operator=(const GraphicsPipelineDesc &other)
{
memcpy(this, &other, sizeof(GraphicsPipelineDesc));
return *this;
}
size_t GraphicsPipelineDesc::hash() const
{
return angle::ComputeGenericHash(*this);
}
bool GraphicsPipelineDesc::operator==(const GraphicsPipelineDesc &other) const
{
return (memcmp(this, &other, sizeof(GraphicsPipelineDesc)) == 0);
}
// TODO(jmadill): We should prefer using Packed GLenums. http://anglebug.com/2169
// Initialize PSO states, it is consistent with initial value of gl::State
void GraphicsPipelineDesc::initDefaults(const ContextVk *contextVk)
{
// Set all vertex input attributes to default, the default format is Float
angle::FormatID defaultFormat = GetCurrentValueFormatID(gl::VertexAttribType::Float);
for (PackedAttribDesc &packedAttrib : mVertexInputAttribs.attribs)
{
SetBitField(packedAttrib.stride, 0);
SetBitField(packedAttrib.divisor, 0);
SetBitField(packedAttrib.format, defaultFormat);
SetBitField(packedAttrib.compressed, 0);
SetBitField(packedAttrib.offset, 0);
}
mRasterizationAndMultisampleStateInfo.bits.subpass = 0;
mRasterizationAndMultisampleStateInfo.bits.depthClampEnable =
contextVk->getFeatures().depthClamping.enabled ? VK_TRUE : VK_FALSE;
mRasterizationAndMultisampleStateInfo.bits.rasterizationDiscardEnable = 0;
SetBitField(mRasterizationAndMultisampleStateInfo.bits.polygonMode, VK_POLYGON_MODE_FILL);
SetBitField(mRasterizationAndMultisampleStateInfo.bits.cullMode, VK_CULL_MODE_BACK_BIT);
SetBitField(mRasterizationAndMultisampleStateInfo.bits.frontFace,
VK_FRONT_FACE_COUNTER_CLOCKWISE);
mRasterizationAndMultisampleStateInfo.bits.depthBiasEnable = 0;
mRasterizationAndMultisampleStateInfo.depthBiasConstantFactor = 0.0f;
mRasterizationAndMultisampleStateInfo.depthBiasClamp = 0.0f;
mRasterizationAndMultisampleStateInfo.depthBiasSlopeFactor = 0.0f;
mRasterizationAndMultisampleStateInfo.lineWidth = 1.0f;
mRasterizationAndMultisampleStateInfo.bits.rasterizationSamples = 1;
mRasterizationAndMultisampleStateInfo.bits.sampleShadingEnable = 0;
mRasterizationAndMultisampleStateInfo.minSampleShading = 1.0f;
for (uint32_t &sampleMask : mRasterizationAndMultisampleStateInfo.sampleMask)
{
sampleMask = 0xFFFFFFFF;
}
mRasterizationAndMultisampleStateInfo.bits.alphaToCoverageEnable = 0;
mRasterizationAndMultisampleStateInfo.bits.alphaToOneEnable = 0;
mDepthStencilStateInfo.enable.depthTest = 0;
mDepthStencilStateInfo.enable.depthWrite = 1;
SetBitField(mDepthStencilStateInfo.depthCompareOpAndSurfaceRotation.depthCompareOp,
VK_COMPARE_OP_LESS);
mDepthStencilStateInfo.enable.depthBoundsTest = 0;
mDepthStencilStateInfo.enable.stencilTest = 0;
mDepthStencilStateInfo.minDepthBounds = 0.0f;
mDepthStencilStateInfo.maxDepthBounds = 0.0f;
SetBitField(mDepthStencilStateInfo.front.ops.fail, VK_STENCIL_OP_KEEP);
SetBitField(mDepthStencilStateInfo.front.ops.pass, VK_STENCIL_OP_KEEP);
SetBitField(mDepthStencilStateInfo.front.ops.depthFail, VK_STENCIL_OP_KEEP);
SetBitField(mDepthStencilStateInfo.front.ops.compare, VK_COMPARE_OP_ALWAYS);
SetBitField(mDepthStencilStateInfo.front.compareMask, 0xFF);
SetBitField(mDepthStencilStateInfo.front.writeMask, 0xFF);
mDepthStencilStateInfo.frontStencilReference = 0;
SetBitField(mDepthStencilStateInfo.back.ops.fail, VK_STENCIL_OP_KEEP);
SetBitField(mDepthStencilStateInfo.back.ops.pass, VK_STENCIL_OP_KEEP);
SetBitField(mDepthStencilStateInfo.back.ops.depthFail, VK_STENCIL_OP_KEEP);
SetBitField(mDepthStencilStateInfo.back.ops.compare, VK_COMPARE_OP_ALWAYS);
SetBitField(mDepthStencilStateInfo.back.compareMask, 0xFF);
SetBitField(mDepthStencilStateInfo.back.writeMask, 0xFF);
mDepthStencilStateInfo.backStencilReference = 0;
mDepthStencilStateInfo.depthCompareOpAndSurfaceRotation.surfaceRotation =
static_cast<uint8_t>(SurfaceRotation::Identity);
PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = mInputAssemblyAndColorBlendStateInfo;
inputAndBlend.logic.opEnable = 0;
inputAndBlend.logic.op = static_cast<uint32_t>(VK_LOGIC_OP_CLEAR);
inputAndBlend.blendEnableMask = 0;
inputAndBlend.blendConstants[0] = 0.0f;
inputAndBlend.blendConstants[1] = 0.0f;
inputAndBlend.blendConstants[2] = 0.0f;
inputAndBlend.blendConstants[3] = 0.0f;
VkFlags allColorBits = (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(inputAndBlend.colorWriteMaskBits, colorIndexGL, allColorBits);
}
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(&inputAndBlend.attachments[0],
&inputAndBlend.attachments[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS],
blendAttachmentState);
SetBitField(inputAndBlend.primitive.topology, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
SetBitField(inputAndBlend.primitive.patchVertices, 3);
inputAndBlend.primitive.restartEnable = 0;
mDrawableSize.width = 1;
mDrawableSize.height = 1;
}
angle::Result GraphicsPipelineDesc::initializePipeline(
ContextVk *contextVk,
const PipelineCache &pipelineCacheVk,
const RenderPass &compatibleRenderPass,
const PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask,
const gl::ComponentTypeMask &programAttribsTypeMask,
const ShaderModule *vertexModule,
const ShaderModule *fragmentModule,
const ShaderModule *geometryModule,
const ShaderModule *tessControlModule,
const ShaderModule *tessEvaluationModule,
const SpecializationConstants &specConsts,
Pipeline *pipelineOut) const
{
angle::FixedVector<VkPipelineShaderStageCreateInfo, 5> shaderStages;
VkPipelineVertexInputStateCreateInfo vertexInputState = {};
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {};
VkPipelineViewportStateCreateInfo viewportState = {};
VkPipelineRasterizationStateCreateInfo rasterState = {};
VkPipelineMultisampleStateCreateInfo multisampleState = {};
VkPipelineDepthStencilStateCreateInfo depthStencilState = {};
gl::DrawBuffersArray<VkPipelineColorBlendAttachmentState> blendAttachmentState;
VkPipelineTessellationStateCreateInfo tessellationState = {};
VkPipelineTessellationDomainOriginStateCreateInfo domainOriginState = {};
VkPipelineColorBlendStateCreateInfo blendState = {};
VkSpecializationInfo specializationInfo = {};
VkGraphicsPipelineCreateInfo createInfo = {};
SpecializationConstantMap<VkSpecializationMapEntry> specializationEntries;
InitializeSpecializationInfo(specConsts, &specializationEntries, &specializationInfo);
// Vertex shader is always expected to be present.
ASSERT(vertexModule != nullptr);
VkPipelineShaderStageCreateInfo vertexStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_VERTEX_BIT, vertexModule->getHandle(),
specializationInfo, &vertexStage);
shaderStages.push_back(vertexStage);
if (tessControlModule)
{
VkPipelineShaderStageCreateInfo tessControlStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
tessControlModule->getHandle(), specializationInfo,
&tessControlStage);
shaderStages.push_back(tessControlStage);
}
if (tessEvaluationModule)
{
VkPipelineShaderStageCreateInfo tessEvaluationStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
tessEvaluationModule->getHandle(), specializationInfo,
&tessEvaluationStage);
shaderStages.push_back(tessEvaluationStage);
}
if (geometryModule)
{
VkPipelineShaderStageCreateInfo geometryStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_GEOMETRY_BIT, geometryModule->getHandle(),
specializationInfo, &geometryStage);
shaderStages.push_back(geometryStage);
}
// Fragment shader is optional.
// anglebug.com/3509 - Don't compile the fragment shader if rasterizationDiscardEnable = true
if (fragmentModule && !mRasterizationAndMultisampleStateInfo.bits.rasterizationDiscardEnable)
{
VkPipelineShaderStageCreateInfo fragmentStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_FRAGMENT_BIT, fragmentModule->getHandle(),
specializationInfo, &fragmentStage);
shaderStages.push_back(fragmentStage);
}
// TODO(jmadill): Possibly use different path for ES 3.1 split bindings/attribs.
gl::AttribArray<VkVertexInputBindingDescription> bindingDescs;
gl::AttribArray<VkVertexInputAttributeDescription> attributeDescs;
uint32_t vertexAttribCount = 0;
size_t unpackedSize = sizeof(shaderStages) + sizeof(vertexInputState) +
sizeof(inputAssemblyState) + sizeof(viewportState) + sizeof(rasterState) +
sizeof(multisampleState) + sizeof(depthStencilState) +
sizeof(tessellationState) + sizeof(blendAttachmentState) +
sizeof(blendState) + sizeof(bindingDescs) + sizeof(attributeDescs);
ANGLE_UNUSED_VARIABLE(unpackedSize);
gl::AttribArray<VkVertexInputBindingDivisorDescriptionEXT> divisorDesc;
VkPipelineVertexInputDivisorStateCreateInfoEXT divisorState = {};
divisorState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
divisorState.pVertexBindingDivisors = divisorDesc.data();
for (size_t attribIndexSizeT : activeAttribLocationsMask)
{
const uint32_t attribIndex = static_cast<uint32_t>(attribIndexSizeT);
VkVertexInputBindingDescription &bindingDesc = bindingDescs[vertexAttribCount];
VkVertexInputAttributeDescription &attribDesc = attributeDescs[vertexAttribCount];
const PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex];
bindingDesc.binding = attribIndex;
bindingDesc.stride = static_cast<uint32_t>(packedAttrib.stride);
if (packedAttrib.divisor != 0)
{
bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_INSTANCE);
divisorDesc[divisorState.vertexBindingDivisorCount].binding = bindingDesc.binding;
divisorDesc[divisorState.vertexBindingDivisorCount].divisor = packedAttrib.divisor;
++divisorState.vertexBindingDivisorCount;
}
else
{
bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_VERTEX);
}
// Get the corresponding VkFormat for the attrib's format.
angle::FormatID formatID = static_cast<angle::FormatID>(packedAttrib.format);
const Format &format = contextVk->getRenderer()->getFormat(formatID);
const angle::Format &angleFormat = format.intendedFormat();
VkFormat vkFormat = format.actualBufferVkFormat(packedAttrib.compressed);
gl::ComponentType attribType =
GetVertexAttributeComponentType(angleFormat.isPureInt(), angleFormat.vertexAttribType);
gl::ComponentType programAttribType =
gl::GetComponentTypeMask(programAttribsTypeMask, attribIndex);
// This forces stride to 0 when glVertexAttribute specifies a different type from the
// program's attribute type except when the type mismatch is a mismatched integer sign.
if (attribType != programAttribType)
{
if (attribType == gl::ComponentType::Float ||
programAttribType == gl::ComponentType::Float)
{
// When dealing with float to int or unsigned int or vice versa, just override the
// format with a compatible one.
vkFormat = kMismatchedComponentTypeMap[programAttribType];
}
else
{
// When converting from an unsigned to a signed format or vice versa, attempt to
// match the bit width.
angle::FormatID convertedFormatID = gl::ConvertFormatSignedness(angleFormat);
const Format &convertedFormat =
contextVk->getRenderer()->getFormat(convertedFormatID);
ASSERT(angleFormat.channelCount == convertedFormat.intendedFormat().channelCount);
ASSERT(angleFormat.redBits == convertedFormat.intendedFormat().redBits);
ASSERT(angleFormat.greenBits == convertedFormat.intendedFormat().greenBits);
ASSERT(angleFormat.blueBits == convertedFormat.intendedFormat().blueBits);
ASSERT(angleFormat.alphaBits == convertedFormat.intendedFormat().alphaBits);
vkFormat = convertedFormat.actualBufferVkFormat(packedAttrib.compressed);
}
GLenum programAttributeType =
contextVk->getState().getProgramExecutable()->getProgramInputs()[attribIndex].type;
GLuint attribSize = gl::GetVertexFormatFromID(formatID).components;
GLuint shaderVarSize =
static_cast<GLuint>(gl::VariableColumnCount(programAttributeType));
ASSERT(contextVk->getNativeExtensions().relaxedVertexAttributeTypeANGLE);
if (programAttribType == gl::ComponentType::Float ||
attribType == gl::ComponentType::Float || attribSize != shaderVarSize)
{
bindingDesc.stride = 0; // Prevent out-of-bounds accesses.
}
}
// The binding index could become more dynamic in ES 3.1.
attribDesc.binding = attribIndex;
attribDesc.format = vkFormat;
attribDesc.location = static_cast<uint32_t>(attribIndex);
attribDesc.offset = packedAttrib.offset;
vertexAttribCount++;
}
// The binding descriptions are filled in at draw time.
vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputState.flags = 0;
vertexInputState.vertexBindingDescriptionCount = vertexAttribCount;
vertexInputState.pVertexBindingDescriptions = bindingDescs.data();
vertexInputState.vertexAttributeDescriptionCount = vertexAttribCount;
vertexInputState.pVertexAttributeDescriptions = attributeDescs.data();
if (divisorState.vertexBindingDivisorCount)
vertexInputState.pNext = &divisorState;
// Primitive topology is filled in at draw time.
inputAssemblyState.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyState.flags = 0;
inputAssemblyState.topology =
static_cast<VkPrimitiveTopology>(mInputAssemblyAndColorBlendStateInfo.primitive.topology);
// http://anglebug.com/3832
// We currently hit a VK Validation here where VUID
// VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00428 is flagged because we allow
// primitiveRestartEnable to be true for topologies VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
// VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
// VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
// VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY and VK_PRIMITIVE_TOPOLOGY_PATCH_LIST
// However if we force primiteRestartEnable to FALSE we fail tests.
// Need to identify alternate fix.
inputAssemblyState.primitiveRestartEnable =
static_cast<VkBool32>(mInputAssemblyAndColorBlendStateInfo.primitive.restartEnable);
// Set initial viewport and scissor state.
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.flags = 0;
viewportState.viewportCount = 1;
viewportState.pViewports = nullptr;
viewportState.scissorCount = 1;
viewportState.pScissors = nullptr;
const PackedRasterizationAndMultisampleStateInfo &rasterAndMS =
mRasterizationAndMultisampleStateInfo;
// Rasterizer state.
rasterState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterState.flags = 0;
rasterState.depthClampEnable = static_cast<VkBool32>(rasterAndMS.bits.depthClampEnable);
rasterState.rasterizerDiscardEnable =
static_cast<VkBool32>(rasterAndMS.bits.rasterizationDiscardEnable);
rasterState.polygonMode = static_cast<VkPolygonMode>(rasterAndMS.bits.polygonMode);
rasterState.cullMode = static_cast<VkCullModeFlags>(rasterAndMS.bits.cullMode);
rasterState.frontFace = static_cast<VkFrontFace>(rasterAndMS.bits.frontFace);
rasterState.depthBiasEnable = static_cast<VkBool32>(rasterAndMS.bits.depthBiasEnable);
rasterState.depthBiasConstantFactor = rasterAndMS.depthBiasConstantFactor;
rasterState.depthBiasClamp = rasterAndMS.depthBiasClamp;
rasterState.depthBiasSlopeFactor = rasterAndMS.depthBiasSlopeFactor;
rasterState.lineWidth = rasterAndMS.lineWidth;
const void **pNextPtr = &rasterState.pNext;
VkPipelineRasterizationLineStateCreateInfoEXT rasterLineState = {};
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 (rasterAndMS.bits.rasterizationSamples <= 1 &&
!rasterAndMS.bits.rasterizationDiscardEnable && !rasterAndMS.bits.alphaToCoverageEnable &&
!rasterAndMS.bits.alphaToOneEnable && !rasterAndMS.bits.sampleShadingEnable &&
contextVk->getFeatures().bresenhamLineRasterization.enabled)
{
rasterLineState.lineRasterizationMode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT;
*pNextPtr = &rasterLineState;
pNextPtr = &rasterLineState.pNext;
}
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexState = {};
provokingVertexState.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT;
// Always set provoking vertex mode to last if available.
if (contextVk->getFeatures().provokingVertex.enabled)
{
provokingVertexState.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;
*pNextPtr = &provokingVertexState;
pNextPtr = &provokingVertexState.pNext;
}
// When depth clamping is used, depth clipping is automatically disabled.
// When the 'depthClamping' feature is enabled, we'll be using depth clamping
// to work around a driver issue, not as an alternative to depth clipping. Therefore we need to
// explicitly re-enable depth clipping.
VkPipelineRasterizationDepthClipStateCreateInfoEXT depthClipState = {};
depthClipState.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT;
if (contextVk->getFeatures().depthClamping.enabled)
{
depthClipState.depthClipEnable = VK_TRUE;
*pNextPtr = &depthClipState;
pNextPtr = &depthClipState.pNext;
}
VkPipelineRasterizationStateStreamCreateInfoEXT rasterStreamState = {};
rasterStreamState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT;
if (contextVk->getFeatures().supportsGeometryStreamsCapability.enabled)
{
rasterStreamState.rasterizationStream = 0;
*pNextPtr = &rasterStreamState;
pNextPtr = &rasterStreamState.pNext;
}
// Multisample state.
multisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleState.flags = 0;
multisampleState.rasterizationSamples =
gl_vk::GetSamples(rasterAndMS.bits.rasterizationSamples);
multisampleState.sampleShadingEnable =
static_cast<VkBool32>(rasterAndMS.bits.sampleShadingEnable);
multisampleState.minSampleShading = rasterAndMS.minSampleShading;
multisampleState.pSampleMask = rasterAndMS.sampleMask;
multisampleState.alphaToCoverageEnable =
static_cast<VkBool32>(rasterAndMS.bits.alphaToCoverageEnable);
multisampleState.alphaToOneEnable = static_cast<VkBool32>(rasterAndMS.bits.alphaToOneEnable);
// Depth/stencil state.
depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilState.flags = 0;
depthStencilState.depthTestEnable =
static_cast<VkBool32>(mDepthStencilStateInfo.enable.depthTest);
depthStencilState.depthWriteEnable =
static_cast<VkBool32>(mDepthStencilStateInfo.enable.depthWrite);
depthStencilState.depthCompareOp = static_cast<VkCompareOp>(
mDepthStencilStateInfo.depthCompareOpAndSurfaceRotation.depthCompareOp);
depthStencilState.depthBoundsTestEnable =
static_cast<VkBool32>(mDepthStencilStateInfo.enable.depthBoundsTest);
depthStencilState.stencilTestEnable =
static_cast<VkBool32>(mDepthStencilStateInfo.enable.stencilTest);
UnpackStencilState(mDepthStencilStateInfo.front, mDepthStencilStateInfo.frontStencilReference,
&depthStencilState.front);
UnpackStencilState(mDepthStencilStateInfo.back, mDepthStencilStateInfo.backStencilReference,
&depthStencilState.back);
depthStencilState.minDepthBounds = mDepthStencilStateInfo.minDepthBounds;
depthStencilState.maxDepthBounds = mDepthStencilStateInfo.maxDepthBounds;
const PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend =
mInputAssemblyAndColorBlendStateInfo;
blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blendState.flags = 0;
blendState.logicOpEnable = static_cast<VkBool32>(inputAndBlend.logic.opEnable);
blendState.logicOp = static_cast<VkLogicOp>(inputAndBlend.logic.op);
blendState.attachmentCount = static_cast<uint32_t>(mRenderPassDesc.colorAttachmentRange());
blendState.pAttachments = blendAttachmentState.data();
// If this graphics pipeline is for the unresolve operation, correct the color attachment count
// for that subpass.
if ((mRenderPassDesc.getColorUnresolveAttachmentMask().any() ||
mRenderPassDesc.hasDepthStencilUnresolveAttachment()) &&
mRasterizationAndMultisampleStateInfo.bits.subpass == 0)
{
blendState.attachmentCount =
static_cast<uint32_t>(mRenderPassDesc.getColorUnresolveAttachmentMask().count());
}
for (int i = 0; i < 4; i++)
{
blendState.blendConstants[i] = inputAndBlend.blendConstants[i];
}
const gl::DrawBufferMask blendEnableMask(inputAndBlend.blendEnableMask);
// Zero-init all states.
blendAttachmentState = {};
for (uint32_t colorIndexGL = 0; colorIndexGL < blendState.attachmentCount; ++colorIndexGL)
{
VkPipelineColorBlendAttachmentState &state = 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(mRenderPassDesc[colorIndexGL]).isInt())
{
ASSERT(!contextVk->getRenderer()
->getFormat(mRenderPassDesc[colorIndexGL])
.actualImageFormat()
.isInt());
state.blendEnable = VK_TRUE;
UnpackBlendAttachmentState(inputAndBlend.attachments[colorIndexGL], &state);
}
}
state.colorWriteMask =
Int4Array_Get<VkColorComponentFlags>(inputAndBlend.colorWriteMaskBits, colorIndexGL);
}
// Dynamic state
angle::FixedVector<VkDynamicState, 2> dynamicStateList;
dynamicStateList.push_back(VK_DYNAMIC_STATE_VIEWPORT);
dynamicStateList.push_back(VK_DYNAMIC_STATE_SCISSOR);
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateList.size());
dynamicState.pDynamicStates = dynamicStateList.data();
// tessellation State
if (tessControlModule && tessEvaluationModule)
{
domainOriginState.sType =
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
domainOriginState.pNext = NULL;
domainOriginState.domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
tessellationState.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessellationState.flags = 0;
tessellationState.pNext = &domainOriginState;
tessellationState.patchControlPoints =
static_cast<uint32_t>(inputAndBlend.primitive.patchVertices);
}
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
createInfo.flags = 0;
createInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
createInfo.pStages = shaderStages.data();
createInfo.pVertexInputState = &vertexInputState;
createInfo.pInputAssemblyState = &inputAssemblyState;
createInfo.pTessellationState = &tessellationState;
createInfo.pViewportState = &viewportState;
createInfo.pRasterizationState = &rasterState;
createInfo.pMultisampleState = &multisampleState;
createInfo.pDepthStencilState = &depthStencilState;
createInfo.pColorBlendState = &blendState;
createInfo.pDynamicState = dynamicStateList.empty() ? nullptr : &dynamicState;
createInfo.layout = pipelineLayout.getHandle();
createInfo.renderPass = compatibleRenderPass.getHandle();
createInfo.subpass = mRasterizationAndMultisampleStateInfo.bits.subpass;
createInfo.basePipelineHandle = VK_NULL_HANDLE;
createInfo.basePipelineIndex = 0;
ANGLE_VK_TRY(contextVk,
pipelineOut->initGraphics(contextVk->getDevice(), createInfo, pipelineCacheVk));
return angle::Result::Continue;
}
void GraphicsPipelineDesc::updateVertexInput(GraphicsPipelineTransitionBits *transition,
uint32_t attribIndex,
GLuint stride,
GLuint divisor,
angle::FormatID format,
bool compressed,
GLuint relativeOffset)
{
PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex];
SetBitField(packedAttrib.stride, stride);
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(mVertexInputAttribs, attribs, attribIndex, kAttribBits);
// Cover the next dirty bit conservatively. Because each attribute is 6 bytes.
transition->set(kBit);
transition->set(kBit + 1);
}
void GraphicsPipelineDesc::updateTopology(GraphicsPipelineTransitionBits *transition,
gl::PrimitiveMode drawMode)
{
VkPrimitiveTopology vkTopology = gl_vk::GetPrimitiveTopology(drawMode);
SetBitField(mInputAssemblyAndColorBlendStateInfo.primitive.topology, vkTopology);
transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, primitive));
}
void GraphicsPipelineDesc::updatePrimitiveRestartEnabled(GraphicsPipelineTransitionBits *transition,
bool primitiveRestartEnabled)
{
mInputAssemblyAndColorBlendStateInfo.primitive.restartEnable =
static_cast<uint16_t>(primitiveRestartEnabled);
transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, primitive));
}
void GraphicsPipelineDesc::setCullMode(VkCullModeFlagBits cullMode)
{
SetBitField(mRasterizationAndMultisampleStateInfo.bits.cullMode, cullMode);
}
void GraphicsPipelineDesc::updateCullMode(GraphicsPipelineTransitionBits *transition,
const gl::RasterizerState &rasterState)
{
setCullMode(gl_vk::GetCullMode(rasterState));
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
void GraphicsPipelineDesc::updateFrontFace(GraphicsPipelineTransitionBits *transition,
const gl::RasterizerState &rasterState,
bool invertFrontFace)
{
mRasterizationAndMultisampleStateInfo.bits.frontFace =
static_cast<uint16_t>(gl_vk::GetFrontFace(rasterState.frontFace, invertFrontFace));
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
void GraphicsPipelineDesc::updateLineWidth(GraphicsPipelineTransitionBits *transition,
float lineWidth)
{
mRasterizationAndMultisampleStateInfo.lineWidth = lineWidth;
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, lineWidth));
}
void GraphicsPipelineDesc::updateRasterizerDiscardEnabled(
GraphicsPipelineTransitionBits *transition,
bool rasterizerDiscardEnabled)
{
mRasterizationAndMultisampleStateInfo.bits.rasterizationDiscardEnable =
static_cast<uint32_t>(rasterizerDiscardEnabled);
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
uint32_t GraphicsPipelineDesc::getRasterizationSamples() const
{
return mRasterizationAndMultisampleStateInfo.bits.rasterizationSamples;
}
void GraphicsPipelineDesc::setRasterizationSamples(uint32_t rasterizationSamples)
{
mRasterizationAndMultisampleStateInfo.bits.rasterizationSamples = rasterizationSamples;
}
void GraphicsPipelineDesc::updateRasterizationSamples(GraphicsPipelineTransitionBits *transition,
uint32_t rasterizationSamples)
{
setRasterizationSamples(rasterizationSamples);
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
void GraphicsPipelineDesc::updateAlphaToCoverageEnable(GraphicsPipelineTransitionBits *transition,
bool enable)
{
mRasterizationAndMultisampleStateInfo.bits.alphaToCoverageEnable = enable;
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
void GraphicsPipelineDesc::updateAlphaToOneEnable(GraphicsPipelineTransitionBits *transition,
bool enable)
{
mRasterizationAndMultisampleStateInfo.bits.alphaToOneEnable = enable;
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
void GraphicsPipelineDesc::updateSampleMask(GraphicsPipelineTransitionBits *transition,
uint32_t maskNumber,
uint32_t mask)
{
ASSERT(maskNumber < gl::MAX_SAMPLE_MASK_WORDS);
mRasterizationAndMultisampleStateInfo.sampleMask[maskNumber] = mask;
constexpr size_t kMaskBits =
sizeof(mRasterizationAndMultisampleStateInfo.sampleMask[0]) * kBitsPerByte;
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo,
sampleMask, maskNumber, kMaskBits));
}
void GraphicsPipelineDesc::updateSampleShading(GraphicsPipelineTransitionBits *transition,
bool enable,
float value)
{
mRasterizationAndMultisampleStateInfo.bits.sampleShadingEnable = enable;
mRasterizationAndMultisampleStateInfo.minSampleShading = (enable ? value : 1.0f);
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
transition->set(
ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, minSampleShading));
}
void GraphicsPipelineDesc::updateBlendColor(GraphicsPipelineTransitionBits *transition,
const gl::ColorF &color)
{
mInputAssemblyAndColorBlendStateInfo.blendConstants[0] = color.red;
mInputAssemblyAndColorBlendStateInfo.blendConstants[1] = color.green;
mInputAssemblyAndColorBlendStateInfo.blendConstants[2] = color.blue;
mInputAssemblyAndColorBlendStateInfo.blendConstants[3] = color.alpha;
constexpr size_t kSize = sizeof(mInputAssemblyAndColorBlendStateInfo.blendConstants[0]) * 8;
for (int index = 0; index < 4; ++index)
{
const size_t kBit = ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo,
blendConstants, index, kSize);
transition->set(kBit);
}
}
void GraphicsPipelineDesc::updateBlendEnabled(GraphicsPipelineTransitionBits *transition,
gl::DrawBufferMask blendEnabledMask)
{
mInputAssemblyAndColorBlendStateInfo.blendEnableMask =
static_cast<uint8_t>(blendEnabledMask.bits());
transition->set(
ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, blendEnableMask));
}
void GraphicsPipelineDesc::updateBlendEquations(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt)
{
constexpr size_t kSize = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex = 0; attachmentIndex < blendStateExt.mMaxDrawBuffers;
++attachmentIndex)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mInputAssemblyAndColorBlendStateInfo.attachments[attachmentIndex];
blendAttachmentState.colorBlendOp =
PackGLBlendOp(blendStateExt.getEquationColorIndexed(attachmentIndex));
blendAttachmentState.alphaBlendOp =
PackGLBlendOp(blendStateExt.getEquationAlphaIndexed(attachmentIndex));
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo,
attachments, attachmentIndex, kSize));
}
}
void GraphicsPipelineDesc::updateBlendFuncs(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt)
{
constexpr size_t kSize = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex = 0; attachmentIndex < blendStateExt.mMaxDrawBuffers;
++attachmentIndex)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mInputAssemblyAndColorBlendStateInfo.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(mInputAssemblyAndColorBlendStateInfo,
attachments, attachmentIndex, kSize));
}
}
void GraphicsPipelineDesc::setColorWriteMasks(gl::BlendStateExt::ColorMaskStorage::Type colorMasks,
const gl::DrawBufferMask &alphaMask,
const gl::DrawBufferMask &enabledDrawBuffers)
{
PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = mInputAssemblyAndColorBlendStateInfo;
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(inputAndBlend.colorWriteMaskBits, colorIndexGL, mask);
}
}
void GraphicsPipelineDesc::setSingleColorWriteMask(uint32_t colorIndexGL,
VkColorComponentFlags colorComponentFlags)
{
PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend = mInputAssemblyAndColorBlendStateInfo;
uint8_t colorMask = static_cast<uint8_t>(colorComponentFlags);
Int4Array_Set(inputAndBlend.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(mInputAssemblyAndColorBlendStateInfo,
colorWriteMaskBits, colorIndexGL, 4));
}
}
void GraphicsPipelineDesc::setDepthTestEnabled(bool enabled)
{
mDepthStencilStateInfo.enable.depthTest = enabled;
}
void GraphicsPipelineDesc::setDepthWriteEnabled(bool enabled)
{
mDepthStencilStateInfo.enable.depthWrite = enabled;
}
void GraphicsPipelineDesc::setDepthFunc(VkCompareOp op)
{
SetBitField(mDepthStencilStateInfo.depthCompareOpAndSurfaceRotation.depthCompareOp, op);
}
void GraphicsPipelineDesc::setDepthClampEnabled(bool enabled)
{
mRasterizationAndMultisampleStateInfo.bits.depthClampEnable = enabled;
}
void GraphicsPipelineDesc::setStencilTestEnabled(bool enabled)
{
mDepthStencilStateInfo.enable.stencilTest = enabled;
}
void GraphicsPipelineDesc::setStencilFrontFuncs(uint8_t reference,
VkCompareOp compareOp,
uint8_t compareMask)
{
mDepthStencilStateInfo.frontStencilReference = reference;
mDepthStencilStateInfo.front.compareMask = compareMask;
SetBitField(mDepthStencilStateInfo.front.ops.compare, compareOp);
}
void GraphicsPipelineDesc::setStencilBackFuncs(uint8_t reference,
VkCompareOp compareOp,
uint8_t compareMask)
{
mDepthStencilStateInfo.backStencilReference = reference;
mDepthStencilStateInfo.back.compareMask = compareMask;
SetBitField(mDepthStencilStateInfo.back.ops.compare, compareOp);
}
void GraphicsPipelineDesc::setStencilFrontOps(VkStencilOp failOp,
VkStencilOp passOp,
VkStencilOp depthFailOp)
{
SetBitField(mDepthStencilStateInfo.front.ops.fail, failOp);
SetBitField(mDepthStencilStateInfo.front.ops.pass, passOp);
SetBitField(mDepthStencilStateInfo.front.ops.depthFail, depthFailOp);
}
void GraphicsPipelineDesc::setStencilBackOps(VkStencilOp failOp,
VkStencilOp passOp,
VkStencilOp depthFailOp)
{
SetBitField(mDepthStencilStateInfo.back.ops.fail, failOp);
SetBitField(mDepthStencilStateInfo.back.ops.pass, passOp);
SetBitField(mDepthStencilStateInfo.back.ops.depthFail, depthFailOp);
}
void GraphicsPipelineDesc::setStencilFrontWriteMask(uint8_t mask)
{
mDepthStencilStateInfo.front.writeMask = mask;
}
void GraphicsPipelineDesc::setStencilBackWriteMask(uint8_t mask)
{
mDepthStencilStateInfo.back.writeMask = mask;
}
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(mDepthStencilStateInfo, enable));
}
void GraphicsPipelineDesc::updateDepthFunc(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setDepthFunc(PackGLCompareFunc(depthStencilState.depthFunc));
transition->set(
ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, depthCompareOpAndSurfaceRotation));
}
void GraphicsPipelineDesc::updateSurfaceRotation(GraphicsPipelineTransitionBits *transition,
const SurfaceRotation surfaceRotation)
{
SetBitField(mDepthStencilStateInfo.depthCompareOpAndSurfaceRotation.surfaceRotation,
surfaceRotation);
transition->set(
ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, depthCompareOpAndSurfaceRotation));
}
void GraphicsPipelineDesc::updateDepthWriteEnabled(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState,
const gl::Framebuffer *drawFramebuffer)
{
// Don't write to depth buffers that should not exist
setDepthWriteEnabled(drawFramebuffer->hasDepth() ? depthStencilState.depthMask : false);
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, enable));
}
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(mDepthStencilStateInfo, enable));
}
void GraphicsPipelineDesc::updateStencilFrontFuncs(GraphicsPipelineTransitionBits *transition,
GLint ref,
const gl::DepthStencilState &depthStencilState)
{
setStencilFrontFuncs(static_cast<uint8_t>(ref),
PackGLCompareFunc(depthStencilState.stencilFunc),
static_cast<uint8_t>(depthStencilState.stencilMask));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, front));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, frontStencilReference));
}
void GraphicsPipelineDesc::updateStencilBackFuncs(GraphicsPipelineTransitionBits *transition,
GLint ref,
const gl::DepthStencilState &depthStencilState)
{
setStencilBackFuncs(static_cast<uint8_t>(ref),
PackGLCompareFunc(depthStencilState.stencilBackFunc),
static_cast<uint8_t>(depthStencilState.stencilBackMask));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, back));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, backStencilReference));
}
void GraphicsPipelineDesc::updateStencilFrontOps(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setStencilFrontOps(PackGLStencilOp(depthStencilState.stencilFail),
PackGLStencilOp(depthStencilState.stencilPassDepthPass),
PackGLStencilOp(depthStencilState.stencilPassDepthFail));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, front));
}
void GraphicsPipelineDesc::updateStencilBackOps(GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState)
{
setStencilBackOps(PackGLStencilOp(depthStencilState.stencilBackFail),
PackGLStencilOp(depthStencilState.stencilBackPassDepthPass),
PackGLStencilOp(depthStencilState.stencilBackPassDepthFail));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, back));
}
void GraphicsPipelineDesc::updateStencilFrontWriteMask(
GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState,
const gl::Framebuffer *drawFramebuffer)
{
// Don't write to stencil buffers that should not exist
setStencilFrontWriteMask(static_cast<uint8_t>(
drawFramebuffer->hasStencil() ? depthStencilState.stencilWritemask : 0));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, front));
}
void GraphicsPipelineDesc::updateStencilBackWriteMask(
GraphicsPipelineTransitionBits *transition,
const gl::DepthStencilState &depthStencilState,
const gl::Framebuffer *drawFramebuffer)
{
// Don't write to stencil buffers that should not exist
setStencilBackWriteMask(static_cast<uint8_t>(
drawFramebuffer->hasStencil() ? depthStencilState.stencilBackWritemask : 0));
transition->set(ANGLE_GET_TRANSITION_BIT(mDepthStencilStateInfo, back));
}
void GraphicsPipelineDesc::updatePolygonOffsetFillEnabled(
GraphicsPipelineTransitionBits *transition,
bool enabled)
{
mRasterizationAndMultisampleStateInfo.bits.depthBiasEnable = enabled;
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
void GraphicsPipelineDesc::updatePolygonOffset(GraphicsPipelineTransitionBits *transition,
const gl::RasterizerState &rasterState)
{
mRasterizationAndMultisampleStateInfo.depthBiasSlopeFactor = rasterState.polygonOffsetFactor;
mRasterizationAndMultisampleStateInfo.depthBiasConstantFactor = rasterState.polygonOffsetUnits;
transition->set(
ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, depthBiasSlopeFactor));
transition->set(
ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, depthBiasConstantFactor));
}
void GraphicsPipelineDesc::setRenderPassDesc(const RenderPassDesc &renderPassDesc)
{
mRenderPassDesc = renderPassDesc;
}
void GraphicsPipelineDesc::updateDrawableSize(GraphicsPipelineTransitionBits *transition,
uint32_t width,
uint32_t height)
{
SetBitField(mDrawableSize.width, width);
SetBitField(mDrawableSize.height, height);
transition->set(ANGLE_GET_TRANSITION_BIT(mDrawableSize, width));
transition->set(ANGLE_GET_TRANSITION_BIT(mDrawableSize, height));
}
void GraphicsPipelineDesc::updateSubpass(GraphicsPipelineTransitionBits *transition,
uint32_t subpass)
{
if (mRasterizationAndMultisampleStateInfo.bits.subpass != subpass)
{
SetBitField(mRasterizationAndMultisampleStateInfo.bits.subpass, subpass);
transition->set(ANGLE_GET_TRANSITION_BIT(mRasterizationAndMultisampleStateInfo, bits));
}
}
void GraphicsPipelineDesc::updatePatchVertices(GraphicsPipelineTransitionBits *transition,
GLuint value)
{
SetBitField(mInputAssemblyAndColorBlendStateInfo.primitive.patchVertices, value);
transition->set(ANGLE_GET_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo, primitive));
}
void GraphicsPipelineDesc::resetSubpass(GraphicsPipelineTransitionBits *transition)
{
updateSubpass(transition, 0);
}
void GraphicsPipelineDesc::nextSubpass(GraphicsPipelineTransitionBits *transition)
{
updateSubpass(transition, mRasterizationAndMultisampleStateInfo.bits.subpass + 1);
}
void GraphicsPipelineDesc::setSubpass(uint32_t subpass)
{
SetBitField(mRasterizationAndMultisampleStateInfo.bits.subpass, subpass);
}
uint32_t GraphicsPipelineDesc::getSubpass() const
{
return mRasterizationAndMultisampleStateInfo.bits.subpass;
}
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, mRenderPassDesc) >> kTransitionByteShift;
constexpr size_t kBitCount = kRenderPassDescSize >> kTransitionByteShift;
for (size_t bit = 0; bit < kBitCount; ++bit)
{
transition->set(kFirstBit + bit);
}
}
// 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, VK_ATTACHMENT_LOAD_OP_LOAD, RenderPassStoreOp::Store);
setStencilOps(index, VK_ATTACHMENT_LOAD_OP_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,
VkAttachmentLoadOp loadOp,
RenderPassStoreOp storeOp)
{
PackedAttachmentOpsDesc &ops = mOps[index.get()];
SetBitField(ops.loadOp, loadOp);
SetBitField(ops.storeOp, storeOp);
ops.isInvalidated = false;
}
void AttachmentOpsArray::setStencilOps(PackedAttachmentIndex index,
VkAttachmentLoadOp 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, VK_ATTACHMENT_LOAD_OP_CLEAR);
}
void AttachmentOpsArray::setClearStencilOp(PackedAttachmentIndex index)
{
PackedAttachmentOpsDesc &ops = mOps[index.get()];
SetBitField(ops.stencilLoadOp, VK_ATTACHMENT_LOAD_OP_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{} {}
DescriptorSetLayoutDesc::~DescriptorSetLayoutDesc() = default;
DescriptorSetLayoutDesc::DescriptorSetLayoutDesc(const DescriptorSetLayoutDesc &other) = default;
DescriptorSetLayoutDesc &DescriptorSetLayoutDesc::operator=(const DescriptorSetLayoutDesc &other) =
default;
size_t DescriptorSetLayoutDesc::hash() const
{
return angle::ComputeGenericHash(mPackedDescriptorSetLayout);
}
bool DescriptorSetLayoutDesc::operator==(const DescriptorSetLayoutDesc &other) const
{
return (memcmp(&mPackedDescriptorSetLayout, &other.mPackedDescriptorSetLayout,
sizeof(mPackedDescriptorSetLayout)) == 0);
}
void DescriptorSetLayoutDesc::update(uint32_t bindingIndex,
VkDescriptorType type,
uint32_t count,
VkShaderStageFlags stages,
const Sampler *immutableSampler)
{
ASSERT(static_cast<size_t>(type) < std::numeric_limits<uint16_t>::max());
ASSERT(count < std::numeric_limits<uint16_t>::max());
PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
SetBitField(packedBinding.type, type);
SetBitField(packedBinding.count, count);
SetBitField(packedBinding.stages, stages);
packedBinding.immutableSampler = VK_NULL_HANDLE;
packedBinding.pad = 0;
if (immutableSampler)
{
ASSERT(count == 1);
packedBinding.immutableSampler = immutableSampler->getHandle();
}
}
void DescriptorSetLayoutDesc::unpackBindings(DescriptorSetLayoutBindingVector *bindings,
std::vector<VkSampler> *immutableSamplers) const
{
for (uint32_t bindingIndex = 0; bindingIndex < kMaxDescriptorSetLayoutBindings; ++bindingIndex)
{
const PackedDescriptorSetBinding &packedBinding = mPackedDescriptorSetLayout[bindingIndex];
if (packedBinding.count == 0)
continue;
VkDescriptorSetLayoutBinding binding = {};
binding.binding = bindingIndex;
binding.descriptorCount = packedBinding.count;
binding.descriptorType = static_cast<VkDescriptorType>(packedBinding.type);
binding.stageFlags = static_cast<VkShaderStageFlags>(packedBinding.stages);
if (packedBinding.immutableSampler != VK_NULL_HANDLE)
{
ASSERT(packedBinding.count == 1);
immutableSamplers->push_back(packedBinding.immutableSampler);
binding.pImmutableSamplers = reinterpret_cast<const VkSampler *>(angle::DirtyPointer);
}
bindings->push_back(binding);
}
if (!immutableSamplers->empty())
{
// Patch up pImmutableSampler addresses now that the vector is stable
int immutableIndex = 0;
for (VkDescriptorSetLayoutBinding &binding : *bindings)
{
if (binding.pImmutableSamplers)
{
binding.pImmutableSamplers = &(*immutableSamplers)[immutableIndex];
immutableIndex++;
}
}
}
}
// PipelineLayoutDesc implementation.
PipelineLayoutDesc::PipelineLayoutDesc() : mDescriptorSetLayouts{}, mPushConstantRanges{} {}
PipelineLayoutDesc::~PipelineLayoutDesc() = default;
PipelineLayoutDesc::PipelineLayoutDesc(const PipelineLayoutDesc &other) = default;
PipelineLayoutDesc &PipelineLayoutDesc::operator=(const PipelineLayoutDesc &rhs)
{
mDescriptorSetLayouts = rhs.mDescriptorSetLayouts;
mPushConstantRanges = rhs.mPushConstantRanges;
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(gl::ShaderType shaderType,
uint32_t offset,
uint32_t size)
{
ASSERT(shaderType == gl::ShaderType::Vertex || shaderType == gl::ShaderType::Fragment ||
shaderType == gl::ShaderType::Geometry || shaderType == gl::ShaderType::Compute);
PackedPushConstantRange &packed = mPushConstantRanges[shaderType];
packed.offset = offset;
packed.size = size;
}
const PushConstantRangeArray<PackedPushConstantRange> &PipelineLayoutDesc::getPushConstantRanges()
const
{
return mPushConstantRanges;
}
// PipelineHelper implementation.
PipelineHelper::PipelineHelper() = default;
PipelineHelper::~PipelineHelper() = default;
void PipelineHelper::destroy(VkDevice device)
{
mPipeline.destroy(device);
}
void PipelineHelper::addTransition(GraphicsPipelineTransitionBits bits,
const GraphicsPipelineDesc *desc,
PipelineHelper *pipeline)
{
mTransitions.emplace_back(bits, desc, pipeline);
}
TextureDescriptorDesc::TextureDescriptorDesc() : mMaxIndex(0)
{
mSerials.fill({kInvalidImageOrBufferViewSubresourceSerial, kInvalidSamplerSerial});
}
TextureDescriptorDesc::~TextureDescriptorDesc() = default;
TextureDescriptorDesc::TextureDescriptorDesc(const TextureDescriptorDesc &other) = default;
TextureDescriptorDesc &TextureDescriptorDesc::operator=(const TextureDescriptorDesc &other) =
default;
void TextureDescriptorDesc::update(size_t index,
ImageOrBufferViewSubresourceSerial viewSerial,
SamplerSerial samplerSerial)
{
if (index >= mMaxIndex)
{
mMaxIndex = static_cast<uint32_t>(index + 1);
}
mSerials[index].view = viewSerial;
mSerials[index].sampler = samplerSerial;
}
size_t TextureDescriptorDesc::hash() const
{
return angle::ComputeGenericHash(&mSerials, sizeof(TexUnitSerials) * mMaxIndex);
}
void TextureDescriptorDesc::reset()
{
memset(mSerials.data(), 0, sizeof(mSerials[0]) * mMaxIndex);
mMaxIndex = 0;
}
bool TextureDescriptorDesc::operator==(const TextureDescriptorDesc &other) const
{
if (mMaxIndex != other.mMaxIndex)
return false;
if (mMaxIndex == 0)
return true;
return memcmp(mSerials.data(), other.mSerials.data(), sizeof(TexUnitSerials) * mMaxIndex) == 0;
}
// UniformsAndXfbDescriptorDesc implementation.
UniformsAndXfbDescriptorDesc::UniformsAndXfbDescriptorDesc()
{
reset();
}
UniformsAndXfbDescriptorDesc::~UniformsAndXfbDescriptorDesc() = default;
UniformsAndXfbDescriptorDesc::UniformsAndXfbDescriptorDesc(
const UniformsAndXfbDescriptorDesc &other) = default;
UniformsAndXfbDescriptorDesc &UniformsAndXfbDescriptorDesc::operator=(
const UniformsAndXfbDescriptorDesc &other) = default;
size_t UniformsAndXfbDescriptorDesc::hash() const
{
ASSERT(mBufferCount > 0);
return angle::ComputeGenericHash(&mBufferSerials, sizeof(mBufferSerials[0]) * mBufferCount) ^
angle::ComputeGenericHash(
&mXfbBufferOffsets,
sizeof(mXfbBufferOffsets[0]) * (mBufferCount - kDefaultUniformBufferCount));
}
void UniformsAndXfbDescriptorDesc::reset()
{
mBufferCount = 0;
memset(&mBufferSerials, 0, sizeof(mBufferSerials));
memset(&mXfbBufferOffsets, 0, sizeof(mXfbBufferOffsets));
}
bool UniformsAndXfbDescriptorDesc::operator==(const UniformsAndXfbDescriptorDesc &other) const
{
if (mBufferCount != other.mBufferCount)
{
return false;
}
ASSERT(mBufferCount > 0);
return memcmp(&mBufferSerials, &other.mBufferSerials,
sizeof(mBufferSerials[0]) * mBufferCount) == 0 &&
memcmp(&mXfbBufferOffsets, &other.mXfbBufferOffsets,
sizeof(mXfbBufferOffsets[0]) * (mBufferCount - kDefaultUniformBufferCount)) == 0;
}
// ShaderBuffersDescriptorDesc implementation.
ShaderBuffersDescriptorDesc::ShaderBuffersDescriptorDesc()
{
reset();
}
ShaderBuffersDescriptorDesc::~ShaderBuffersDescriptorDesc() = default;
ShaderBuffersDescriptorDesc::ShaderBuffersDescriptorDesc(const ShaderBuffersDescriptorDesc &other) =
default;
ShaderBuffersDescriptorDesc &ShaderBuffersDescriptorDesc::operator=(
const ShaderBuffersDescriptorDesc &other) = default;
size_t ShaderBuffersDescriptorDesc::hash() const
{
return angle::ComputeGenericHash(mPayload.data(), sizeof(mPayload[0]) * mPayload.size());
}
void ShaderBuffersDescriptorDesc::reset()
{
mPayload.clear();
}
bool ShaderBuffersDescriptorDesc::operator==(const ShaderBuffersDescriptorDesc &other) const
{
return mPayload == other.mPayload;
}
// 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);
}
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::updateFramebufferFetchMode(bool hasFramebufferFetch)
{
SetBitField(mHasFramebufferFetch, hasFramebufferFetch);
}
void FramebufferDesc::updateRenderToTexture(bool isRenderToTexture)
{
SetBitField(mIsRenderToTexture, isRenderToTexture);
}
// 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,
uint64_t externalFormat,
angle::FormatID formatID)
{
update(contextVk, samplerState, stencilMode, externalFormat, formatID);
}
void SamplerDesc::reset()
{
mMipLodBias = 0.0f;
mMaxAnisotropy = 0.0f;
mMinLod = 0.0f;
mMaxLod = 0.0f;
mExternalOrVkFormat = 0;
mMagFilter = 0;
mMinFilter = 0;
mMipmapMode = 0;
mAddressModeU = 0;
mAddressModeV = 0;
mAddressModeW = 0;
mCompareEnabled = 0;
mCompareOp = 0;
mIsExternalFormat = 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,
uint64_t externalFormat,
angle::FormatID formatID)
{
const angle::FeaturesVk &featuresVk = contextVk->getFeatures();
mMipLodBias = 0.0f;
for (size_t lodOffsetFeatureIdx = 0;
lodOffsetFeatureIdx < featuresVk.forceTextureLODOffset.size(); lodOffsetFeatureIdx++)
{
if (featuresVk.forceTextureLODOffset[lodOffsetFeatureIdx].enabled)
{
// Make sure only one forceTextureLODOffset feature is set.
ASSERT(mMipLodBias == 0.0f);
mMipLodBias = static_cast<float>(lodOffsetFeatureIdx + 1);
}
}
mMaxAnisotropy = samplerState.getMaxAnisotropy();
mMinLod = samplerState.getMinLod();
mMaxLod = samplerState.getMaxLod();
// GL has no notion of external format, this must be provided from metadata from the image
const vk::Format &vkFormat = contextVk->getRenderer()->getFormat(formatID);
mIsExternalFormat = (externalFormat != 0) ? 1 : 0;
mExternalOrVkFormat = (externalFormat != 0)
? externalFormat
: (vkFormat.intendedFormat().isYUV)
? static_cast<uint64_t>(vkFormat.actualImageVkFormat())
: 0;
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;
}
GLenum magFilter = samplerState.getMagFilter();
GLenum minFilter = samplerState.getMinFilter();
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;
mBorderColor = samplerState.getBorderColor().colorF;
if (vkFormat.intendedFormatID != angle::FormatID::NONE)
{
LoadTextureBorderFunctionInfo loadFunction = vkFormat.textureBorderLoadFunctions();
loadFunction.loadFunction(mBorderColor);
}
mReserved = 0;
}
angle::Result SamplerDesc::init(ContextVk *contextVk, Sampler *sampler) const
{
const gl::Extensions &extensions = contextVk->getExtensions();
bool anisotropyEnable = extensions.textureFilterAnisotropic && 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;
// Note: because we don't detect changes to this hint (no dirty bit), if a sampler is created
// with the hint enabled, and then the hint gets disabled, the next render will do so with the
// hint enabled.
VkSamplerFilteringPrecisionGOOGLE filteringInfo = {};
GLenum hint = contextVk->getState().getTextureFilteringHint();
if (hint == GL_NICEST)
{
ASSERT(extensions.textureFilteringCHROMIUM);
filteringInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_FILTERING_PRECISION_GOOGLE;
filteringInfo.samplerFilteringPrecisionMode =
VK_SAMPLER_FILTERING_PRECISION_MODE_HIGH_GOOGLE;
AddToPNextChain(&createInfo, &filteringInfo);
}
VkSamplerYcbcrConversionInfo yuvConversionInfo = {};
if (mExternalOrVkFormat)
{
ASSERT((contextVk->getRenderer()->getFeatures().supportsYUVSamplerConversion.enabled));
yuvConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO;
yuvConversionInfo.pNext = nullptr;
yuvConversionInfo.conversion =
contextVk->getRenderer()->getYuvConversionCache().getSamplerYcbcrConversion(
mExternalOrVkFormat, (mIsExternalFormat == 1));
AddToPNextChain(&createInfo, &yuvConversionInfo);
// 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;
// VUID-VkSamplerCreateInfo-minFilter VkCreateSampler:
// VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT
// specifies that the format can have different chroma, min, and mag filters. However,
// VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT is
// not supported for VkSamplerYcbcrConversionCreateInfo.format = VK_FORMAT_UNDEFINED so
// minFilter/magFilter needs to be equal to chromaFilter.
// HardwareBufferImageSiblingVkAndroid() forces VK_FILTER_NEAREST, so force
// VK_FILTER_NEAREST here too.
createInfo.magFilter = VK_FILTER_NEAREST;
createInfo.minFilter = VK_FILTER_NEAREST;
}
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().supportsCustomBorderColorEXT.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);
}
const RenderPass &RenderPassHelper::getRenderPass() const
{
return mRenderPass;
}
RenderPass &RenderPassHelper::getRenderPass()
{
return mRenderPass;
}
const RenderPassPerfCounters &RenderPassHelper::getPerfCounters() const
{
return mPerfCounters;
}
RenderPassPerfCounters &RenderPassHelper::getPerfCounters()
{
return mPerfCounters;
}
} // namespace vk
// RenderPassCache implementation.
RenderPassCache::RenderPassCache() = default;
RenderPassCache::~RenderPassCache()
{
ASSERT(mPayload.empty());
}
void RenderPassCache::destroy(RendererVk *rendererVk)
{
rendererVk->accumulateCacheStats(VulkanCacheType::CompatibleRenderPass,
mCompatibleRenderPassCacheStats);
rendererVk->accumulateCacheStats(VulkanCacheType::RenderPassWithOps,
mRenderPassWithOpsCacheStats);
VkDevice device = rendererVk->getDevice();
for (auto &outerIt : mPayload)
{
for (auto &innerIt : outerIt.second)
{
innerIt.second.destroy(device);
}
}
mPayload.clear();
}
angle::Result RenderPassCache::addRenderPass(ContextVk *contextVk,
const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut)
{
// Insert some placeholder attachment ops. Note that render passes with different ops are still
// compatible. The load/store values are not important as they are aren't used for real RPs.
//
// It would be nice to pre-populate the cache in the Renderer so we rarely miss here.
vk::AttachmentOpsArray ops;
vk::PackedAttachmentIndex colorIndexVk(0);
for (uint32_t colorIndexGL = 0; colorIndexGL < desc.colorAttachmentRange(); ++colorIndexGL)
{
if (!desc.isColorAttachmentEnabled(colorIndexGL))
{
continue;
}
ops.initWithLoadStore(colorIndexVk, vk::ImageLayout::ColorAttachment,
vk::ImageLayout::ColorAttachment);
++colorIndexVk;
}
if (desc.hasDepthStencilAttachment())
{
// This API is only called by getCompatibleRenderPass(). What we need here is to create a
// compatible renderpass with the desc. Vulkan spec says image layout are not counted toward
// render pass compatibility: "Two render passes are compatible if their corresponding
// color, input, resolve, and depth/stencil attachment references are compatible and if they
// are otherwise identical except for: Initial and final image layout in attachment
// descriptions; Image layout in attachment references". We pick the most used layout here
// since it doesn't matter.
vk::ImageLayout imageLayout = vk::ImageLayout::DepthStencilAttachment;
ops.initWithLoadStore(colorIndexVk, imageLayout, imageLayout);
}
return getRenderPassWithOpsImpl(contextVk, desc, ops, false, renderPassOut);
}
angle::Result RenderPassCache::getRenderPassWithOps(ContextVk *contextVk,
const vk::RenderPassDesc &desc,
const vk::AttachmentOpsArray &attachmentOps,
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,
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.miss();
vk::RenderPassHelper newRenderPass;
ANGLE_TRY(vk::InitializeRenderPassFromDesc(contextVk, desc, attachmentOps, &newRenderPass));
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;
}
// GraphicsPipelineCache implementation.
GraphicsPipelineCache::GraphicsPipelineCache() = default;
GraphicsPipelineCache::~GraphicsPipelineCache()
{
ASSERT(mPayload.empty());
}
void GraphicsPipelineCache::destroy(RendererVk *rendererVk)
{
accumulateCacheStats(rendererVk);
VkDevice device = rendererVk->getDevice();
for (auto &item : mPayload)
{
vk::PipelineHelper &pipeline = item.second;
pipeline.destroy(device);
}
mPayload.clear();
}
void GraphicsPipelineCache::release(ContextVk *context)
{
for (auto &item : mPayload)
{
vk::PipelineHelper &pipeline = item.second;
context->addGarbage(&pipeline.getPipeline());
}
mPayload.clear();
}
angle::Result GraphicsPipelineCache::insertPipeline(
ContextVk *contextVk,
const vk::PipelineCache &pipelineCacheVk,
const vk::RenderPass &compatibleRenderPass,
const vk::PipelineLayout &pipelineLayout,
const gl::AttributesMask &activeAttribLocationsMask,
const gl::ComponentTypeMask &programAttribsTypeMask,
const vk::ShaderModule *vertexModule,
const vk::ShaderModule *fragmentModule,
const vk::ShaderModule *geometryModule,
const vk::ShaderModule *tessControlModule,
const vk::ShaderModule *tessEvaluationModule,
const vk::SpecializationConstants &specConsts,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut)
{
vk::Pipeline newPipeline;
// This "if" is left here for the benefit of VulkanPipelineCachePerfTest.
if (contextVk != nullptr)
{
contextVk->getRenderer()->onNewGraphicsPipeline();
ANGLE_TRY(desc.initializePipeline(
contextVk, pipelineCacheVk, compatibleRenderPass, pipelineLayout,
activeAttribLocationsMask, programAttribsTypeMask, vertexModule, fragmentModule,
geometryModule, tessControlModule, tessEvaluationModule, specConsts, &newPipeline));
}
// The Serial will be updated outside of this query.
auto insertedItem = mPayload.emplace(desc, std::move(newPipeline));
*descPtrOut = &insertedItem.first->first;
*pipelineOut = &insertedItem.first->second;
return angle::Result::Continue;
}
void GraphicsPipelineCache::populate(const vk::GraphicsPipelineDesc &desc, vk::Pipeline &&pipeline)
{
auto item = mPayload.find(desc);
if (item != mPayload.end())
{
return;
}
mPayload.emplace(desc, std::move(pipeline));
}
// DescriptorSetLayoutCache implementation.
DescriptorSetLayoutCache::DescriptorSetLayoutCache() = default;
DescriptorSetLayoutCache::~DescriptorSetLayoutCache()
{
ASSERT(mPayload.empty());
}
void DescriptorSetLayoutCache::destroy(RendererVk *rendererVk)
{
rendererVk->accumulateCacheStats(VulkanCacheType::DescriptorSetLayout, mCacheStats);
VkDevice device = rendererVk->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::BindingPointer<vk::DescriptorSetLayout> *descriptorSetLayoutOut)
{
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::RefCountedDescriptorSetLayout &layout = iter->second;
descriptorSetLayoutOut->set(&layout);
mCacheStats.hit();
return angle::Result::Continue;
}
mCacheStats.miss();
// We must unpack the descriptor set layout description.
vk::DescriptorSetLayoutBindingVector bindingVector;
std::vector<VkSampler> immutableSamplers;
desc.unpackBindings(&bindingVector, &immutableSamplers);
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, vk::RefCountedDescriptorSetLayout(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(RendererVk *rendererVk)
{
accumulateCacheStats(rendererVk);
VkDevice device = rendererVk->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::BindingPointer<vk::PipelineLayout> *pipelineLayoutOut)
{
auto iter = mPayload.find(desc);
if (iter != mPayload.end())
{
vk::RefCountedPipelineLayout &layout = iter->second;
pipelineLayoutOut->set(&layout);
mCacheStats.hit();
return angle::Result::Continue;
}
mCacheStats.miss();
// Note this does not handle gaps in descriptor set layouts gracefully.
angle::FixedVector<VkDescriptorSetLayout, vk::kMaxDescriptorSetLayouts> setLayoutHandles;
for (const vk::BindingPointer<vk::DescriptorSetLayout> &layoutPtr : descriptorSetLayouts)
{
if (layoutPtr.valid())
{
VkDescriptorSetLayout setLayout = layoutPtr.get().getHandle();
if (setLayout != VK_NULL_HANDLE)
{
setLayoutHandles.push_back(setLayout);
}
}
}
const vk::PushConstantRangeArray<vk::PackedPushConstantRange> &descPushConstantRanges =
desc.getPushConstantRanges();
gl::ShaderVector<VkPushConstantRange> pushConstantRanges;
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
const vk::PackedPushConstantRange &pushConstantDesc = descPushConstantRanges[shaderType];
if (pushConstantDesc.size > 0)
{
VkPushConstantRange range;
range.stageFlags = gl_vk::kShaderStageMap[shaderType];
range.offset = pushConstantDesc.offset;
range.size = pushConstantDesc.size;
pushConstantRanges.push_back(range);
}
}
// 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();
createInfo.pushConstantRangeCount = static_cast<uint32_t>(pushConstantRanges.size());
createInfo.pPushConstantRanges = pushConstantRanges.data();
vk::PipelineLayout newLayout;
ANGLE_VK_TRY(context, newLayout.init(context->getDevice(), createInfo));
auto insertedItem = mPayload.emplace(desc, vk::RefCountedPipelineLayout(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(RendererVk *rendererVk)
{
rendererVk->accumulateCacheStats(VulkanCacheType::SamplerYcbcrConversion, mCacheStats);
VkDevice device = rendererVk->getDevice();
for (auto &iter : mExternalFormatPayload)
{
vk::RefCountedSamplerYcbcrConversion &yuvSampler = iter.second;
ASSERT(!yuvSampler.isReferenced());
yuvSampler.get().destroy(device);
rendererVk->getActiveHandleCounts().onDeallocate(vk::HandleType::SamplerYcbcrConversion);
}
for (auto &iter : mVkFormatPayload)
{
vk::RefCountedSamplerYcbcrConversion &yuvSampler = iter.second;
ASSERT(!yuvSampler.isReferenced());
yuvSampler.get().destroy(device);
rendererVk->getActiveHandleCounts().onDeallocate(vk::HandleType::SamplerYcbcrConversion);
}
mExternalFormatPayload.clear();
mVkFormatPayload.clear();
}
template <typename T>
angle::Result SamplerYcbcrConversionCache::getYuvConversionImpl(
vk::Context *context,
T format,
SamplerYcbcrConversionMap<T> *payload,
const VkSamplerYcbcrConversionCreateInfo &yuvConversionCreateInfo,
vk::BindingPointer<vk::SamplerYcbcrConversion> *yuvConversionOut)
{
const auto iter = payload->find(format);
if (iter != payload->end())
{
vk::RefCountedSamplerYcbcrConversion &yuvConversion = iter->second;
yuvConversionOut->set(&yuvConversion);
mCacheStats.hit();
return angle::Result::Continue;
}
mCacheStats.miss();
vk::SamplerYcbcrConversion wrappedYuvConversion;
ANGLE_VK_TRY(context, wrappedYuvConversion.init(context->getDevice(), yuvConversionCreateInfo));
auto insertedItem = payload->emplace(
format, vk::RefCountedSamplerYcbcrConversion(std::move(wrappedYuvConversion)));
vk::RefCountedSamplerYcbcrConversion &insertedYuvConversion = insertedItem.first->second;
yuvConversionOut->set(&insertedYuvConversion);
context->getRenderer()->getActiveHandleCounts().onAllocate(
vk::HandleType::SamplerYcbcrConversion);
return angle::Result::Continue;
}
angle::Result SamplerYcbcrConversionCache::getYuvConversion(
vk::Context *context,
uint64_t externalOrVkFormat,
bool isExternalFormat,
const VkSamplerYcbcrConversionCreateInfo &yuvConversionCreateInfo,
vk::BindingPointer<vk::SamplerYcbcrConversion> *yuvConversionOut)
{
if (isExternalFormat)
{
return getYuvConversionImpl(context, externalOrVkFormat, &mExternalFormatPayload,
yuvConversionCreateInfo, yuvConversionOut);
}
else
{
return getYuvConversionImpl(context, static_cast<VkFormat>(externalOrVkFormat),
&mVkFormatPayload, yuvConversionCreateInfo, yuvConversionOut);
}
}
template <typename T>
VkSamplerYcbcrConversion SamplerYcbcrConversionCache::getSamplerYcbcrConversionImpl(
T format,
const SamplerYcbcrConversionMap<T> &payload) const
{
const auto iter = payload.find(format);
if (iter != payload.end())
{
const vk::RefCountedSamplerYcbcrConversion &yuvConversion = iter->second;
return yuvConversion.get().getHandle();
}
// Should never get here if we have a valid format.
UNREACHABLE();
return VK_NULL_HANDLE;
}
VkSamplerYcbcrConversion SamplerYcbcrConversionCache::getSamplerYcbcrConversion(
uint64_t externalOrVkFormat,
bool isExternalFormat) const
{
if (isExternalFormat)
{
return getSamplerYcbcrConversionImpl(externalOrVkFormat, mExternalFormatPayload);
}
else
{
return getSamplerYcbcrConversionImpl(static_cast<VkFormat>(externalOrVkFormat),
mVkFormatPayload);
}
}
// SamplerCache implementation.
SamplerCache::SamplerCache() = default;
SamplerCache::~SamplerCache()
{
ASSERT(mPayload.empty());
}
void SamplerCache::destroy(RendererVk *rendererVk)
{
rendererVk->accumulateCacheStats(VulkanCacheType::Sampler, mCacheStats);
VkDevice device = rendererVk->getDevice();
for (auto &iter : mPayload)
{
vk::RefCountedSampler &sampler = iter.second;
ASSERT(!sampler.isReferenced());
sampler.get().get().destroy(device);
rendererVk->getActiveHandleCounts().onDeallocate(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.miss();
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()->getActiveHandleCounts().onAllocate(vk::HandleType::Sampler);
return angle::Result::Continue;
}
// DriverUniformsDescriptorSetCache implementation.
void DriverUniformsDescriptorSetCache::destroy(RendererVk *rendererVk)
{
accumulateCacheStats(rendererVk);
mPayload.clear();
}
// DescriptorSetCache implementation.
template <typename Key, VulkanCacheType CacheType>
void DescriptorSetCache<Key, CacheType>::destroy(RendererVk *rendererVk)
{
this->accumulateCacheStats(rendererVk);
mPayload.clear();
}
// RendererVk's methods are not accessible in vk_cache_utils.h
// Below declarations are needed to avoid linker errors.
// Unclear why Clang warns about weak vtables in this case.
ANGLE_DISABLE_WEAK_TEMPLATE_VTABLES_WARNING
template class DescriptorSetCache<vk::TextureDescriptorDesc, VulkanCacheType::TextureDescriptors>;
template class DescriptorSetCache<vk::UniformsAndXfbDescriptorDesc,
VulkanCacheType::UniformsAndXfbDescriptors>;
template class DescriptorSetCache<vk::ShaderBuffersDescriptorDesc,
VulkanCacheType::ShaderBuffersDescriptors>;
ANGLE_REENABLE_WEAK_TEMPLATE_VTABLES_WARNING
} // namespace rx