blob: 05c8a3bd0061c5b77e70f23b3a159091404c84ce [file] [log] [blame]
//
// Copyright 2016 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.
//
// ContextVk.cpp:
// Implements the class methods for ContextVk.
//
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "common/bitset_utils.h"
#include "common/debug.h"
#include "common/system_utils.h"
#include "common/utilities.h"
#include "image_util/loadimage.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/Program.h"
#include "libANGLE/Semaphore.h"
#include "libANGLE/ShareGroup.h"
#include "libANGLE/Surface.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/renderer_utils.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/CompilerVk.h"
#include "libANGLE/renderer/vulkan/DisplayVk.h"
#include "libANGLE/renderer/vulkan/FenceNVVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/MemoryObjectVk.h"
#include "libANGLE/renderer/vulkan/OverlayVk.h"
#include "libANGLE/renderer/vulkan/ProgramPipelineVk.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "libANGLE/renderer/vulkan/QueryVk.h"
#include "libANGLE/renderer/vulkan/RenderbufferVk.h"
#include "libANGLE/renderer/vulkan/SamplerVk.h"
#include "libANGLE/renderer/vulkan/SemaphoreVk.h"
#include "libANGLE/renderer/vulkan/ShaderVk.h"
#include "libANGLE/renderer/vulkan/SurfaceVk.h"
#include "libANGLE/renderer/vulkan/SyncVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
#include "libANGLE/renderer/vulkan/VertexArrayVk.h"
#include "libANGLE/renderer/vulkan/vk_renderer.h"
#include <fstream>
#include <iostream>
#include <sstream>
namespace rx
{
namespace
{
// If the total size of copyBufferToImage commands in the outside command buffer reaches the
// threshold below, the latter is flushed.
static constexpr VkDeviceSize kMaxBufferToImageCopySize = 64 * 1024 * 1024;
// The number of queueSerials we will reserve for outsideRenderPassCommands when we generate one for
// RenderPassCommands.
static constexpr size_t kMaxReservedOutsideRenderPassQueueSerials = 15;
// For shader uniforms such as gl_DepthRange and the viewport size.
struct GraphicsDriverUniforms
{
// Contain packed 8-bit values for atomic counter buffer offsets. These offsets are within
// Vulkan's minStorageBufferOffsetAlignment limit and are used to support unaligned offsets
// allowed in GL.
std::array<uint32_t, 2> acbBufferOffsets;
// .x is near, .y is far
std::array<float, 2> depthRange;
// Used to flip gl_FragCoord. Packed uvec2
uint32_t renderArea;
// Packed vec4 of snorm8
uint32_t flipXY;
// Only the lower 16 bits used
uint32_t dither;
// Various bits of state:
// - Surface rotation
// - Advanced blend equation
// - Sample count
// - Enabled clip planes
// - Depth transformation
uint32_t misc;
};
static_assert(sizeof(GraphicsDriverUniforms) % (sizeof(uint32_t) * 4) == 0,
"GraphicsDriverUniforms should be 16bytes aligned");
// Only used when transform feedback is emulated.
struct GraphicsDriverUniformsExtended
{
GraphicsDriverUniforms common;
// Only used with transform feedback emulation
std::array<int32_t, 4> xfbBufferOffsets;
int32_t xfbVerticesPerInstance;
int32_t padding[3];
};
static_assert(sizeof(GraphicsDriverUniformsExtended) % (sizeof(uint32_t) * 4) == 0,
"GraphicsDriverUniformsExtended should be 16bytes aligned");
struct ComputeDriverUniforms
{
// Atomic counter buffer offsets with the same layout as in GraphicsDriverUniforms.
std::array<uint32_t, 4> acbBufferOffsets;
};
uint32_t MakeFlipUniform(bool flipX, bool flipY, bool invertViewport)
{
// Create snorm values of either -1 or 1, based on whether flipping is enabled or not
// respectively.
constexpr uint8_t kSnormOne = 0x7F;
constexpr uint8_t kSnormMinusOne = 0x81;
// .xy are flips for the fragment stage.
uint32_t x = flipX ? kSnormMinusOne : kSnormOne;
uint32_t y = flipY ? kSnormMinusOne : kSnormOne;
// .zw are flips for the vertex stage.
uint32_t z = x;
uint32_t w = flipY != invertViewport ? kSnormMinusOne : kSnormOne;
return x | y << 8 | z << 16 | w << 24;
}
GLenum DefaultGLErrorCode(VkResult result)
{
switch (result)
{
case VK_ERROR_OUT_OF_HOST_MEMORY:
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
case VK_ERROR_TOO_MANY_OBJECTS:
return GL_OUT_OF_MEMORY;
case VK_ERROR_DEVICE_LOST:
return GL_CONTEXT_LOST;
default:
return GL_INVALID_OPERATION;
}
}
constexpr gl::ShaderMap<vk::ImageLayout> kShaderReadOnlyImageLayouts = {
{gl::ShaderType::Vertex, vk::ImageLayout::VertexShaderReadOnly},
{gl::ShaderType::TessControl, vk::ImageLayout::PreFragmentShadersReadOnly},
{gl::ShaderType::TessEvaluation, vk::ImageLayout::PreFragmentShadersReadOnly},
{gl::ShaderType::Geometry, vk::ImageLayout::PreFragmentShadersReadOnly},
{gl::ShaderType::Fragment, vk::ImageLayout::FragmentShaderReadOnly},
{gl::ShaderType::Compute, vk::ImageLayout::ComputeShaderReadOnly}};
constexpr gl::ShaderMap<vk::ImageLayout> kShaderWriteImageLayouts = {
{gl::ShaderType::Vertex, vk::ImageLayout::VertexShaderWrite},
{gl::ShaderType::TessControl, vk::ImageLayout::PreFragmentShadersWrite},
{gl::ShaderType::TessEvaluation, vk::ImageLayout::PreFragmentShadersWrite},
{gl::ShaderType::Geometry, vk::ImageLayout::PreFragmentShadersWrite},
{gl::ShaderType::Fragment, vk::ImageLayout::FragmentShaderWrite},
{gl::ShaderType::Compute, vk::ImageLayout::ComputeShaderWrite}};
constexpr VkBufferUsageFlags kVertexBufferUsage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
constexpr size_t kDynamicVertexDataSize = 16 * 1024;
bool CanMultiDrawIndirectUseCmd(ContextVk *contextVk,
VertexArrayVk *vertexArray,
gl::PrimitiveMode mode,
GLsizei drawcount,
GLsizei stride)
{
// Use the generic implementation if multiDrawIndirect is disabled, if line loop is being used
// for multiDraw, if drawcount is greater than maxDrawIndirectCount, or if there are streaming
// vertex attributes.
ASSERT(drawcount > 1);
const bool supportsMultiDrawIndirect =
contextVk->getFeatures().supportsMultiDrawIndirect.enabled;
const bool isMultiDrawLineLoop = (mode == gl::PrimitiveMode::LineLoop);
const bool isDrawCountBeyondLimit =
(static_cast<uint32_t>(drawcount) >
contextVk->getRenderer()->getPhysicalDeviceProperties().limits.maxDrawIndirectCount);
const bool isMultiDrawWithStreamingAttribs = vertexArray->getStreamingVertexAttribsMask().any();
const bool canMultiDrawIndirectUseCmd = supportsMultiDrawIndirect && !isMultiDrawLineLoop &&
!isDrawCountBeyondLimit &&
!isMultiDrawWithStreamingAttribs;
return canMultiDrawIndirectUseCmd;
}
uint32_t GetCoverageSampleCount(const gl::State &glState, GLint samples)
{
ASSERT(glState.isSampleCoverageEnabled());
// Get a fraction of the samples based on the coverage parameters.
// There are multiple ways to obtain an integer value from a float -
// truncation, ceil and round
//
// round() provides a more even distribution of values but doesn't seem to play well
// with all vendors (AMD). A way to work around this is to increase the comparison threshold
// of deqp tests. Though this takes care of deqp tests other apps would still have issues.
//
// Truncation provides an uneven distribution near the edges of the interval but seems to
// play well with all vendors.
//
// We are going with truncation for expediency.
return static_cast<uint32_t>(glState.getSampleCoverageValue() * samples);
}
void ApplySampleCoverage(const gl::State &glState, uint32_t coverageSampleCount, uint32_t *maskOut)
{
ASSERT(glState.isSampleCoverageEnabled());
uint32_t coverageMask = angle::BitMask<uint32_t>(coverageSampleCount);
if (glState.getSampleCoverageInvert())
{
coverageMask = ~coverageMask;
}
*maskOut &= coverageMask;
}
SurfaceRotation DetermineSurfaceRotation(const gl::Framebuffer *framebuffer,
const WindowSurfaceVk *windowSurface)
{
if (windowSurface && framebuffer->isDefault())
{
switch (windowSurface->getPreTransform())
{
case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
// Do not rotate gl_Position (surface matches the device's orientation):
return SurfaceRotation::Identity;
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
// Rotate gl_Position 90 degrees:
return SurfaceRotation::Rotated90Degrees;
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
// Rotate gl_Position 180 degrees:
return SurfaceRotation::Rotated180Degrees;
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
// Rotate gl_Position 270 degrees:
return SurfaceRotation::Rotated270Degrees;
default:
UNREACHABLE();
return SurfaceRotation::Identity;
}
}
else
{
// Do not rotate gl_Position (offscreen framebuffer):
return SurfaceRotation::Identity;
}
}
// Should not generate a copy with modern C++.
EventName GetTraceEventName(const char *title, uint64_t counter)
{
EventName buf;
snprintf(buf.data(), kMaxGpuEventNameLen - 1, "%s %llu", title,
static_cast<unsigned long long>(counter));
return buf;
}
vk::ResourceAccess GetColorAccess(const gl::State &state,
const gl::FramebufferState &framebufferState,
const gl::DrawBufferMask &emulatedAlphaMask,
bool hasFramebufferFetch,
size_t colorIndexGL)
{
// No access if draw buffer is disabled altogether
// Without framebuffer fetch:
// No access if color output is masked, or rasterizer discard is enabled
// With framebuffer fetch:
// Read access if color output is masked, or rasterizer discard is enabled
if (!framebufferState.getEnabledDrawBuffers().test(colorIndexGL))
{
return vk::ResourceAccess::Unused;
}
const gl::BlendStateExt &blendStateExt = state.getBlendStateExt();
uint8_t colorMask = gl::BlendStateExt::ColorMaskStorage::GetValueIndexed(
colorIndexGL, blendStateExt.getColorMaskBits());
if (emulatedAlphaMask[colorIndexGL])
{
colorMask &= ~VK_COLOR_COMPONENT_A_BIT;
}
const bool isOutputMasked = colorMask == 0 || state.isRasterizerDiscardEnabled();
if (isOutputMasked)
{
return hasFramebufferFetch ? vk::ResourceAccess::ReadOnly : vk::ResourceAccess::Unused;
}
return vk::ResourceAccess::ReadWrite;
}
vk::ResourceAccess GetDepthAccess(const gl::DepthStencilState &dsState,
UpdateDepthFeedbackLoopReason reason)
{
// Skip if depth/stencil not actually accessed.
if (reason == UpdateDepthFeedbackLoopReason::None)
{
return vk::ResourceAccess::Unused;
}
// Note that clear commands don't respect depth test enable, only the mask
// Note Other state can be stated here too in the future, such as rasterizer discard.
if (!dsState.depthTest && reason != UpdateDepthFeedbackLoopReason::Clear)
{
return vk::ResourceAccess::Unused;
}
if (dsState.isDepthMaskedOut())
{
// If depthFunc is GL_ALWAYS or GL_NEVER, we do not need to load depth value.
return (dsState.depthFunc == GL_ALWAYS || dsState.depthFunc == GL_NEVER)
? vk::ResourceAccess::Unused
: vk::ResourceAccess::ReadOnly;
}
return vk::ResourceAccess::ReadWrite;
}
vk::ResourceAccess GetStencilAccess(const gl::DepthStencilState &dsState,
UpdateDepthFeedbackLoopReason reason)
{
// Skip if depth/stencil not actually accessed.
if (reason == UpdateDepthFeedbackLoopReason::None)
{
return vk::ResourceAccess::Unused;
}
// Note that clear commands don't respect stencil test enable, only the mask
// Note Other state can be stated here too in the future, such as rasterizer discard.
if (!dsState.stencilTest && reason != UpdateDepthFeedbackLoopReason::Clear)
{
return vk::ResourceAccess::Unused;
}
return dsState.isStencilNoOp() && dsState.isStencilBackNoOp() ? vk::ResourceAccess::ReadOnly
: vk::ResourceAccess::ReadWrite;
}
egl::ContextPriority GetContextPriority(const gl::State &state)
{
return egl::FromEGLenum<egl::ContextPriority>(state.getContextPriority());
}
bool IsStencilSamplerBinding(const gl::ProgramExecutable &executable, size_t textureUnit)
{
const gl::SamplerFormat format = executable.getSamplerFormatForTextureUnitIndex(textureUnit);
const bool isStencilTexture = format == gl::SamplerFormat::Unsigned;
return isStencilTexture;
}
vk::ImageLayout GetDepthStencilAttachmentImageReadLayout(const vk::ImageHelper &image,
gl::ShaderType firstShader)
{
const bool isDepthTexture =
image.hasRenderPassUsageFlag(vk::RenderPassUsage::DepthTextureSampler);
const bool isStencilTexture =
image.hasRenderPassUsageFlag(vk::RenderPassUsage::StencilTextureSampler);
const bool isDepthReadOnlyAttachment =
image.hasRenderPassUsageFlag(vk::RenderPassUsage::DepthReadOnlyAttachment);
const bool isStencilReadOnlyAttachment =
image.hasRenderPassUsageFlag(vk::RenderPassUsage::StencilReadOnlyAttachment);
const bool isFS = firstShader == gl::ShaderType::Fragment;
// Only called when at least one aspect of the image is bound as texture
ASSERT(isDepthTexture || isStencilTexture);
// Check for feedback loop; this is when depth or stencil is both bound as a texture and is used
// in a non-read-only way as attachment.
if ((isDepthTexture && !isDepthReadOnlyAttachment) ||
(isStencilTexture && !isStencilReadOnlyAttachment))
{
return isFS ? vk::ImageLayout::DepthStencilFragmentShaderFeedback
: vk::ImageLayout::DepthStencilAllShadersFeedback;
}
if (isDepthReadOnlyAttachment)
{
if (isStencilReadOnlyAttachment)
{
// Depth read + stencil read
return isFS ? vk::ImageLayout::DepthReadStencilReadFragmentShaderRead
: vk::ImageLayout::DepthReadStencilReadAllShadersRead;
}
else
{
// Depth read + stencil write
return isFS ? vk::ImageLayout::DepthReadStencilWriteFragmentShaderDepthRead
: vk::ImageLayout::DepthReadStencilWriteAllShadersDepthRead;
}
}
else
{
if (isStencilReadOnlyAttachment)
{
// Depth write + stencil read
return isFS ? vk::ImageLayout::DepthWriteStencilReadFragmentShaderStencilRead
: vk::ImageLayout::DepthWriteStencilReadAllShadersStencilRead;
}
else
{
// Depth write + stencil write: This is definitely a feedback loop and is handled above.
UNREACHABLE();
return vk::ImageLayout::DepthStencilAllShadersFeedback;
}
}
}
vk::ImageLayout GetImageReadLayout(TextureVk *textureVk,
const gl::ProgramExecutable &executable,
size_t textureUnit,
PipelineType pipelineType)
{
vk::ImageHelper &image = textureVk->getImage();
// If this texture has been bound as image and the current executable program accesses images,
// we consider this image's layout as writeable.
if (textureVk->hasBeenBoundAsImage() && executable.hasImages())
{
return pipelineType == PipelineType::Compute ? vk::ImageLayout::ComputeShaderWrite
: vk::ImageLayout::AllGraphicsShadersWrite;
}
gl::ShaderBitSet remainingShaderBits =
executable.getSamplerShaderBitsForTextureUnitIndex(textureUnit);
ASSERT(remainingShaderBits.any());
gl::ShaderType firstShader = remainingShaderBits.first();
gl::ShaderType lastShader = remainingShaderBits.last();
remainingShaderBits.reset(firstShader);
remainingShaderBits.reset(lastShader);
const bool isFragmentShaderOnly = firstShader == gl::ShaderType::Fragment;
if (isFragmentShaderOnly)
{
ASSERT(remainingShaderBits.none() && lastShader == firstShader);
}
if (image.hasRenderPassUsageFlag(vk::RenderPassUsage::RenderTargetAttachment))
{
// Right now we set the *TextureSampler flag only when RenderTargetAttachment is set since
// we do not track all textures in the render pass.
if (image.isDepthOrStencil())
{
if (IsStencilSamplerBinding(executable, textureUnit))
{
image.setRenderPassUsageFlag(vk::RenderPassUsage::StencilTextureSampler);
}
else
{
image.setRenderPassUsageFlag(vk::RenderPassUsage::DepthTextureSampler);
}
return GetDepthStencilAttachmentImageReadLayout(image, firstShader);
}
image.setRenderPassUsageFlag(vk::RenderPassUsage::ColorTextureSampler);
return isFragmentShaderOnly ? vk::ImageLayout::ColorWriteFragmentShaderFeedback
: vk::ImageLayout::ColorWriteAllShadersFeedback;
}
if (image.isDepthOrStencil())
{
// We always use a depth-stencil read-only layout for any depth Textures to simplify
// our implementation's handling of depth-stencil read-only mode. We don't have to
// split a RenderPass to transition a depth texture from shader-read to read-only.
// This improves performance in Manhattan. Future optimizations are likely possible
// here including using specialized barriers without breaking the RenderPass.
return isFragmentShaderOnly ? vk::ImageLayout::DepthReadStencilReadFragmentShaderRead
: vk::ImageLayout::DepthReadStencilReadAllShadersRead;
}
// We barrier against either:
// - Vertex only
// - Fragment only
// - Pre-fragment only (vertex, geometry and tessellation together)
if (remainingShaderBits.any() || firstShader != lastShader)
{
return lastShader == gl::ShaderType::Fragment ? vk::ImageLayout::AllGraphicsShadersReadOnly
: vk::ImageLayout::PreFragmentShadersReadOnly;
}
return kShaderReadOnlyImageLayouts[firstShader];
}
vk::ImageLayout GetImageWriteLayoutAndSubresource(const gl::ImageUnit &imageUnit,
vk::ImageHelper &image,
gl::ShaderBitSet shaderStages,
gl::LevelIndex *levelOut,
uint32_t *layerStartOut,
uint32_t *layerCountOut)
{
*levelOut = gl::LevelIndex(static_cast<uint32_t>(imageUnit.level));
*layerStartOut = 0;
*layerCountOut = image.getLayerCount();
if (imageUnit.layered)
{
*layerStartOut = imageUnit.layered;
*layerCountOut = 1;
}
gl::ShaderType firstShader = shaderStages.first();
gl::ShaderType lastShader = shaderStages.last();
shaderStages.reset(firstShader);
shaderStages.reset(lastShader);
// We barrier against either:
// - Vertex only
// - Fragment only
// - Pre-fragment only (vertex, geometry and tessellation together)
if (shaderStages.any() || firstShader != lastShader)
{
return lastShader == gl::ShaderType::Fragment ? vk::ImageLayout::AllGraphicsShadersWrite
: vk::ImageLayout::PreFragmentShadersWrite;
}
return kShaderWriteImageLayouts[firstShader];
}
template <typename CommandBufferT>
void OnTextureBufferRead(ContextVk *contextVk,
vk::BufferHelper *buffer,
gl::ShaderBitSet stages,
CommandBufferT *commandBufferHelper)
{
ASSERT(stages.any());
// TODO: accept multiple stages in bufferRead. http://anglebug.com/3573
for (gl::ShaderType stage : stages)
{
// Note: if another range of the same buffer is simultaneously used for storage,
// such as for transform feedback output, or SSBO, unnecessary barriers can be
// generated.
commandBufferHelper->bufferRead(contextVk, VK_ACCESS_SHADER_READ_BIT,
vk::GetPipelineStage(stage), buffer);
}
}
void OnImageBufferWrite(ContextVk *contextVk,
BufferVk *bufferVk,
gl::ShaderBitSet stages,
vk::CommandBufferHelperCommon *commandBufferHelper)
{
vk::BufferHelper &buffer = bufferVk->getBuffer();
// TODO: accept multiple stages in bufferWrite. http://anglebug.com/3573
for (gl::ShaderType stage : stages)
{
commandBufferHelper->bufferWrite(contextVk,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
vk::GetPipelineStage(stage), &buffer);
}
}
constexpr angle::PackedEnumMap<RenderPassClosureReason, const char *> kRenderPassClosureReason = {{
{RenderPassClosureReason::AlreadySpecifiedElsewhere, nullptr},
{RenderPassClosureReason::ContextDestruction, "Render pass closed due to context destruction"},
{RenderPassClosureReason::ContextChange, "Render pass closed due to context change"},
{RenderPassClosureReason::GLFlush, "Render pass closed due to glFlush()"},
{RenderPassClosureReason::GLFinish, "Render pass closed due to glFinish()"},
{RenderPassClosureReason::EGLSwapBuffers, "Render pass closed due to eglSwapBuffers()"},
{RenderPassClosureReason::EGLWaitClient, "Render pass closed due to eglWaitClient()"},
{RenderPassClosureReason::SurfaceUnMakeCurrent,
"Render pass closed due to onSurfaceUnMakeCurrent()"},
{RenderPassClosureReason::FramebufferBindingChange,
"Render pass closed due to framebuffer binding change"},
{RenderPassClosureReason::FramebufferChange, "Render pass closed due to framebuffer change"},
{RenderPassClosureReason::NewRenderPass,
"Render pass closed due to starting a new render pass"},
{RenderPassClosureReason::BufferUseThenXfbWrite,
"Render pass closed due to buffer use as transform feedback output after prior use in render "
"pass"},
{RenderPassClosureReason::XfbWriteThenVertexIndexBuffer,
"Render pass closed due to transform feedback buffer use as vertex/index input"},
{RenderPassClosureReason::XfbWriteThenIndirectDrawBuffer,
"Render pass closed due to indirect draw buffer previously used as transform feedback output "
"in render pass"},
{RenderPassClosureReason::XfbResumeAfterDrawBasedClear,
"Render pass closed due to transform feedback resume after clear through draw"},
{RenderPassClosureReason::DepthStencilUseInFeedbackLoop,
"Render pass closed due to depth/stencil attachment use under feedback loop"},
{RenderPassClosureReason::DepthStencilWriteAfterFeedbackLoop,
"Render pass closed due to depth/stencil attachment write after feedback loop"},
{RenderPassClosureReason::PipelineBindWhileXfbActive,
"Render pass closed due to graphics pipeline change while transform feedback is active"},
{RenderPassClosureReason::BufferWriteThenMap,
"Render pass closed due to mapping buffer being written to by said render pass"},
{RenderPassClosureReason::BufferWriteThenOutOfRPRead,
"Render pass closed due to non-render-pass read of buffer that was written to in render pass"},
{RenderPassClosureReason::BufferUseThenOutOfRPWrite,
"Render pass closed due to non-render-pass write of buffer that was used in render pass"},
{RenderPassClosureReason::ImageUseThenOutOfRPRead,
"Render pass closed due to non-render-pass read of image that was used in render pass"},
{RenderPassClosureReason::ImageUseThenOutOfRPWrite,
"Render pass closed due to non-render-pass write of image that was used in render pass"},
{RenderPassClosureReason::XfbWriteThenComputeRead,
"Render pass closed due to compute read of buffer previously used as transform feedback "
"output in render pass"},
{RenderPassClosureReason::XfbWriteThenIndirectDispatchBuffer,
"Render pass closed due to indirect dispatch buffer previously used as transform feedback "
"output in render pass"},
{RenderPassClosureReason::ImageAttachmentThenComputeRead,
"Render pass closed due to compute read of image previously used as framebuffer attachment in "
"render pass"},
{RenderPassClosureReason::GetQueryResult, "Render pass closed due to getting query result"},
{RenderPassClosureReason::BeginNonRenderPassQuery,
"Render pass closed due to non-render-pass query begin"},
{RenderPassClosureReason::EndNonRenderPassQuery,
"Render pass closed due to non-render-pass query end"},
{RenderPassClosureReason::TimestampQuery, "Render pass closed due to timestamp query"},
{RenderPassClosureReason::EndRenderPassQuery,
"Render pass closed due to switch from query enabled draw to query disabled draw"},
{RenderPassClosureReason::GLReadPixels, "Render pass closed due to glReadPixels()"},
{RenderPassClosureReason::BufferUseThenReleaseToExternal,
"Render pass closed due to buffer (used by render pass) release to external"},
{RenderPassClosureReason::ImageUseThenReleaseToExternal,
"Render pass closed due to image (used by render pass) release to external"},
{RenderPassClosureReason::BufferInUseWhenSynchronizedMap,
"Render pass closed due to mapping buffer in use by GPU without GL_MAP_UNSYNCHRONIZED_BIT"},
{RenderPassClosureReason::GLMemoryBarrierThenStorageResource,
"Render pass closed due to glMemoryBarrier before storage output in render pass"},
{RenderPassClosureReason::StorageResourceUseThenGLMemoryBarrier,
"Render pass closed due to glMemoryBarrier after storage output in render pass"},
{RenderPassClosureReason::ExternalSemaphoreSignal,
"Render pass closed due to external semaphore signal"},
{RenderPassClosureReason::SyncObjectInit, "Render pass closed due to sync object insertion"},
{RenderPassClosureReason::SyncObjectWithFdInit,
"Render pass closed due to sync object with fd insertion"},
{RenderPassClosureReason::SyncObjectClientWait,
"Render pass closed due to sync object client wait"},
{RenderPassClosureReason::SyncObjectServerWait,
"Render pass closed due to sync object server wait"},
{RenderPassClosureReason::SyncObjectGetStatus,
"Render pass closed due to sync object get status"},
{RenderPassClosureReason::XfbPause, "Render pass closed due to transform feedback pause"},
{RenderPassClosureReason::FramebufferFetchEmulation,
"Render pass closed due to framebuffer fetch emulation"},
{RenderPassClosureReason::ColorBufferInvalidate,
"Render pass closed due to glInvalidateFramebuffer() on a color buffer"},
{RenderPassClosureReason::GenerateMipmapOnCPU,
"Render pass closed due to fallback to CPU when generating mipmaps"},
{RenderPassClosureReason::CopyTextureOnCPU,
"Render pass closed due to fallback to CPU when copying texture"},
{RenderPassClosureReason::TextureReformatToRenderable,
"Render pass closed due to reformatting texture to a renderable fallback"},
{RenderPassClosureReason::DeviceLocalBufferMap,
"Render pass closed due to mapping device local buffer"},
{RenderPassClosureReason::PrepareForBlit, "Render pass closed prior to draw-based blit"},
{RenderPassClosureReason::PrepareForImageCopy,
"Render pass closed prior to draw-based image copy"},
{RenderPassClosureReason::TemporaryForImageClear,
"Temporary render pass used for image clear closed"},
{RenderPassClosureReason::TemporaryForImageCopy,
"Temporary render pass used for image copy closed"},
{RenderPassClosureReason::TemporaryForOverlayDraw,
"Temporary render pass used for overlay draw closed"},
}};
VkDependencyFlags GetLocalDependencyFlags(ContextVk *contextVk)
{
VkDependencyFlags dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
if (contextVk->getCurrentViewCount() > 0)
{
dependencyFlags |= VK_DEPENDENCY_VIEW_LOCAL_BIT;
}
return dependencyFlags;
}
bool BlendModeSupportsDither(const ContextVk *contextVk, size_t colorIndex)
{
const gl::State &state = contextVk->getState();
// Specific combinations of color blend modes are known to work with our dithering emulation.
// Note we specifically don't check alpha blend, as dither isn't applied to alpha.
// See http://b/232574868 for more discussion and reasoning.
gl::BlendFactorType srcBlendFactor = state.getBlendStateExt().getSrcColorIndexed(colorIndex);
gl::BlendFactorType dstBlendFactor = state.getBlendStateExt().getDstColorIndexed(colorIndex);
const bool ditheringCompatibleBlendFactors =
(srcBlendFactor == gl::BlendFactorType::SrcAlpha &&
dstBlendFactor == gl::BlendFactorType::OneMinusSrcAlpha);
const bool allowAdditionalBlendFactors =
contextVk->getFeatures().enableAdditionalBlendFactorsForDithering.enabled &&
(srcBlendFactor == gl::BlendFactorType::One &&
dstBlendFactor == gl::BlendFactorType::OneMinusSrcAlpha);
return ditheringCompatibleBlendFactors || allowAdditionalBlendFactors;
}
bool ShouldUseGraphicsDriverUniformsExtended(const vk::Context *context)
{
return context->getFeatures().emulateTransformFeedback.enabled;
}
bool IsAnySamplesQuery(gl::QueryType type)
{
return type == gl::QueryType::AnySamples || type == gl::QueryType::AnySamplesConservative;
}
enum class GraphicsPipelineSubsetRenderPass
{
Unused,
Required,
};
template <typename Cache>
angle::Result CreateGraphicsPipelineSubset(ContextVk *contextVk,
const vk::GraphicsPipelineDesc &desc,
vk::GraphicsPipelineTransitionBits transition,
GraphicsPipelineSubsetRenderPass renderPass,
Cache *cache,
vk::PipelineCacheAccess *pipelineCache,
vk::PipelineHelper **pipelineOut)
{
const vk::PipelineLayout unusedPipelineLayout;
const vk::ShaderModuleMap unusedShaders;
const vk::SpecializationConstants unusedSpecConsts = {};
if (*pipelineOut != nullptr && !transition.any())
{
return angle::Result::Continue;
}
if (*pipelineOut != nullptr)
{
ASSERT((*pipelineOut)->valid());
if ((*pipelineOut)->findTransition(transition, desc, pipelineOut))
{
return angle::Result::Continue;
}
}
vk::PipelineHelper *oldPipeline = *pipelineOut;
const vk::GraphicsPipelineDesc *descPtr = nullptr;
if (!cache->getPipeline(desc, &descPtr, pipelineOut))
{
const vk::RenderPass unusedRenderPass;
const vk::RenderPass *compatibleRenderPass = &unusedRenderPass;
if (renderPass == GraphicsPipelineSubsetRenderPass::Required)
{
// Pull in a compatible RenderPass if used by this subset.
ANGLE_TRY(contextVk->getCompatibleRenderPass(desc.getRenderPassDesc(),
&compatibleRenderPass));
}
ANGLE_TRY(cache->createPipeline(contextVk, pipelineCache, *compatibleRenderPass,
unusedPipelineLayout, unusedShaders, unusedSpecConsts,
PipelineSource::Draw, desc, &descPtr, pipelineOut));
}
if (oldPipeline)
{
oldPipeline->addTransition(transition, descPtr, *pipelineOut);
}
return angle::Result::Continue;
}
bool QueueSerialsHaveDifferentIndexOrSmaller(const QueueSerial &queueSerial1,
const QueueSerial &queueSerial2)
{
return queueSerial1.getIndex() != queueSerial2.getIndex() || queueSerial1 < queueSerial2;
}
void UpdateImagesWithSharedCacheKey(const gl::ActiveTextureArray<TextureVk *> &activeImages,
const std::vector<gl::ImageBinding> &imageBindings,
const vk::SharedDescriptorSetCacheKey &sharedCacheKey)
{
for (const gl::ImageBinding &imageBinding : imageBindings)
{
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint imageUnit = imageBinding.boundImageUnits[arrayElement];
// For simplicity, we do not check if uniform is active or duplicate. The worst case is
// we unnecessarily delete the cache entry when image bound to inactive uniform is
// destroyed.
activeImages[imageUnit]->onNewDescriptorSet(sharedCacheKey);
}
}
}
void UpdateBufferWithSharedCacheKey(const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding,
VkDescriptorType descriptorType,
const vk::SharedDescriptorSetCacheKey &sharedCacheKey)
{
if (bufferBinding.get() != nullptr)
{
// For simplicity, we do not check if uniform is active or duplicate. The worst case is
// we unnecessarily delete the cache entry when buffer bound to inactive uniform is
// destroyed.
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
if (vk::IsDynamicDescriptor(descriptorType))
{
bufferHelper.getBufferBlock()->onNewDescriptorSet(sharedCacheKey);
}
else
{
bufferHelper.onNewDescriptorSet(sharedCacheKey);
}
}
}
void GenerateTextureUnitSamplerIndexMap(
const std::vector<GLuint> &samplerBoundTextureUnits,
std::unordered_map<size_t, uint32_t> *textureUnitSamplerIndexMapOut)
{
// Create a map of textureUnit <-> samplerIndex
for (size_t samplerIndex = 0; samplerIndex < samplerBoundTextureUnits.size(); samplerIndex++)
{
textureUnitSamplerIndexMapOut->insert(
{samplerBoundTextureUnits[samplerIndex], static_cast<uint32_t>(samplerIndex)});
}
}
} // anonymous namespace
void ContextVk::flushDescriptorSetUpdates()
{
mPerfCounters.writeDescriptorSets +=
mShareGroupVk->getUpdateDescriptorSetsBuilder()->flushDescriptorSetUpdates(getDevice());
}
ANGLE_INLINE void ContextVk::onRenderPassFinished(RenderPassClosureReason reason)
{
if (mRenderPassCommandBuffer != nullptr)
{
pauseRenderPassQueriesIfActive();
// If reason is specified, add it to the command buffer right before ending the render pass,
// so it will show up in GPU debuggers.
const char *reasonText = kRenderPassClosureReason[reason];
if (reasonText)
{
insertEventMarkerImpl(GL_DEBUG_SOURCE_API, reasonText);
}
mRenderPassCommandBuffer = nullptr;
// Restart at subpass 0.
mGraphicsPipelineDesc->resetSubpass(&mGraphicsPipelineTransition);
}
mGraphicsDirtyBits.set(DIRTY_BIT_RENDER_PASS);
}
// ContextVk implementation.
ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, vk::Renderer *renderer)
: ContextImpl(state, errorSet),
vk::Context(renderer),
mGraphicsDirtyBitHandlers{},
mComputeDirtyBitHandlers{},
mRenderPassCommandBuffer(nullptr),
mCurrentGraphicsPipeline(nullptr),
mCurrentGraphicsPipelineShaders(nullptr),
mCurrentGraphicsPipelineVertexInput(nullptr),
mCurrentGraphicsPipelineFragmentOutput(nullptr),
mCurrentComputePipeline(nullptr),
mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum),
mCurrentWindowSurface(nullptr),
mCurrentRotationDrawFramebuffer(SurfaceRotation::Identity),
mCurrentRotationReadFramebuffer(SurfaceRotation::Identity),
mActiveRenderPassQueries{},
mLastIndexBufferOffset(nullptr),
mCurrentIndexBufferOffset(0),
mCurrentDrawElementsType(gl::DrawElementsType::InvalidEnum),
mXfbBaseVertex(0),
mXfbVertexCountPerInstance(0),
mClearColorValue{},
mClearDepthStencilValue{},
mClearColorMasks(0),
mFlipYForCurrentSurface(false),
mFlipViewportForDrawFramebuffer(false),
mFlipViewportForReadFramebuffer(false),
mIsAnyHostVisibleBufferWritten(false),
mEmulateSeamfulCubeMapSampling(false),
mCurrentQueueSerialIndex(kInvalidQueueSerialIndex),
mOutsideRenderPassCommands(nullptr),
mRenderPassCommands(nullptr),
mQueryEventType(GraphicsEventCmdBuf::NotInQueryCmd),
mGpuEventsEnabled(false),
mPrimaryBufferEventCounter(0),
mHasDeferredFlush(false),
mHasAnyCommandsPendingSubmission(false),
mIsInFramebufferFetchMode(false),
mAllowRenderPassToReactivate(true),
mTotalBufferToImageCopySize(0),
mEstimatedPendingImageGarbageSize(0),
mHasWaitSemaphoresPendingSubmission(false),
mGpuClockSync{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()},
mGpuEventTimestampOrigin(0),
mInitialContextPriority(renderer->getDriverPriority(GetContextPriority(state))),
mContextPriority(mInitialContextPriority),
mProtectionType(vk::ConvertProtectionBoolToType(state.hasProtectedContent())),
mShareGroupVk(vk::GetImpl(state.getShareGroup()))
{
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::ContextVk");
memset(&mClearColorValue, 0, sizeof(mClearColorValue));
memset(&mClearDepthStencilValue, 0, sizeof(mClearDepthStencilValue));
memset(&mViewport, 0, sizeof(mViewport));
memset(&mScissor, 0, sizeof(mScissor));
// Ensure viewport is within Vulkan requirements
vk::ClampViewport(&mViewport);
mNonIndexedDirtyBitsMask.set();
mNonIndexedDirtyBitsMask.reset(DIRTY_BIT_INDEX_BUFFER);
mIndexedDirtyBitsMask.set();
// Once a command buffer is ended, all bindings (through |vkCmdBind*| calls) are lost per Vulkan
// spec. Once a new command buffer is allocated, we must make sure every previously bound
// resource is bound again.
//
// Note that currently these dirty bits are set every time a new render pass command buffer is
// begun. However, using ANGLE's SecondaryCommandBuffer, the Vulkan command buffer (which is
// the primary command buffer) is not ended, so technically we don't need to rebind these.
mNewGraphicsCommandBufferDirtyBits = DirtyBits{
DIRTY_BIT_RENDER_PASS, DIRTY_BIT_COLOR_ACCESS, DIRTY_BIT_DEPTH_STENCIL_ACCESS,
DIRTY_BIT_PIPELINE_BINDING, DIRTY_BIT_TEXTURES, DIRTY_BIT_VERTEX_BUFFERS,
DIRTY_BIT_INDEX_BUFFER, DIRTY_BIT_SHADER_RESOURCES, DIRTY_BIT_DESCRIPTOR_SETS,
DIRTY_BIT_DRIVER_UNIFORMS,
};
if (getFeatures().supportsTransformFeedbackExtension.enabled ||
getFeatures().emulateTransformFeedback.enabled)
{
mNewGraphicsCommandBufferDirtyBits.set(DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS);
}
mNewComputeCommandBufferDirtyBits =
DirtyBits{DIRTY_BIT_PIPELINE_BINDING, DIRTY_BIT_TEXTURES, DIRTY_BIT_SHADER_RESOURCES,
DIRTY_BIT_DESCRIPTOR_SETS, DIRTY_BIT_DRIVER_UNIFORMS};
mDynamicStateDirtyBits = DirtyBits{
DIRTY_BIT_DYNAMIC_VIEWPORT, DIRTY_BIT_DYNAMIC_SCISSOR,
DIRTY_BIT_DYNAMIC_LINE_WIDTH, DIRTY_BIT_DYNAMIC_DEPTH_BIAS,
DIRTY_BIT_DYNAMIC_BLEND_CONSTANTS, DIRTY_BIT_DYNAMIC_STENCIL_COMPARE_MASK,
DIRTY_BIT_DYNAMIC_STENCIL_WRITE_MASK, DIRTY_BIT_DYNAMIC_STENCIL_REFERENCE,
};
if (mRenderer->useVertexInputBindingStrideDynamicState() ||
getFeatures().supportsVertexInputDynamicState.enabled)
{
mDynamicStateDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
}
if (mRenderer->useCullModeDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_CULL_MODE);
}
if (mRenderer->useFrontFaceDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_FRONT_FACE);
}
if (mRenderer->useDepthTestEnableDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_DEPTH_TEST_ENABLE);
}
if (mRenderer->useDepthWriteEnableDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_DEPTH_WRITE_ENABLE);
}
if (mRenderer->useDepthCompareOpDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_DEPTH_COMPARE_OP);
}
if (mRenderer->useStencilTestEnableDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_STENCIL_TEST_ENABLE);
}
if (mRenderer->useStencilOpDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_STENCIL_OP);
}
if (mRenderer->usePrimitiveRestartEnableDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_PRIMITIVE_RESTART_ENABLE);
}
if (mRenderer->useRasterizerDiscardEnableDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_RASTERIZER_DISCARD_ENABLE);
}
if (mRenderer->useDepthBiasEnableDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_DEPTH_BIAS_ENABLE);
}
if (mRenderer->useLogicOpDynamicState())
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_LOGIC_OP);
}
if (getFeatures().supportsFragmentShadingRate.enabled)
{
mDynamicStateDirtyBits.set(DIRTY_BIT_DYNAMIC_FRAGMENT_SHADING_RATE);
}
mNewGraphicsCommandBufferDirtyBits |= mDynamicStateDirtyBits;
mGraphicsDirtyBitHandlers[DIRTY_BIT_MEMORY_BARRIER] =
&ContextVk::handleDirtyGraphicsMemoryBarrier;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DEFAULT_ATTRIBS] =
&ContextVk::handleDirtyGraphicsDefaultAttribs;
mGraphicsDirtyBitHandlers[DIRTY_BIT_PIPELINE_DESC] =
&ContextVk::handleDirtyGraphicsPipelineDesc;
mGraphicsDirtyBitHandlers[DIRTY_BIT_READ_ONLY_DEPTH_FEEDBACK_LOOP_MODE] =
&ContextVk::handleDirtyGraphicsReadOnlyDepthFeedbackLoopMode;
mGraphicsDirtyBitHandlers[DIRTY_BIT_ANY_SAMPLE_PASSED_QUERY_END] =
&ContextVk::handleDirtyAnySamplePassedQueryEnd;
mGraphicsDirtyBitHandlers[DIRTY_BIT_RENDER_PASS] = &ContextVk::handleDirtyGraphicsRenderPass;
mGraphicsDirtyBitHandlers[DIRTY_BIT_EVENT_LOG] = &ContextVk::handleDirtyGraphicsEventLog;
mGraphicsDirtyBitHandlers[DIRTY_BIT_COLOR_ACCESS] = &ContextVk::handleDirtyGraphicsColorAccess;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DEPTH_STENCIL_ACCESS] =
&ContextVk::handleDirtyGraphicsDepthStencilAccess;
mGraphicsDirtyBitHandlers[DIRTY_BIT_PIPELINE_BINDING] =
&ContextVk::handleDirtyGraphicsPipelineBinding;
mGraphicsDirtyBitHandlers[DIRTY_BIT_TEXTURES] = &ContextVk::handleDirtyGraphicsTextures;
mGraphicsDirtyBitHandlers[DIRTY_BIT_VERTEX_BUFFERS] =
&ContextVk::handleDirtyGraphicsVertexBuffers;
mGraphicsDirtyBitHandlers[DIRTY_BIT_INDEX_BUFFER] = &ContextVk::handleDirtyGraphicsIndexBuffer;
mGraphicsDirtyBitHandlers[DIRTY_BIT_UNIFORMS] = &ContextVk::handleDirtyGraphicsUniforms;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DRIVER_UNIFORMS] =
&ContextVk::handleDirtyGraphicsDriverUniforms;
mGraphicsDirtyBitHandlers[DIRTY_BIT_SHADER_RESOURCES] =
&ContextVk::handleDirtyGraphicsShaderResources;
mGraphicsDirtyBitHandlers[DIRTY_BIT_UNIFORM_BUFFERS] =
&ContextVk::handleDirtyGraphicsUniformBuffers;
mGraphicsDirtyBitHandlers[DIRTY_BIT_FRAMEBUFFER_FETCH_BARRIER] =
&ContextVk::handleDirtyGraphicsFramebufferFetchBarrier;
mGraphicsDirtyBitHandlers[DIRTY_BIT_BLEND_BARRIER] =
&ContextVk::handleDirtyGraphicsBlendBarrier;
if (getFeatures().supportsTransformFeedbackExtension.enabled)
{
mGraphicsDirtyBitHandlers[DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS] =
&ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension;
mGraphicsDirtyBitHandlers[DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME] =
&ContextVk::handleDirtyGraphicsTransformFeedbackResume;
}
else if (getFeatures().emulateTransformFeedback.enabled)
{
mGraphicsDirtyBitHandlers[DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS] =
&ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation;
}
mGraphicsDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] =
&ContextVk::handleDirtyGraphicsDescriptorSets;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_VIEWPORT] =
&ContextVk::handleDirtyGraphicsDynamicViewport;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_SCISSOR] =
&ContextVk::handleDirtyGraphicsDynamicScissor;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_LINE_WIDTH] =
&ContextVk::handleDirtyGraphicsDynamicLineWidth;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_DEPTH_BIAS] =
&ContextVk::handleDirtyGraphicsDynamicDepthBias;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_BLEND_CONSTANTS] =
&ContextVk::handleDirtyGraphicsDynamicBlendConstants;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_STENCIL_COMPARE_MASK] =
&ContextVk::handleDirtyGraphicsDynamicStencilCompareMask;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_STENCIL_WRITE_MASK] =
&ContextVk::handleDirtyGraphicsDynamicStencilWriteMask;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_STENCIL_REFERENCE] =
&ContextVk::handleDirtyGraphicsDynamicStencilReference;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_CULL_MODE] =
&ContextVk::handleDirtyGraphicsDynamicCullMode;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_FRONT_FACE] =
&ContextVk::handleDirtyGraphicsDynamicFrontFace;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_DEPTH_TEST_ENABLE] =
&ContextVk::handleDirtyGraphicsDynamicDepthTestEnable;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_DEPTH_WRITE_ENABLE] =
&ContextVk::handleDirtyGraphicsDynamicDepthWriteEnable;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_DEPTH_COMPARE_OP] =
&ContextVk::handleDirtyGraphicsDynamicDepthCompareOp;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_STENCIL_TEST_ENABLE] =
&ContextVk::handleDirtyGraphicsDynamicStencilTestEnable;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_STENCIL_OP] =
&ContextVk::handleDirtyGraphicsDynamicStencilOp;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_RASTERIZER_DISCARD_ENABLE] =
&ContextVk::handleDirtyGraphicsDynamicRasterizerDiscardEnable;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_DEPTH_BIAS_ENABLE] =
&ContextVk::handleDirtyGraphicsDynamicDepthBiasEnable;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_LOGIC_OP] =
&ContextVk::handleDirtyGraphicsDynamicLogicOp;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_PRIMITIVE_RESTART_ENABLE] =
&ContextVk::handleDirtyGraphicsDynamicPrimitiveRestartEnable;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DYNAMIC_FRAGMENT_SHADING_RATE] =
&ContextVk::handleDirtyGraphicsDynamicFragmentShadingRate;
mComputeDirtyBitHandlers[DIRTY_BIT_MEMORY_BARRIER] =
&ContextVk::handleDirtyComputeMemoryBarrier;
mComputeDirtyBitHandlers[DIRTY_BIT_EVENT_LOG] = &ContextVk::handleDirtyComputeEventLog;
mComputeDirtyBitHandlers[DIRTY_BIT_PIPELINE_DESC] = &ContextVk::handleDirtyComputePipelineDesc;
mComputeDirtyBitHandlers[DIRTY_BIT_PIPELINE_BINDING] =
&ContextVk::handleDirtyComputePipelineBinding;
mComputeDirtyBitHandlers[DIRTY_BIT_TEXTURES] = &ContextVk::handleDirtyComputeTextures;
mComputeDirtyBitHandlers[DIRTY_BIT_UNIFORMS] = &ContextVk::handleDirtyComputeUniforms;
mComputeDirtyBitHandlers[DIRTY_BIT_DRIVER_UNIFORMS] =
&ContextVk::handleDirtyComputeDriverUniforms;
mComputeDirtyBitHandlers[DIRTY_BIT_SHADER_RESOURCES] =
&ContextVk::handleDirtyComputeShaderResources;
mComputeDirtyBitHandlers[DIRTY_BIT_UNIFORM_BUFFERS] =
&ContextVk::handleDirtyComputeUniformBuffers;
mComputeDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] =
&ContextVk::handleDirtyComputeDescriptorSets;
mGraphicsDirtyBits = mNewGraphicsCommandBufferDirtyBits;
mComputeDirtyBits = mNewComputeCommandBufferDirtyBits;
FillWithNullptr(&mActiveImages);
// The following dirty bits don't affect the program pipeline:
//
// - READ_FRAMEBUFFER_BINDING only affects operations that read from said framebuffer,
// - CLEAR_* only affect following clear calls,
// - PACK/UNPACK_STATE only affect texture data upload/download,
// - *_BINDING only affect descriptor sets.
//
// Additionally, state that is set dynamically doesn't invalidate the program pipeline.
//
mPipelineDirtyBitsMask.set();
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_CLEAR_COLOR);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_CLEAR_DEPTH);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_CLEAR_STENCIL);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_UNPACK_STATE);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_UNPACK_BUFFER_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_PACK_STATE);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_PACK_BUFFER_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_RENDERBUFFER_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_SAMPLER_BINDINGS);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_TEXTURE_BINDINGS);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_IMAGE_BINDINGS);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING);
// Dynamic state in core Vulkan 1.0:
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_VIEWPORT);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_SCISSOR_TEST_ENABLED);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_SCISSOR);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_LINE_WIDTH);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_POLYGON_OFFSET);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_BLEND_COLOR);
if (!getFeatures().useNonZeroStencilWriteMaskStaticState.enabled)
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_STENCIL_WRITEMASK_FRONT);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_STENCIL_WRITEMASK_BACK);
}
// Dynamic state in VK_EXT_extended_dynamic_state:
if (mRenderer->useCullModeDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_CULL_FACE_ENABLED);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_CULL_FACE);
}
if (mRenderer->useFrontFaceDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_FRONT_FACE);
}
if (mRenderer->useDepthTestEnableDynamicState())
{
// Depth test affects depth write state too in GraphicsPipelineDesc, so the pipeline needs
// to stay dirty if depth test changes while depth write state is static.
if (mRenderer->useDepthWriteEnableDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_DEPTH_TEST_ENABLED);
}
}
if (mRenderer->useDepthWriteEnableDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_DEPTH_MASK);
}
if (mRenderer->useDepthCompareOpDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_DEPTH_FUNC);
}
if (mRenderer->useStencilTestEnableDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_STENCIL_TEST_ENABLED);
}
if (mRenderer->useStencilOpDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_STENCIL_OPS_FRONT);
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_STENCIL_OPS_BACK);
}
// Dynamic state in VK_EXT_extended_dynamic_state2:
if (mRenderer->usePrimitiveRestartEnableDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED);
}
if (mRenderer->useRasterizerDiscardEnableDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED);
}
if (mRenderer->useDepthBiasEnableDynamicState())
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED);
}
if (getFeatures().supportsVertexInputDynamicState.enabled)
{
mPipelineDirtyBitsMask.reset(gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING);
}
// Stash the mRefCountedEventRecycler in vk::Context for ImageHelper to conveniently access
mShareGroupRefCountedEventsGarbageRecycler =
mShareGroupVk->getRefCountedEventsGarbageRecycler();
mDeviceQueueIndex = renderer->getDeviceQueueIndex(mContextPriority);
angle::PerfMonitorCounterGroup vulkanGroup;
vulkanGroup.name = "vulkan";
#define ANGLE_ADD_PERF_MONITOR_COUNTER_GROUP(COUNTER) \
{ \
angle::PerfMonitorCounter counter; \
counter.name = #COUNTER; \
counter.value = 0; \
vulkanGroup.counters.push_back(counter); \
}
ANGLE_VK_PERF_COUNTERS_X(ANGLE_ADD_PERF_MONITOR_COUNTER_GROUP)
#undef ANGLE_ADD_PERF_MONITOR_COUNTER_GROUP
mPerfMonitorCounters.push_back(vulkanGroup);
}
ContextVk::~ContextVk() {}
void ContextVk::onDestroy(const gl::Context *context)
{
// If there is a context lost, destroy all the command buffers and resources regardless of
// whether they finished execution on GPU.
if (mRenderer->isDeviceLost())
{
mRenderer->handleDeviceLost();
}
// This will not destroy any resources. It will release them to be collected after finish.
mIncompleteTextures.onDestroy(context);
// Flush and complete current outstanding work before destruction.
(void)finishImpl(RenderPassClosureReason::ContextDestruction);
// The finish call could also generate device loss.
if (mRenderer->isDeviceLost())
{
mRenderer->handleDeviceLost();
}
// Everything must be finished
ASSERT(mRenderer->hasResourceUseFinished(mSubmittedResourceUse));
VkDevice device = getDevice();
mRenderer->getRefCountedEventRecycler()->destroy(getDevice());
mDefaultUniformStorage.release(mRenderer);
mEmptyBuffer.release(mRenderer);
for (vk::DynamicBuffer &defaultBuffer : mStreamedVertexBuffers)
{
defaultBuffer.destroy(mRenderer);
}
for (vk::DynamicQueryPool &queryPool : mQueryPools)
{
queryPool.destroy(device);
}
// Recycle current command buffers.
// Release functions are only used for Vulkan secondary command buffers.
mOutsideRenderPassCommands->releaseCommandPool();
mRenderPassCommands->releaseCommandPool();
// Detach functions are only used for ring buffer allocators.
mOutsideRenderPassCommands->detachAllocator();
mRenderPassCommands->detachAllocator();
mRenderer->recycleOutsideRenderPassCommandBufferHelper(&mOutsideRenderPassCommands);
mRenderer->recycleRenderPassCommandBufferHelper(&mRenderPassCommands);
mInterfacePipelinesCache.destroy(device);
mUtils.destroy(this);
mRenderPassCache.destroy(this);
mShaderLibrary.destroy(device);
mGpuEventQueryPool.destroy(device);
// Must retire all Vulkan secondary command buffers before destroying the pools.
if ((!vk::OutsideRenderPassCommandBuffer::ExecutesInline() ||
!vk::RenderPassCommandBuffer::ExecutesInline()) &&
mRenderer->isAsyncCommandBufferResetEnabled())
{
// This will also reset Primary command buffers which is REQUIRED on some buggy Vulkan
// implementations.
(void)mRenderer->retireFinishedCommands(this);
}
mCommandPools.outsideRenderPassPool.destroy(device);
mCommandPools.renderPassPool.destroy(device);
ASSERT(mCurrentGarbage.empty());
if (mCurrentQueueSerialIndex != kInvalidQueueSerialIndex)
{
releaseQueueSerialIndex();
}
mImageLoadContext = {};
}
VertexArrayVk *ContextVk::getVertexArray() const
{
return vk::GetImpl(mState.getVertexArray());
}
FramebufferVk *ContextVk::getDrawFramebuffer() const
{
return vk::GetImpl(mState.getDrawFramebuffer());
}
angle::Result ContextVk::getIncompleteTexture(const gl::Context *context,
gl::TextureType type,
gl::SamplerFormat format,
gl::Texture **textureOut)
{
return mIncompleteTextures.getIncompleteTexture(context, type, format, this, textureOut);
}
angle::Result ContextVk::initialize(const angle::ImageLoadContext &imageLoadContext)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::initialize");
mImageLoadContext = imageLoadContext;
ANGLE_TRY(mShareGroupVk->unifyContextsPriority(this));
ANGLE_TRY(mQueryPools[gl::QueryType::AnySamples].init(this, VK_QUERY_TYPE_OCCLUSION,
vk::kDefaultOcclusionQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::AnySamplesConservative].init(
this, VK_QUERY_TYPE_OCCLUSION, vk::kDefaultOcclusionQueryPoolSize));
// Only initialize the timestamp query pools if the extension is available.
if (mRenderer->getQueueFamilyProperties().timestampValidBits > 0)
{
ANGLE_TRY(mQueryPools[gl::QueryType::Timestamp].init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
ANGLE_TRY(mQueryPools[gl::QueryType::TimeElapsed].init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
}
if (getFeatures().supportsTransformFeedbackExtension.enabled)
{
ANGLE_TRY(mQueryPools[gl::QueryType::TransformFeedbackPrimitivesWritten].init(
this, VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT,
vk::kDefaultTransformFeedbackQueryPoolSize));
}
// If VK_EXT_primitives_generated_query is supported, use that to implement the OpenGL query.
// Otherwise, the primitives generated query is provided through the Vulkan pipeline statistics
// query if supported.
if (getFeatures().supportsPrimitivesGeneratedQuery.enabled)
{
ANGLE_TRY(mQueryPools[gl::QueryType::PrimitivesGenerated].init(
this, VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT,
vk::kDefaultPrimitivesGeneratedQueryPoolSize));
}
else if (getFeatures().supportsPipelineStatisticsQuery.enabled)
{
ANGLE_TRY(mQueryPools[gl::QueryType::PrimitivesGenerated].init(
this, VK_QUERY_TYPE_PIPELINE_STATISTICS, vk::kDefaultPrimitivesGeneratedQueryPoolSize));
}
// Init GLES to Vulkan index type map.
initIndexTypeMap();
mGraphicsPipelineDesc.reset(new vk::GraphicsPipelineDesc());
mGraphicsPipelineDesc->initDefaults(this, vk::GraphicsPipelineSubset::Complete,
pipelineRobustness(), pipelineProtectedAccess());
// Initialize current value/default attribute buffers.
for (vk::DynamicBuffer &buffer : mStreamedVertexBuffers)
{
buffer.init(mRenderer, kVertexBufferUsage, vk::kVertexBufferAlignment,
kDynamicVertexDataSize, true);
}
#if ANGLE_ENABLE_VULKAN_GPU_TRACE_EVENTS
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
ASSERT(platform);
// GPU tracing workaround for anglebug.com/2927. The renderer should not emit gpu events
// during platform discovery.
const unsigned char *gpuEventsEnabled =
platform->getTraceCategoryEnabledFlag(platform, "gpu.angle.gpu");
mGpuEventsEnabled = gpuEventsEnabled && *gpuEventsEnabled;
#endif
mEmulateSeamfulCubeMapSampling = shouldEmulateSeamfulCubeMapSampling();
// Assign initial command buffers from queue
ANGLE_TRY(vk::OutsideRenderPassCommandBuffer::InitializeCommandPool(
this, &mCommandPools.outsideRenderPassPool, mRenderer->getQueueFamilyIndex(),
getProtectionType()));
ANGLE_TRY(vk::RenderPassCommandBuffer::InitializeCommandPool(
this, &mCommandPools.renderPassPool, mRenderer->getQueueFamilyIndex(),
getProtectionType()));
ANGLE_TRY(mRenderer->getOutsideRenderPassCommandBufferHelper(
this, &mCommandPools.outsideRenderPassPool, &mOutsideRenderPassCommandsAllocator,
&mOutsideRenderPassCommands));
ANGLE_TRY(mRenderer->getRenderPassCommandBufferHelper(
this, &mCommandPools.renderPassPool, &mRenderPassCommandsAllocator, &mRenderPassCommands));
if (mGpuEventsEnabled)
{
// GPU events should only be available if timestamp queries are available.
ASSERT(mRenderer->getQueueFamilyProperties().timestampValidBits > 0);
// Calculate the difference between CPU and GPU clocks for GPU event reporting.
ANGLE_TRY(mGpuEventQueryPool.init(this, VK_QUERY_TYPE_TIMESTAMP,
vk::kDefaultTimestampQueryPoolSize));
ANGLE_TRY(synchronizeCpuGpuTime());
EventName eventName = GetTraceEventName("Primary", mPrimaryBufferEventCounter);
ANGLE_TRY(traceGpuEvent(&mOutsideRenderPassCommands->getCommandBuffer(),
TRACE_EVENT_PHASE_BEGIN, eventName));
}
size_t minAlignment = static_cast<size_t>(
mRenderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
mDefaultUniformStorage.init(mRenderer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, minAlignment,
mRenderer->getDefaultUniformBufferSize(), true);
// Initialize an "empty" buffer for use with default uniform blocks where there are no uniforms,
// or atomic counter buffer array indices that are unused.
constexpr VkBufferUsageFlags kEmptyBufferUsage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
VkBufferCreateInfo emptyBufferInfo = {};
emptyBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
emptyBufferInfo.flags = 0;
emptyBufferInfo.size = 16;
emptyBufferInfo.usage = kEmptyBufferUsage;
emptyBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
emptyBufferInfo.queueFamilyIndexCount = 0;
emptyBufferInfo.pQueueFamilyIndices = nullptr;
constexpr VkMemoryPropertyFlags kMemoryType = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
ANGLE_TRY(mEmptyBuffer.init(this, emptyBufferInfo, kMemoryType));
// If the share group has one context and is about to add the second one, the first context's
// mutable textures should be flushed.
if (isEligibleForMutableTextureFlush())
{
ASSERT(mShareGroupVk->getContexts().size() == 1);
for (auto context : mShareGroupVk->getContexts())
{
ANGLE_TRY(vk::GetImpl(context.second)->flushOutsideRenderPassCommands());
}
}
// Allocate queueSerial index and generate queue serial for commands.
ANGLE_TRY(allocateQueueSerialIndex());
// Initialize serials to be valid but appear submitted and finished.
mLastFlushedQueueSerial = QueueSerial(mCurrentQueueSerialIndex, Serial());
mLastSubmittedQueueSerial = mLastFlushedQueueSerial;
return angle::Result::Continue;
}
angle::Result ContextVk::flush(const gl::Context *context)
{
// Skip the flush if there's nothing recorded.
//
// Don't skip flushes for single-buffered windows with staged updates. It is expected that a
// flush call on a single-buffered window ensures any pending updates reach the screen.
const bool isSingleBufferedWindow =
mCurrentWindowSurface != nullptr && mCurrentWindowSurface->isSharedPresentMode();
const bool isSingleBufferedWindowWithStagedUpdates =
isSingleBufferedWindow && mCurrentWindowSurface->hasStagedUpdates();
if (!mHasAnyCommandsPendingSubmission && !hasActiveRenderPass() &&
mOutsideRenderPassCommands->empty() && !isSingleBufferedWindowWithStagedUpdates)
{
return angle::Result::Continue;
}
// Don't defer flushes when performing front buffer rendering. This can happen when -
// 1. we have a single-buffered window, in this mode the application is not required to
// call eglSwapBuffers(), and glFlush() is expected to ensure that work is submitted.
// 2. the framebuffer attachment has FRONT_BUFFER usage. Attachments being rendered to with such
// usage flags are expected to behave similar to a single-buffered window
FramebufferVk *drawFramebufferVk = getDrawFramebuffer();
ASSERT(drawFramebufferVk == vk::GetImpl(mState.getDrawFramebuffer()));
const bool frontBufferRenderingEnabled =
isSingleBufferedWindow || drawFramebufferVk->hasFrontBufferUsage();
if (mRenderer->getFeatures().deferFlushUntilEndRenderPass.enabled && hasActiveRenderPass() &&
!frontBufferRenderingEnabled)
{
mHasDeferredFlush = true;
return angle::Result::Continue;
}
if (isSingleBufferedWindow &&
mRenderer->getFeatures().swapbuffersOnFlushOrFinishWithSingleBuffer.enabled)
{
return mCurrentWindowSurface->onSharedPresentContextFlush(context);
}
return flushImpl(nullptr, nullptr, RenderPassClosureReason::GLFlush);
}
angle::Result ContextVk::finish(const gl::Context *context)
{
if (mRenderer->getFeatures().swapbuffersOnFlushOrFinishWithSingleBuffer.enabled &&
(mCurrentWindowSurface != nullptr) && mCurrentWindowSurface->isSharedPresentMode())
{
ANGLE_TRY(mCurrentWindowSurface->onSharedPresentContextFlush(context));
}
else
{
ANGLE_TRY(finishImpl(RenderPassClosureReason::GLFinish));
}
syncObjectPerfCounters(mRenderer->getCommandQueuePerfCounters());
return angle::Result::Continue;
}
angle::Result ContextVk::setupDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertexOrInvalid,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
DirtyBits dirtyBitMask)
{
// Set any dirty bits that depend on draw call parameters or other objects.
if (mode != mCurrentDrawMode)
{
invalidateCurrentGraphicsPipeline();
mCurrentDrawMode = mode;
mGraphicsPipelineDesc->updateTopology(&mGraphicsPipelineTransition, mCurrentDrawMode);
}
// Must be called before the command buffer is started. Can call finish.
VertexArrayVk *vertexArrayVk = getVertexArray();
if (vertexArrayVk->getStreamingVertexAttribsMask().any())
{
// All client attribs & any emulated buffered attribs will be updated
ANGLE_TRY(vertexArrayVk->updateStreamedAttribs(context, firstVertexOrInvalid,
vertexOrIndexCount, instanceCount,
indexTypeOrInvalid, indices));
mGraphicsDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
}
ProgramExecutableVk *executableVk = vk::GetImpl(mState.getProgramExecutable());
if (executableVk->hasDirtyUniforms())
{
mGraphicsDirtyBits.set(DIRTY_BIT_UNIFORMS);
}
// Update transform feedback offsets on every draw call when emulating transform feedback. This
// relies on the fact that no geometry/tessellation, indirect or indexed calls are supported in
// ES3.1 (and emulation is not done for ES3.2).
if (getFeatures().emulateTransformFeedback.enabled &&
mState.isTransformFeedbackActiveUnpaused())
{
ASSERT(firstVertexOrInvalid != -1);
mXfbBaseVertex = firstVertexOrInvalid;
mXfbVertexCountPerInstance = vertexOrIndexCount;
invalidateGraphicsDriverUniforms();
}
DirtyBits dirtyBits = mGraphicsDirtyBits & dirtyBitMask;
if (dirtyBits.any())
{
// Flush any relevant dirty bits.
for (DirtyBits::Iterator dirtyBitIter = dirtyBits.begin(); dirtyBitIter != dirtyBits.end();
++dirtyBitIter)
{
ASSERT(mGraphicsDirtyBitHandlers[*dirtyBitIter]);
ANGLE_TRY(
(this->*mGraphicsDirtyBitHandlers[*dirtyBitIter])(&dirtyBitIter, dirtyBitMask));
}
mGraphicsDirtyBits &= ~dirtyBitMask;
}
// Render pass must be always available at this point.
ASSERT(hasActiveRenderPass());
ASSERT(mState.getAndResetDirtyUniformBlocks().none());
return angle::Result::Continue;
}
angle::Result ContextVk::setupIndexedDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei indexCount,
GLsizei instanceCount,
gl::DrawElementsType indexType,
const void *indices)
{
ASSERT(mode != gl::PrimitiveMode::LineLoop);
if (indexType != mCurrentDrawElementsType)
{
mCurrentDrawElementsType = indexType;
ANGLE_TRY(onIndexBufferChange(nullptr));
}
VertexArrayVk *vertexArrayVk = getVertexArray();
const gl::Buffer *elementArrayBuffer = vertexArrayVk->getState().getElementArrayBuffer();
if (!elementArrayBuffer)
{
BufferBindingDirty bindingDirty;
ANGLE_TRY(vertexArrayVk->convertIndexBufferCPU(this, indexType, indexCount, indices,
&bindingDirty));
mCurrentIndexBufferOffset = 0;
// We only set dirty bit when the bound buffer actually changed.
if (bindingDirty == BufferBindingDirty::Yes)
{
mGraphicsDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
}
}
else
{
mCurrentIndexBufferOffset = reinterpret_cast<VkDeviceSize>(indices);
if (indices != mLastIndexBufferOffset)
{
mGraphicsDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
mLastIndexBufferOffset = indices;
}
// When you draw with LineLoop mode or GL_UNSIGNED_BYTE type, we may allocate its own
// element buffer and modify mCurrentElementArrayBuffer. When we switch out of that draw
// mode, we must reset mCurrentElementArrayBuffer back to the vertexArray's element buffer.
// Since in either case we set DIRTY_BIT_INDEX_BUFFER dirty bit, we use this bit to re-sync
// mCurrentElementArrayBuffer.
if (mGraphicsDirtyBits[DIRTY_BIT_INDEX_BUFFER])
{
vertexArrayVk->updateCurrentElementArrayBuffer();
}
if (shouldConvertUint8VkIndexType(indexType) && mGraphicsDirtyBits[DIRTY_BIT_INDEX_BUFFER])
{
ANGLE_VK_PERF_WARNING(this, GL_DEBUG_SEVERITY_LOW,
"Potential inefficiency emulating uint8 vertex attributes due to "
"lack of hardware support");
BufferVk *bufferVk = vk::GetImpl(elementArrayBuffer);
vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
if (bufferHelper.isHostVisible() &&
mRenderer->hasResourceUseFinished(bufferHelper.getResourceUse()))
{
uint8_t *src = nullptr;
ANGLE_TRY(
bufferVk->mapImpl(this, GL_MAP_READ_BIT, reinterpret_cast<void **>(&src)));
// Note: bufferOffset is not added here because mapImpl already adds it.
src += reinterpret_cast<uintptr_t>(indices);
const size_t byteCount = static_cast<size_t>(elementArrayBuffer->getSize()) -
reinterpret_cast<uintptr_t>(indices);
BufferBindingDirty bindingDirty;
ANGLE_TRY(vertexArrayVk->convertIndexBufferCPU(this, indexType, byteCount, src,
&bindingDirty));
ANGLE_TRY(bufferVk->unmapImpl(this));
}
else
{
ANGLE_TRY(vertexArrayVk->convertIndexBufferGPU(this, bufferVk, indices));
}
mCurrentIndexBufferOffset = 0;
}
}
return setupDraw(context, mode, 0, indexCount, instanceCount, indexType, indices,
mIndexedDirtyBitsMask);
}
angle::Result ContextVk::setupIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
DirtyBits dirtyBitMask,
vk::BufferHelper *indirectBuffer)
{
GLint firstVertex = -1;
GLsizei vertexCount = 0;
GLsizei instanceCount = 1;
// Break the render pass if the indirect buffer was previously used as the output from transform
// feedback.
if (mCurrentTransformFeedbackQueueSerial.valid() &&
indirectBuffer->writtenByCommandBuffer(mCurrentTransformFeedbackQueueSerial))
{
ANGLE_TRY(
flushCommandsAndEndRenderPass(RenderPassClosureReason::XfbWriteThenIndirectDrawBuffer));
}
ANGLE_TRY(setupDraw(context, mode, firstVertex, vertexCount, instanceCount,
gl::DrawElementsType::InvalidEnum, nullptr, dirtyBitMask));
// Process indirect buffer after render pass has started.
mRenderPassCommands->bufferRead(this, VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
vk::PipelineStage::DrawIndirect, indirectBuffer);
return angle::Result::Continue;
}
angle::Result ContextVk::setupIndexedIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType indexType,
vk::BufferHelper *indirectBuffer)
{
ASSERT(mode != gl::PrimitiveMode::LineLoop);
if (indexType != mCurrentDrawElementsType)
{
mCurrentDrawElementsType = indexType;
ANGLE_TRY(onIndexBufferChange(nullptr));
}
return setupIndirectDraw(context, mode, mIndexedDirtyBitsMask, indirectBuffer);
}
angle::Result ContextVk::setupLineLoopIndexedIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType indexType,
vk::BufferHelper *srcIndirectBuf,
VkDeviceSize indirectBufferOffset,
vk::BufferHelper **indirectBufferOut)
{
ASSERT(mode == gl::PrimitiveMode::LineLoop);
vk::BufferHelper *dstIndirectBuf = nullptr;
VertexArrayVk *vertexArrayVk = getVertexArray();
ANGLE_TRY(vertexArrayVk->handleLineLoopIndexIndirect(this, indexType, srcIndirectBuf,
indirectBufferOffset, &dstIndirectBuf));
*indirectBufferOut = dstIndirectBuf;
if (indexType != mCurrentDrawElementsType)
{
mCurrentDrawElementsType = indexType;
ANGLE_TRY(onIndexBufferChange(nullptr));
}
return setupIndirectDraw(context, mode, mIndexedDirtyBitsMask, dstIndirectBuf);
}
angle::Result ContextVk::setupLineLoopIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
vk::BufferHelper *indirectBuffer,
VkDeviceSize indirectBufferOffset,
vk::BufferHelper **indirectBufferOut)
{
ASSERT(mode == gl::PrimitiveMode::LineLoop);
vk::BufferHelper *indirectBufferHelperOut = nullptr;
VertexArrayVk *vertexArrayVk = getVertexArray();
ANGLE_TRY(vertexArrayVk->handleLineLoopIndirectDraw(
context, indirectBuffer, indirectBufferOffset, &indirectBufferHelperOut));
*indirectBufferOut = indirectBufferHelperOut;
if (gl::DrawElementsType::UnsignedInt != mCurrentDrawElementsType)
{
mCurrentDrawElementsType = gl::DrawElementsType::UnsignedInt;
ANGLE_TRY(onIndexBufferChange(nullptr));
}
return setupIndirectDraw(context, mode, mIndexedDirtyBitsMask, indirectBufferHelperOut);
}
angle::Result ContextVk::setupLineLoopDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertex,
GLsizei vertexOrIndexCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
uint32_t *numIndicesOut)
{
mCurrentIndexBufferOffset = 0;
VertexArrayVk *vertexArrayVk = getVertexArray();
ANGLE_TRY(vertexArrayVk->handleLineLoop(this, firstVertex, vertexOrIndexCount,
indexTypeOrInvalid, indices, numIndicesOut));
ANGLE_TRY(onIndexBufferChange(nullptr));
mCurrentDrawElementsType = indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum
? indexTypeOrInvalid
: gl::DrawElementsType::UnsignedInt;
return setupDraw(context, mode, firstVertex, vertexOrIndexCount, 1, indexTypeOrInvalid, indices,
mIndexedDirtyBitsMask);
}
angle::Result ContextVk::setupDispatch(const gl::Context *context)
{
// Note: numerous tests miss a glMemoryBarrier call between the initial texture data upload and
// the dispatch call. Flush the outside render pass command buffer as a workaround.
// TODO: Remove this and fix tests. http://anglebug.com/5070
ANGLE_TRY(flushOutsideRenderPassCommands());
ProgramExecutableVk *executableVk = vk::GetImpl(mState.getProgramExecutable());
if (executableVk->hasDirtyUniforms())
{
mComputeDirtyBits.set(DIRTY_BIT_UNIFORMS);
mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
DirtyBits dirtyBits = mComputeDirtyBits;
// Flush any relevant dirty bits.
for (DirtyBits::Iterator dirtyBitIter = dirtyBits.begin(); dirtyBitIter != dirtyBits.end();
++dirtyBitIter)
{
ASSERT(mComputeDirtyBitHandlers[*dirtyBitIter]);
ANGLE_TRY((this->*mComputeDirtyBitHandlers[*dirtyBitIter])(&dirtyBitIter));
}
mComputeDirtyBits.reset();
ASSERT(mState.getAndResetDirtyUniformBlocks().none());
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsMemoryBarrier(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyMemoryBarrierImpl(dirtyBitsIterator, dirtyBitMask);
}
angle::Result ContextVk::handleDirtyComputeMemoryBarrier(DirtyBits::Iterator *dirtyBitsIterator)
{
return handleDirtyMemoryBarrierImpl(nullptr, {});
}
bool ContextVk::renderPassUsesStorageResources() const
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
if (!hasActiveRenderPass())
{
return false;
}
// Storage images:
for (size_t imageUnitIndex : executable->getActiveImagesMask())
{
const gl::Texture *texture = mState.getImageUnit(imageUnitIndex).texture.get();
if (texture == nullptr)
{
continue;
}
TextureVk *textureVk = vk::GetImpl(texture);
if (texture->getType() == gl::TextureType::Buffer)
{
vk::BufferHelper &buffer = vk::GetImpl(textureVk->getBuffer().get())->getBuffer();
if (mRenderPassCommands->usesBuffer(buffer))
{
return true;
}
}
else
{
vk::ImageHelper &image = textureVk->getImage();
// Images only need to close the render pass if they need a layout transition. Outside
// render pass command buffer doesn't need closing as the layout transition barriers are
// recorded in sequence with the rest of the commands.
if (mRenderPassCommands->usesImage(image))
{
return true;
}
}
}
// Storage buffers:
const std::vector<gl::InterfaceBlock> &blocks = executable->getShaderStorageBlocks();
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const uint32_t binding = executable->getShaderStorageBlockBinding(bufferIndex);
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
mState.getIndexedShaderStorageBuffer(binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
vk::BufferHelper &buffer = vk::GetImpl(bufferBinding.get())->getBuffer();
if (mRenderPassCommands->usesBuffer(buffer))
{
return true;
}
}
// Atomic counters:
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
executable->getAtomicCounterBuffers();
for (uint32_t bufferIndex = 0; bufferIndex < atomicCounterBuffers.size(); ++bufferIndex)
{
const uint32_t binding = executable->getAtomicCounterBufferBinding(bufferIndex);
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
mState.getIndexedAtomicCounterBuffer(binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
vk::BufferHelper &buffer = vk::GetImpl(bufferBinding.get())->getBuffer();
if (mRenderPassCommands->usesBuffer(buffer))
{
return true;
}
}
return false;
}
angle::Result ContextVk::handleDirtyMemoryBarrierImpl(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
const bool hasImages = executable->hasImages();
const bool hasStorageBuffers = executable->hasStorageBuffers();
const bool hasAtomicCounters = executable->hasAtomicCounterBuffers();
if (!hasImages && !hasStorageBuffers && !hasAtomicCounters)
{
return angle::Result::Continue;
}
// Break the render pass if necessary. This is only needed for write-after-read situations, and
// is done by checking whether current storage buffers and images are used in the render pass.
if (renderPassUsesStorageResources())
{
// Either set later bits (if called during handling of graphics dirty bits), or set the
// dirty bits directly (if called during handling of compute dirty bits).
if (dirtyBitsIterator)
{
return flushDirtyGraphicsRenderPass(
dirtyBitsIterator, dirtyBitMask,
RenderPassClosureReason::GLMemoryBarrierThenStorageResource);
}
else
{
return flushCommandsAndEndRenderPass(
RenderPassClosureReason::GLMemoryBarrierThenStorageResource);
}
}
// Flushing outside render pass commands is cheap. If a memory barrier has been issued in its
// life time, just flush it instead of wasting time trying to figure out if it's necessary.
if (mOutsideRenderPassCommands->hasGLMemoryBarrierIssued())
{
ANGLE_TRY(flushOutsideRenderPassCommands());
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsEventLog(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyEventLogImpl(mRenderPassCommandBuffer);
}
angle::Result ContextVk::handleDirtyComputeEventLog(DirtyBits::Iterator *dirtyBitsIterator)
{
return handleDirtyEventLogImpl(&mOutsideRenderPassCommands->getCommandBuffer());
}
template <typename CommandBufferT>
angle::Result ContextVk::handleDirtyEventLogImpl(CommandBufferT *commandBuffer)
{
// This method is called when a draw or dispatch command is being processed. It's purpose is
// to call the vkCmd*DebugUtilsLabelEXT functions in order to communicate to debuggers
// (e.g. AGI) the OpenGL ES commands that the application uses.
// Exit early if no OpenGL ES commands have been logged, or if no command buffer (for a no-op
// draw), or if calling the vkCmd*DebugUtilsLabelEXT functions is not enabled.
if (mEventLog.empty() || commandBuffer == nullptr || !mRenderer->angleDebuggerMode())
{
return angle::Result::Continue;
}
// Insert OpenGL ES commands into debug label. We create a 3-level cascade here for
// OpenGL-ES-first debugging in AGI. Here's the general outline of commands:
// -glDrawCommand
// --vkCmdBeginDebugUtilsLabelEXT() #1 for "glDrawCommand"
// --OpenGL ES Commands
// ---vkCmdBeginDebugUtilsLabelEXT() #2 for "OpenGL ES Commands"
// ---Individual OpenGL ES Commands leading up to glDrawCommand
// ----vkCmdBeginDebugUtilsLabelEXT() #3 for each individual OpenGL ES Command
// ----vkCmdEndDebugUtilsLabelEXT() #3 for each individual OpenGL ES Command
// ----...More Individual OGL Commands...
// ----Final Individual OGL command will be the same glDrawCommand shown in #1 above
// ---vkCmdEndDebugUtilsLabelEXT() #2 for "OpenGL ES Commands"
// --VK SetupDraw & Draw-related commands will be embedded here under glDraw #1
// --vkCmdEndDebugUtilsLabelEXT() #1 is called after each vkDraw* or vkDispatch* call
// AGI desires no parameters on the top-level of the hierarchy.
std::string topLevelCommand = mEventLog.back();
size_t startOfParameters = topLevelCommand.find("(");
if (startOfParameters != std::string::npos)
{
topLevelCommand = topLevelCommand.substr(0, startOfParameters);
}
VkDebugUtilsLabelEXT label = {VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
nullptr,
topLevelCommand.c_str(),
{0.0f, 0.0f, 0.0f, 0.0f}};
// This is #1 from comment above
commandBuffer->beginDebugUtilsLabelEXT(label);
std::string oglCmds = "OpenGL ES Commands";
label.pLabelName = oglCmds.c_str();
// This is #2 from comment above
commandBuffer->beginDebugUtilsLabelEXT(label);
for (uint32_t i = 0; i < mEventLog.size(); ++i)
{
label.pLabelName = mEventLog[i].c_str();
// NOTE: We have to use a begin/end pair here because AGI does not promote the
// pLabelName from an insertDebugUtilsLabelEXT() call to the Commands panel.
// Internal bug b/169243237 is tracking this and once the insert* call shows the
// pLabelName similar to begin* call, we can switch these to insert* calls instead.
// This is #3 from comment above.
commandBuffer->beginDebugUtilsLabelEXT(label);
commandBuffer->endDebugUtilsLabelEXT();
}
commandBuffer->endDebugUtilsLabelEXT();
// The final end* call for #1 above is made in the ContextVk::draw* or
// ContextVk::dispatch* function calls.
mEventLog.clear();
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsDefaultAttribs(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
ASSERT(mDirtyDefaultAttribsMask.any());
gl::AttributesMask attribsMask =
mDirtyDefaultAttribsMask & mState.getProgramExecutable()->getAttributesMask();
VertexArrayVk *vertexArrayVk = getVertexArray();
for (size_t attribIndex : attribsMask)
{
ANGLE_TRY(vertexArrayVk->updateDefaultAttrib(this, attribIndex));
}
mDirtyDefaultAttribsMask.reset();
return angle::Result::Continue;
}
angle::Result ContextVk::createGraphicsPipeline()
{
ASSERT(mState.getProgramExecutable() != nullptr);
ProgramExecutableVk *executableVk = vk::GetImpl(mState.getProgramExecutable());
ASSERT(executableVk);
// Wait for any warmup task if necessary
executableVk->waitForPostLinkTasksIfNecessary(this, mGraphicsPipelineDesc.get());
vk::PipelineCacheAccess pipelineCache;
ANGLE_TRY(mRenderer->getPipelineCache(this, &pipelineCache));
vk::PipelineHelper *oldGraphicsPipeline = mCurrentGraphicsPipeline;
// Attempt to use an existing pipeline.
const vk::GraphicsPipelineDesc *descPtr = nullptr;
ANGLE_TRY(executableVk->getGraphicsPipeline(this, vk::GraphicsPipelineSubset::Complete,
*mGraphicsPipelineDesc, &descPtr,
&mCurrentGraphicsPipeline));
// If no such pipeline exists:
//
// - If VK_EXT_graphics_pipeline_library is not supported, create a new monolithic pipeline
// - If VK_EXT_graphics_pipeline_library is supported:
// * Create the Shaders subset of the pipeline through the program executable
// * Create the VertexInput and FragmentOutput subsets
// * Link them together through the program executable
if (mCurrentGraphicsPipeline == nullptr)
{
// Not found in cache
ASSERT(descPtr == nullptr);
if (!getFeatures().supportsGraphicsPipelineLibrary.enabled)
{
ANGLE_TRY(executableVk->createGraphicsPipeline(
this, vk::GraphicsPipelineSubset::Complete, &pipelineCache, PipelineSource::Draw,
*mGraphicsPipelineDesc, &descPtr, &mCurrentGraphicsPipeline));
}
else
{
const vk::GraphicsPipelineTransitionBits kShadersTransitionBitsMask =
vk::GetGraphicsPipelineTransitionBitsMask(vk::GraphicsPipelineSubset::Shaders);
const vk::GraphicsPipelineTransitionBits kVertexInputTransitionBitsMask =
vk::GetGraphicsPipelineTransitionBitsMask(vk::GraphicsPipelineSubset::VertexInput);
const vk::GraphicsPipelineTransitionBits kFragmentOutputTransitionBitsMask =
vk::GetGraphicsPipelineTransitionBitsMask(
vk::GraphicsPipelineSubset::FragmentOutput);
// Recreate the shaders subset if necessary
const vk::GraphicsPipelineTransitionBits shadersTransitionBits =
mGraphicsPipelineLibraryTransition & kShadersTransitionBitsMask;
if (mCurrentGraphicsPipelineShaders == nullptr || shadersTransitionBits.any())
{
bool shouldRecreatePipeline = true;
if (mCurrentGraphicsPipelineShaders != nullptr)
{
ASSERT(mCurrentGraphicsPipelineShaders->valid());
shouldRecreatePipeline = !mCurrentGraphicsPipelineShaders->findTransition(
shadersTransitionBits, *mGraphicsPipelineDesc,
&mCurrentGraphicsPipelineShaders);
}
if (shouldRecreatePipeline)
{
vk::PipelineHelper *oldGraphicsPipelineShaders =
mCurrentGraphicsPipelineShaders;
const vk::GraphicsPipelineDesc *shadersDescPtr = nullptr;
ANGLE_TRY(executableVk->getGraphicsPipeline(
this, vk::GraphicsPipelineSubset::Shaders, *mGraphicsPipelineDesc,
&shadersDescPtr, &mCurrentGraphicsPipelineShaders));
if (shadersDescPtr == nullptr)
{
ANGLE_TRY(executableVk->createGraphicsPipeline(
this, vk::GraphicsPipelineSubset::Shaders, &pipelineCache,
PipelineSource::Draw, *mGraphicsPipelineDesc, &shadersDescPtr,
&mCurrentGraphicsPipelineShaders));
}
if (oldGraphicsPipelineShaders)
{
oldGraphicsPipelineShaders->addTransition(
shadersTransitionBits, shadersDescPtr, mCurrentGraphicsPipelineShaders);
}
}
}
// If blobs are reused between the pipeline libraries and the monolithic pipelines (so
// |mergeProgramPipelineCachesToGlobalCache| would be enabled because merging the
// pipelines would be beneficial), directly use the global cache for the vertex input
// and fragment output pipelines. This _may_ cause stalls as the worker thread that
// creates pipelines is also holding the same lock.
//
// On the other hand, if there is not going to be any reuse of blobs, use a private
// pipeline cache to avoid the aforementioned potential stall.
vk::PipelineCacheAccess interfacePipelineCacheStorage;
vk::PipelineCacheAccess *interfacePipelineCache = &pipelineCache;
if (!getFeatures().mergeProgramPipelineCachesToGlobalCache.enabled)
{
ANGLE_TRY(ensureInterfacePipelineCache());
interfacePipelineCacheStorage.init(&mInterfacePipelinesCache, nullptr);
interfacePipelineCache = &interfacePipelineCacheStorage;
}
// Recreate the vertex input subset if necessary
ANGLE_TRY(CreateGraphicsPipelineSubset(
this, *mGraphicsPipelineDesc,
mGraphicsPipelineLibraryTransition & kVertexInputTransitionBitsMask,
GraphicsPipelineSubsetRenderPass::Unused,
mShareGroupVk->getVertexInputGraphicsPipelineCache(), interfacePipelineCache,
&mCurrentGraphicsPipelineVertexInput));
// Recreate the fragment output subset if necessary
ANGLE_TRY(CreateGraphicsPipelineSubset(
this, *mGraphicsPipelineDesc,
mGraphicsPipelineLibraryTransition & kFragmentOutputTransitionBitsMask,
GraphicsPipelineSubsetRenderPass::Required,
mShareGroupVk->getFragmentOutputGraphicsPipelineCache(), interfacePipelineCache,
&mCurrentGraphicsPipelineFragmentOutput));
// Link the three subsets into one pipeline.
ANGLE_TRY(executableVk->linkGraphicsPipelineLibraries(
this, &pipelineCache, *mGraphicsPipelineDesc, mCurrentGraphicsPipelineVertexInput,
mCurrentGraphicsPipelineShaders, mCurrentGraphicsPipelineFragmentOutput, &descPtr,
&mCurrentGraphicsPipeline));
// Reset the transition bits for pipeline libraries, they are only made to be up-to-date
// here.
mGraphicsPipelineLibraryTransition.reset();
}
}
// Maintain the transition cache
if (oldGraphicsPipeline)
{
oldGraphicsPipeline->addTransition(mGraphicsPipelineTransition, descPtr,
mCurrentGraphicsPipeline);
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const VkPipeline previousPipeline = mCurrentGraphicsPipeline
? mCurrentGraphicsPipeline->getPipeline().getHandle()
: VK_NULL_HANDLE;
// Accumulate transition bits for the sake of pipeline libraries. If a cache is hit in this
// path, |mGraphicsPipelineTransition| is reset while the partial pipelines are left stale. A
// future partial library recreation would need to know the bits that have changed since.
mGraphicsPipelineLibraryTransition |= mGraphicsPipelineTransition;
// Recreate the pipeline if necessary.
bool shouldRecreatePipeline =
mCurrentGraphicsPipeline == nullptr || mGraphicsPipelineTransition.any();
// If one can be found in the transition cache, recover it.
if (mCurrentGraphicsPipeline != nullptr && mGraphicsPipelineTransition.any())
{
ASSERT(mCurrentGraphicsPipeline->valid());
shouldRecreatePipeline = !mCurrentGraphicsPipeline->findTransition(
mGraphicsPipelineTransition, *mGraphicsPipelineDesc, &mCurrentGraphicsPipeline);
}
// Otherwise either retrieve the pipeline from the cache, or create a new one.
if (shouldRecreatePipeline)
{
ANGLE_TRY(createGraphicsPipeline());
}
mGraphicsPipelineTransition.reset();
// Update the queue serial for the pipeline object.
ASSERT(mCurrentGraphicsPipeline && mCurrentGraphicsPipeline->valid());
const VkPipeline newPipeline = mCurrentGraphicsPipeline->getPipeline().getHandle();
// If there's no change in pipeline, avoid rebinding it later. If the rebind is due to a new
// command buffer or UtilsVk, it will happen anyway with DIRTY_BIT_PIPELINE_BINDING.
if (newPipeline == previousPipeline)
{
return angle::Result::Continue;
}
// VK_EXT_transform_feedback disallows binding pipelines while transform feedback is active.
// If a new pipeline needs to be bound, the render pass should necessarily be broken (which
// implicitly pauses transform feedback), as resuming requires a barrier on the transform
// feedback counter buffer.
if (mRenderPassCommands->started())
{
mCurrentGraphicsPipeline->retainInRenderPass(mRenderPassCommands);
if (mRenderPassCommands->isTransformFeedbackActiveUnpaused())
{
ANGLE_TRY(
flushDirtyGraphicsRenderPass(dirtyBitsIterator, dirtyBitMask,
RenderPassClosureReason::PipelineBindWhileXfbActive));
dirtyBitsIterator->setLaterBit(DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME);
}
}
// The pipeline needs to rebind because it's changed.
dirtyBitsIterator->setLaterBit(DIRTY_BIT_PIPELINE_BINDING);
return angle::Result::Continue;
}
angle::Result ContextVk::updateRenderPassDepthFeedbackLoopMode(
UpdateDepthFeedbackLoopReason depthReason,
UpdateDepthFeedbackLoopReason stencilReason)
{
return switchOutReadOnlyDepthStencilMode(nullptr, {}, depthReason, stencilReason);
}
angle::Result ContextVk::switchOutReadOnlyDepthStencilMode(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask,
UpdateDepthFeedbackLoopReason depthReason,
UpdateDepthFeedbackLoopReason stencilReason)
{
FramebufferVk *drawFramebufferVk = getDrawFramebuffer();
if (!hasActiveRenderPass() || drawFramebufferVk->getDepthStencilRenderTarget() == nullptr)
{
return angle::Result::Continue;
}
const gl::DepthStencilState &dsState = mState.getDepthStencilState();
vk::ResourceAccess depthAccess = GetDepthAccess(dsState, depthReason);
vk::ResourceAccess stencilAccess = GetStencilAccess(dsState, stencilReason);
if ((HasResourceWriteAccess(depthAccess) &&
mDepthStencilAttachmentFlags[vk::RenderPassUsage::DepthReadOnlyAttachment]) ||
(HasResourceWriteAccess(stencilAccess) &&
mDepthStencilAttachmentFlags[vk::RenderPassUsage::StencilReadOnlyAttachment]))
{
// We should not in the actual feedback mode
ASSERT((mDepthStencilAttachmentFlags & vk::kDepthStencilFeedbackModeBits).none());
// If we are switching out of read only mode and we are in feedback loop, we must end
// render pass here. Otherwise, updating it to writeable layout will produce a writable
// feedback loop that is illegal in vulkan and will trigger validation errors that depth
// texture is using the writable layout.
if (dirtyBitsIterator)
{
ANGLE_TRY(flushDirtyGraphicsRenderPass(
dirtyBitsIterator, dirtyBitMask,
RenderPassClosureReason::DepthStencilWriteAfterFeedbackLoop));
}
else
{
ANGLE_TRY(flushCommandsAndEndRenderPass(
RenderPassClosureReason::DepthStencilWriteAfterFeedbackLoop));
}
// Clear read-only depth/stencil feedback mode.
mDepthStencilAttachmentFlags &= ~vk::kDepthStencilReadOnlyBits;
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsReadOnlyDepthFeedbackLoopMode(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return switchOutReadOnlyDepthStencilMode(dirtyBitsIterator, dirtyBitMask,
UpdateDepthFeedbackLoopReason::Draw,
UpdateDepthFeedbackLoopReason::Draw);
}
angle::Result ContextVk::handleDirtyAnySamplePassedQueryEnd(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
if (mRenderPassCommands->started())
{
// When we switch from query enabled draw to query disabled draw, we do immediate flush to
// ensure the query result will be ready early so that application thread calling
// getQueryResult gets unblocked sooner.
dirtyBitsIterator->setLaterBit(DIRTY_BIT_RENDER_PASS);
// Don't let next render pass end up reactivate and reuse the current render pass, which
// defeats the purpose of it.
mAllowRenderPassToReactivate = false;
mHasDeferredFlush = true;
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
FramebufferVk *drawFramebufferVk = getDrawFramebuffer();
gl::Rectangle renderArea = drawFramebufferVk->getRenderArea(this);
// Check to see if we can reactivate the current renderPass, if all arguments that we use to
// start the render pass is the same. We don't need to check clear values since mid render pass
// clear are handled differently.
bool reactivateStartedRenderPass =
hasStartedRenderPassWithQueueSerial(drawFramebufferVk->getLastRenderPassQueueSerial()) &&
mAllowRenderPassToReactivate && renderArea == mRenderPassCommands->getRenderArea();
if (reactivateStartedRenderPass)
{
INFO() << "Reactivate already started render pass on draw.";
mRenderPassCommandBuffer = &mRenderPassCommands->getCommandBuffer();
ASSERT(!drawFramebufferVk->hasDeferredClears());
ASSERT(hasActiveRenderPass());
ASSERT(drawFramebufferVk->getRenderPassDesc() == mRenderPassCommands->getRenderPassDesc());
return angle::Result::Continue;
}
// If the render pass needs to be recreated, close it using the special mid-dirty-bit-handling
// function, so later dirty bits can be set.
if (mRenderPassCommands->started())
{
ANGLE_TRY(flushDirtyGraphicsRenderPass(dirtyBitsIterator,
dirtyBitMask & ~DirtyBits{DIRTY_BIT_RENDER_PASS},
RenderPassClosureReason::AlreadySpecifiedElsewhere));
}
bool renderPassDescChanged = false;
ANGLE_TRY(startRenderPass(renderArea, nullptr, &renderPassDescChanged));
// The render pass desc can change when starting the render pass, for example due to
// multisampled-render-to-texture needs based on loadOps. In that case, recreate the graphics
// pipeline.
if (renderPassDescChanged)
{
ANGLE_TRY(handleDirtyGraphicsPipelineDesc(dirtyBitsIterator, dirtyBitMask));
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsColorAccess(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
FramebufferVk *drawFramebufferVk = getDrawFramebuffer();
const gl::FramebufferState &framebufferState = drawFramebufferVk->getState();
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
// Update color attachment accesses
vk::PackedAttachmentIndex colorIndexVk(0);
for (size_t colorIndexGL : framebufferState.getColorAttachmentsMask())
{
if (framebufferState.getEnabledDrawBuffers().test(colorIndexGL))
{
vk::ResourceAccess colorAccess = GetColorAccess(
mState, framebufferState, drawFramebufferVk->getEmulatedAlphaAttachmentMask(),
executable->usesFramebufferFetch(), colorIndexGL);
mRenderPassCommands->onColorAccess(colorIndexVk, colorAccess);
}
++colorIndexVk;
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsDepthStencilAccess(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const FramebufferVk &drawFramebufferVk = *getDrawFramebuffer();
if (drawFramebufferVk.getDepthStencilRenderTarget() == nullptr)
{
return angle::Result::Continue;
}
// Update depth/stencil attachment accesses
const gl::DepthStencilState &dsState = mState.getDepthStencilState();
vk::ResourceAccess depthAccess = GetDepthAccess(dsState, UpdateDepthFeedbackLoopReason::Draw);
vk::ResourceAccess stencilAccess =
GetStencilAccess(dsState, UpdateDepthFeedbackLoopReason::Draw);
mRenderPassCommands->onDepthAccess(depthAccess);
mRenderPassCommands->onStencilAccess(stencilAccess);
mRenderPassCommands->updateDepthReadOnlyMode(mDepthStencilAttachmentFlags);
mRenderPassCommands->updateStencilReadOnlyMode(mDepthStencilAttachmentFlags);
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
ASSERT(mCurrentGraphicsPipeline);
const vk::Pipeline *pipeline = nullptr;
ANGLE_TRY(mCurrentGraphicsPipeline->getPreferredPipeline(this, &pipeline));
mRenderPassCommandBuffer->bindGraphicsPipeline(*pipeline);
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyComputePipelineDesc(DirtyBits::Iterator *dirtyBitsIterator)
{
if (mCurrentComputePipeline == nullptr)
{
vk::PipelineCacheAccess pipelineCache;
ANGLE_TRY(mRenderer->getPipelineCache(this, &pipelineCache));
ProgramExecutableVk *executableVk = vk::GetImpl(mState.getProgramExecutable());
ASSERT(executableVk);
executableVk->waitForPostLinkTasksIfNecessary(this, nullptr);
ANGLE_TRY(executableVk->getOrCreateComputePipeline(
this, &pipelineCache, PipelineSource::Draw, pipelineRobustness(),
pipelineProtectedAccess(), &mCurrentComputePipeline));
}
ASSERT(mComputeDirtyBits.test(DIRTY_BIT_PIPELINE_BINDING));
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyComputePipelineBinding(DirtyBits::Iterator *dirtyBitsIterator)
{
ASSERT(mCurrentComputePipeline);
mOutsideRenderPassCommands->getCommandBuffer().bindComputePipeline(
mCurrentComputePipeline->getPipeline());
mOutsideRenderPassCommands->retainResource(mCurrentComputePipeline);
return angle::Result::Continue;
}
template <typename CommandBufferHelperT>
ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(
CommandBufferHelperT *commandBufferHelper,
PipelineType pipelineType)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask();
for (size_t textureUnit : activeTextures)
{
TextureVk *textureVk = mActiveTextures[textureUnit];
// If it's a texture buffer, get the attached buffer.
if (textureVk->getBuffer().get() != nullptr)
{
vk::BufferHelper *buffer = textureVk->getPossiblyEmulatedTextureBuffer(this);
const gl::ShaderBitSet stages =
executable->getSamplerShaderBitsForTextureUnitIndex(textureUnit);
OnTextureBufferRead(this, buffer, stages, commandBufferHelper);
textureVk->retainBufferViews(commandBufferHelper);
continue;
}
// The image should be flushed and ready to use at this point. There may still be
// lingering staged updates in its staging buffer for unused texture mip levels or
// layers. Therefore we can't verify it has no staged updates right here.
vk::ImageHelper &image = textureVk->getImage();
const vk::ImageLayout imageLayout =
GetImageReadLayout(textureVk, *executable, textureUnit, pipelineType);
// Ensure the image is in the desired layout
commandBufferHelper->imageRead(this, image.getAspectFlags(), imageLayout, &image);
}
if (executable->hasTextures())
{
ProgramExecutableVk *executableVk = vk::GetImpl(executable);
UpdatePreCacheActiveTextures(*executable, executable->getSamplerBindings(),
executable->getActiveSamplersMask(), mActiveTextures,
mState.getSamplers(), &mActiveTexturesDesc);
ANGLE_TRY(executableVk->updateTexturesDescriptorSet(
this, mActiveTextures, mState.getSamplers(), mEmulateSeamfulCubeMapSampling,
pipelineType, mShareGroupVk->getUpdateDescriptorSetsBuilder(), commandBufferHelper,
mActiveTexturesDesc));
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsTextures(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyTexturesImpl(mRenderPassCommands, PipelineType::Graphics);
}
angle::Result ContextVk::handleDirtyComputeTextures(DirtyBits::Iterator *dirtyBitsIterator)
{
return handleDirtyTexturesImpl(mOutsideRenderPassCommands, PipelineType::Compute);
}
angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
VertexArrayVk *vertexArrayVk = getVertexArray();
uint32_t maxAttrib = mState.getProgramExecutable()->getMaxActiveAttribLocation();
const gl::AttribArray<VkBuffer> &bufferHandles = vertexArrayVk->getCurrentArrayBufferHandles();
const gl::AttribArray<VkDeviceSize> &bufferOffsets =
vertexArrayVk->getCurrentArrayBufferOffsets();
if (mRenderer->useVertexInputBindingStrideDynamicState() ||
getFeatures().supportsVertexInputDynamicState.enabled)
{
const gl::AttribArray<GLuint> &bufferStrides =
vertexArrayVk->getCurrentArrayBufferStrides();
const gl::AttribArray<angle::FormatID> &bufferFormats =
vertexArrayVk->getCurrentArrayBufferFormats();
gl::AttribArray<VkDeviceSize> strides = {};
const gl::AttribArray<GLuint> &bufferDivisors =
vertexArrayVk->getCurrentArrayBufferDivisors();
const gl::AttribArray<GLuint> &bufferRelativeOffsets =
vertexArrayVk->getCurrentArrayBufferRelativeOffsets();
const gl::AttributesMask &bufferCompressed =
vertexArrayVk->getCurrentArrayBufferCompressed();
gl::AttribVector<VkVertexInputBindingDescription2EXT> bindingDescs;
gl::AttribVector<VkVertexInputAttributeDescription2EXT> attributeDescs;
// Set stride to 0 for mismatching formats between the program's declared attribute and that
// which is specified in glVertexAttribPointer. See comment in vk_cache_utils.cpp
// (initializePipeline) for more details.
const gl::AttributesMask &activeAttribLocations =
executable->getNonBuiltinAttribLocationsMask();
const gl::ComponentTypeMask &programAttribsTypeMask = executable->getAttributesTypeMask();
for (size_t attribIndex : activeAttribLocations)
{
const angle::Format &intendedFormat =
mRenderer->getFormat(bufferFormats[attribIndex]).getIntendedFormat();
const gl::ComponentType attribType = GetVertexAttributeComponentType(
intendedFormat.isPureInt(), intendedFormat.vertexAttribType);
const gl::ComponentType programAttribType =
gl::GetComponentTypeMask(programAttribsTypeMask, attribIndex);
const bool mismatchingType =
attribType != programAttribType && (programAttribType == gl::ComponentType::Float ||
attribType == gl::ComponentType::Float);
strides[attribIndex] = mismatchingType ? 0 : bufferStrides[attribIndex];
if (getFeatures().supportsVertexInputDynamicState.enabled)
{
VkVertexInputBindingDescription2EXT bindingDesc = {};
VkVertexInputAttributeDescription2EXT attribDesc = {};
bindingDesc.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT;
bindingDesc.binding = static_cast<uint32_t>(attribIndex);
bindingDesc.stride = static_cast<uint32_t>(strides[attribIndex]);
bindingDesc.divisor =
bufferDivisors[attribIndex] > mRenderer->getMaxVertexAttribDivisor()
? 1
: bufferDivisors[attribIndex];
if (bindingDesc.divisor != 0)
{
bindingDesc.inputRate =
static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_INSTANCE);
}
else
{
bindingDesc.inputRate =
static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_VERTEX);
// Divisor value is ignored by the implementation when using
// VK_VERTEX_INPUT_RATE_VERTEX, but it is set to 1 to avoid a validation error
// due to a validation layer issue.
bindingDesc.divisor = 1;
}
attribDesc.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT;
attribDesc.binding = static_cast<uint32_t>(attribIndex);
attribDesc.format = vk::GraphicsPipelineDesc::getPipelineVertexInputStateFormat(
this, bufferFormats[attribIndex], bufferCompressed[attribIndex],
programAttribType, static_cast<uint32_t>(attribIndex));
attribDesc.location = static_cast<uint32_t>(attribIndex);
attribDesc.offset = bufferRelativeOffsets[attribIndex];
bindingDescs.push_back(bindingDesc);
attributeDescs.push_back(attribDesc);
}
}
if (getFeatures().supportsVertexInputDynamicState.enabled)
{
mRenderPassCommandBuffer->setVertexInput(
static_cast<uint32_t>(bindingDescs.size()), bindingDescs.data(),
static_cast<uint32_t>(attributeDescs.size()), attributeDescs.data());
if (bindingDescs.size() != 0)
{
mRenderPassCommandBuffer->bindVertexBuffers(0, maxAttrib, bufferHandles.data(),
bufferOffsets.data());
}
}
else
{
// TODO: Use the sizes parameters here to fix the robustness issue worked around in
// crbug.com/1310038
mRenderPassCommandBuffer->bindVertexBuffers2(
0, maxAttrib, bufferHandles.data(), bufferOffsets.data(), nullptr, strides.data());
}
}
else
{
mRenderPassCommandBuffer->bindVertexBuffers(0, maxAttrib, bufferHandles.data(),
bufferOffsets.data());
}
const gl::AttribArray<vk::BufferHelper *> &arrayBufferResources =
vertexArrayVk->getCurrentArrayBuffers();
// Mark all active vertex buffers as accessed.
for (uint32_t attribIndex = 0; attribIndex < maxAttrib; ++attribIndex)
{
vk::BufferHelper *arrayBuffer = arrayBufferResources[attribIndex];
if (arrayBuffer)
{
mRenderPassCommands->bufferRead(this, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
vk::PipelineStage::VertexInput, arrayBuffer);
}
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsIndexBuffer(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
VertexArrayVk *vertexArrayVk = getVertexArray();
vk::BufferHelper *elementArrayBuffer = vertexArrayVk->getCurrentElementArrayBuffer();
ASSERT(elementArrayBuffer != nullptr);
VkDeviceSize bufferOffset;
const vk::Buffer &buffer = elementArrayBuffer->getBufferForVertexArray(
this, elementArrayBuffer->getSize(), &bufferOffset);
mRenderPassCommandBuffer->bindIndexBuffer(buffer, bufferOffset + mCurrentIndexBufferOffset,
getVkIndexType(mCurrentDrawElementsType));
mRenderPassCommands->bufferRead(this, VK_ACCESS_INDEX_READ_BIT, vk::PipelineStage::VertexInput,
elementArrayBuffer);
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsFramebufferFetchBarrier(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
VkMemoryBarrier memoryBarrier = {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
mRenderPassCommandBuffer->pipelineBarrier(
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
GetLocalDependencyFlags(this), 1, &memoryBarrier, 0, nullptr, 0, nullptr);
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsBlendBarrier(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
VkMemoryBarrier memoryBarrier = {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT;
mRenderPassCommandBuffer->pipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
GetLocalDependencyFlags(this), 1, &memoryBarrier, 0,
nullptr, 0, nullptr);
return angle::Result::Continue;
}
template <typename CommandBufferHelperT>
angle::Result ContextVk::handleDirtyShaderResourcesImpl(CommandBufferHelperT *commandBufferHelper,
PipelineType pipelineType,
DirtyBits::Iterator *dirtyBitsIterator)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
// DIRTY_BIT_UNIFORM_BUFFERS is set when uniform buffer bindings change.
// DIRTY_BIT_SHADER_RESOURCES gets set when the program executable has changed. In that case,
// this function will update entire the shader resource descriptorSet. This means there is no
// need to process uniform buffer bindings again.
dirtyBitsIterator->resetLaterBit(DIRTY_BIT_UNIFORM_BUFFERS);
// This function processes uniform buffers, so it doesn't matter which are dirty. The following
// makes sure the dirty bits are reset.
mState.getAndResetDirtyUniformBlocks();
const bool hasImages = executable->hasImages();
const bool hasStorageBuffers = executable->hasStorageBuffers();
const bool hasAtomicCounterBuffers = executable->hasAtomicCounterBuffers();
const bool hasUniformBuffers = executable->hasUniformBuffers();
const bool hasFramebufferFetch = executable->usesFramebufferFetch();
if (!hasUniformBuffers && !hasStorageBuffers && !hasAtomicCounterBuffers && !hasImages &&
!hasFramebufferFetch)
{
return angle::Result::Continue;
}
const VkPhysicalDeviceLimits &limits = mRenderer->getPhysicalDeviceProperties().limits;
ProgramExecutableVk *executableVk = vk::GetImpl(executable);
const ShaderInterfaceVariableInfoMap &variableInfoMap = executableVk->getVariableInfoMap();
mShaderBufferWriteDescriptorDescs = executableVk->getShaderResourceWriteDescriptorDescs();
// Update writeDescriptorDescs with inputAttachments
mShaderBufferWriteDescriptorDescs.updateInputAttachments(
*executable, variableInfoMap, vk::GetImpl(mState.getDrawFramebuffer()));
mShaderBuffersDescriptorDesc.resize(
mShaderBufferWriteDescriptorDescs.getTotalDescriptorCount());
if (hasUniformBuffers)
{
mShaderBuffersDescriptorDesc.updateShaderBuffers(
this, commandBufferHelper, *executable, variableInfoMap,
mState.getOffsetBindingPointerUniformBuffers(), executable->getUniformBlocks(),
executableVk->getUniformBufferDescriptorType(), limits.maxUniformBufferRange,
mEmptyBuffer, mShaderBufferWriteDescriptorDescs);
}
if (hasStorageBuffers)
{
mShaderBuffersDescriptorDesc.updateShaderBuffers(
this, commandBufferHelper, *executable, variableInfoMap,
mState.getOffsetBindingPointerShaderStorageBuffers(),
executable->getShaderStorageBlocks(), executableVk->getStorageBufferDescriptorType(),
limits.maxStorageBufferRange, mEmptyBuffer, mShaderBufferWriteDescriptorDescs);
}
if (hasAtomicCounterBuffers)
{
mShaderBuffersDescriptorDesc.updateAtomicCounters(
this, commandBufferHelper, *executable, variableInfoMap,
mState.getOffsetBindingPointerAtomicCounterBuffers(),
executable->getAtomicCounterBuffers(), limits.minStorageBufferOffsetAlignment,
mEmptyBuffer, mShaderBufferWriteDescriptorDescs);
}
if (hasImages)
{
ANGLE_TRY(updateActiveImages(commandBufferHelper));
ANGLE_TRY(mShaderBuffersDescriptorDesc.updateImages(this, *executable, variableInfoMap,
mActiveImages, mState.getImageUnits(),
mShaderBufferWriteDescriptorDescs));
}
if (hasFramebufferFetch)
{
ANGLE_TRY(mShaderBuffersDescriptorDesc.updateInputAttachments(
this, *executable, variableInfoMap, vk::GetImpl(mState.getDrawFramebuffer()),
mShaderBufferWriteDescriptorDescs));
}
vk::SharedDescriptorSetCacheKey newSharedCacheKey;
ANGLE_TRY(executableVk->updateShaderResourcesDescriptorSet(
this, mShareGroupVk->getUpdateDescriptorSetsBuilder(), mShaderBufferWriteDescriptorDescs,
commandBufferHelper, mShaderBuffersDescriptorDesc, &newSharedCacheKey));
if (newSharedCacheKey)
{
// A new cache entry has been created. We record this cache key in the images and buffers so
// that the descriptorSet cache can be destroyed when buffer/image is destroyed.
updateShaderResourcesWithSharedCacheKey(newSharedCacheKey);
}
// Record usage of storage buffers and images in the command buffer to aid handling of
// glMemoryBarrier.
if (hasImages || hasStorageBuffers || hasAtomicCounterBuffers)
{
commandBufferHelper->setHasShaderStorageOutput();
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsShaderResources(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyShaderResourcesImpl(mRenderPassCommands, PipelineType::Graphics,
dirtyBitsIterator);
}
angle::Result ContextVk::handleDirtyComputeShaderResources(DirtyBits::Iterator *dirtyBitsIterator)
{
return handleDirtyShaderResourcesImpl(mOutsideRenderPassCommands, PipelineType::Compute,
dirtyBitsIterator);
}
template <typename CommandBufferT>
angle::Result ContextVk::handleDirtyUniformBuffersImpl(CommandBufferT *commandBufferHelper)
{
gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
ASSERT(executable->hasUniformBuffers());
const VkPhysicalDeviceLimits &limits = mRenderer->getPhysicalDeviceProperties().limits;
ProgramExecutableVk *executableVk = vk::GetImpl(executable);
const ShaderInterfaceVariableInfoMap &variableInfoMap = executableVk->getVariableInfoMap();
gl::ProgramUniformBlockMask dirtyBits = mState.getAndResetDirtyUniformBlocks();
for (size_t blockIndex : dirtyBits)
{
const GLuint binding = executable->getUniformBlockBinding(blockIndex);
mShaderBuffersDescriptorDesc.updateOneShaderBuffer(
this, commandBufferHelper, variableInfoMap,
mState.getOffsetBindingPointerUniformBuffers(),
executable->getUniformBlocks()[blockIndex], binding,
executableVk->getUniformBufferDescriptorType(), limits.maxUniformBufferRange,
mEmptyBuffer, mShaderBufferWriteDescriptorDescs);
}
vk::SharedDescriptorSetCacheKey newSharedCacheKey;
ANGLE_TRY(executableVk->updateShaderResourcesDescriptorSet(
this, mShareGroupVk->getUpdateDescriptorSetsBuilder(), mShaderBufferWriteDescriptorDescs,
commandBufferHelper, mShaderBuffersDescriptorDesc, &newSharedCacheKey));
if (newSharedCacheKey)
{
// A new cache entry has been created. We record this cache key in the images and
// buffers so that the descriptorSet cache can be destroyed when buffer/image is
// destroyed.
updateShaderResourcesWithSharedCacheKey(newSharedCacheKey);
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsUniformBuffers(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyUniformBuffersImpl(mRenderPassCommands);
}
angle::Result ContextVk::handleDirtyComputeUniformBuffers(DirtyBits::Iterator *dirtyBitsIterator)
{
return handleDirtyUniformBuffersImpl(mOutsideRenderPassCommands);
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
if (!executable->hasTransformFeedbackOutput())
{
return angle::Result::Continue;
}
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(mState.getCurrentTransformFeedback());
if (mState.isTransformFeedbackActiveUnpaused())
{
size_t bufferCount = executable->getTransformFeedbackBufferCount();
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &bufferHelpers =