blob: 3bb326cf7b4817243a72cbc788a704ced748ad62 [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/utilities.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/Program.h"
#include "libANGLE/Semaphore.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/RendererVk.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/trace.h"
#include <iostream>
namespace rx
{
namespace
{
// For DesciptorSetUpdates
constexpr size_t kDescriptorBufferInfosInitialSize = 8;
constexpr size_t kDescriptorImageInfosInitialSize = 4;
constexpr size_t kDescriptorWriteInfosInitialSize =
kDescriptorBufferInfosInitialSize + kDescriptorImageInfosInitialSize;
// For shader uniforms such as gl_DepthRange and the viewport size.
struct GraphicsDriverUniforms
{
std::array<float, 4> viewport;
// 32 bits for 32 clip planes
uint32_t enabledClipPlanes;
uint32_t xfbActiveUnpaused;
int32_t xfbVerticesPerInstance;
// Used to replace gl_NumSamples. Because gl_NumSamples cannot be recognized in SPIR-V.
int32_t numSamples;
std::array<int32_t, 4> xfbBufferOffsets;
// .xy 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.
//
// .zw are unused.
std::array<uint32_t, 4> acbBufferOffsets;
// We'll use x, y, z for near / far / diff respectively.
std::array<float, 4> depthRange;
};
static_assert(sizeof(GraphicsDriverUniforms) % (sizeof(uint32_t) * 4) == 0,
"GraphicsDriverUniforms should 16bytes aligned");
// TODO: http://issuetracker.google.com/173636783 Once the bug is fixed, we should remove this.
struct GraphicsDriverUniformsExtended
{
GraphicsDriverUniforms common;
// Used to flip gl_FragCoord (both .xy for Android pre-rotation; only .y for desktop)
std::array<float, 2> halfRenderArea;
std::array<float, 2> flipXY;
std::array<float, 2> negFlipXY;
std::array<int32_t, 2> padding;
// Used to pre-rotate gl_FragCoord for swapchain images on Android (a mat2, which is padded to
// the size of two vec4's).
std::array<float, 8> fragRotation;
};
struct ComputeDriverUniforms
{
// Atomic counter buffer offsets with the same layout as in GraphicsDriverUniforms.
std::array<uint32_t, 4> acbBufferOffsets;
};
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;
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 kDefaultValueSize = sizeof(gl::VertexAttribCurrentValueData::Values);
constexpr size_t kDefaultBufferSize = kDefaultValueSize * 16;
constexpr size_t kDriverUniformsAllocatorPageSize = 4 * 1024;
uint32_t GetCoverageSampleCount(const gl::State &glState, FramebufferVk *drawFramebuffer)
{
if (!glState.isSampleCoverageEnabled())
{
return 0;
}
// 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() * drawFramebuffer->getSamples());
}
void ApplySampleCoverage(const gl::State &glState,
uint32_t coverageSampleCount,
uint32_t maskNumber,
uint32_t *maskOut)
{
if (!glState.isSampleCoverageEnabled())
{
return;
}
uint32_t maskBitOffset = maskNumber * 32;
uint32_t coverageMask = coverageSampleCount >= (maskBitOffset + 32)
? std::numeric_limits<uint32_t>::max()
: (1u << (coverageSampleCount - maskBitOffset)) - 1;
if (glState.getSampleCoverageInvert())
{
coverageMask = ~coverageMask;
}
*maskOut &= coverageMask;
}
bool IsRenderPassStartedAndUsesImage(const vk::CommandBufferHelper &renderPassCommands,
const vk::ImageHelper &image)
{
return renderPassCommands.started() && renderPassCommands.usesImageInRenderPass(image);
}
// When an Android surface is rotated differently than the device's native orientation, ANGLE must
// rotate gl_Position in the last pre-rasterization shader and gl_FragCoord in the fragment shader.
// Rotation of gl_Position is done in SPIR-V. The following are the rotation matrices for the
// fragment shader.
//
// Note: these are mat2's that are appropriately padded (4 floats per row).
using PreRotationMatrixValues = std::array<float, 8>;
constexpr angle::PackedEnumMap<rx::SurfaceRotation,
PreRotationMatrixValues,
angle::EnumSize<rx::SurfaceRotation>()>
kFragRotationMatrices = {
{{rx::SurfaceRotation::Identity, {{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotation::Rotated90Degrees,
{{0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotation::Rotated180Degrees,
{{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotation::Rotated270Degrees,
{{0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotation::FlippedIdentity, {{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotation::FlippedRotated90Degrees,
{{0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotation::FlippedRotated180Degrees,
{{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}}},
{rx::SurfaceRotation::FlippedRotated270Degrees,
{{0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}}}}};
bool IsRotatedAspectRatio(SurfaceRotation rotation)
{
return ((rotation == SurfaceRotation::Rotated90Degrees) ||
(rotation == SurfaceRotation::Rotated270Degrees) ||
(rotation == SurfaceRotation::FlippedRotated90Degrees) ||
(rotation == SurfaceRotation::FlippedRotated270Degrees));
}
SurfaceRotation DetermineSurfaceRotation(gl::Framebuffer *framebuffer,
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, uint32_t counter)
{
EventName buf;
snprintf(buf.data(), kMaxGpuEventNameLen - 1, "%s %u", title, counter);
return buf;
}
vk::ResourceAccess GetDepthAccess(const gl::DepthStencilState &dsState)
{
if (!dsState.depthTest)
{
return vk::ResourceAccess::Unused;
}
return dsState.isDepthMaskedOut() ? vk::ResourceAccess::ReadOnly : vk::ResourceAccess::Write;
}
vk::ResourceAccess GetStencilAccess(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
return vk::ResourceAccess::Unused;
}
return dsState.isStencilNoOp() && dsState.isStencilBackNoOp() ? vk::ResourceAccess::ReadOnly
: vk::ResourceAccess::Write;
}
egl::ContextPriority GetContextPriority(const gl::State &state)
{
return egl::FromEGLenum<egl::ContextPriority>(state.getContextPriority());
}
} // anonymous namespace
// Not necessary once upgraded to C++17.
constexpr ContextVk::DirtyBits ContextVk::kIndexAndVertexDirtyBits;
constexpr ContextVk::DirtyBits ContextVk::kPipelineDescAndBindingDirtyBits;
constexpr ContextVk::DirtyBits ContextVk::kTexturesAndDescSetDirtyBits;
constexpr ContextVk::DirtyBits ContextVk::kResourcesAndDescSetDirtyBits;
constexpr ContextVk::DirtyBits ContextVk::kXfbBuffersAndDescSetDirtyBits;
constexpr ContextVk::DirtyBits ContextVk::kDriverUniformsAndBindingDirtyBits;
ANGLE_INLINE void ContextVk::flushDescriptorSetUpdates()
{
if (mWriteDescriptorSets.empty())
{
ASSERT(mDescriptorBufferInfos.empty());
ASSERT(mDescriptorImageInfos.empty());
return;
}
vkUpdateDescriptorSets(getDevice(), static_cast<uint32_t>(mWriteDescriptorSets.size()),
mWriteDescriptorSets.data(), 0, nullptr);
mWriteDescriptorSets.clear();
mDescriptorBufferInfos.clear();
mDescriptorImageInfos.clear();
}
ANGLE_INLINE void ContextVk::onRenderPassFinished()
{
pauseRenderPassQueriesIfActive();
mRenderPassCommandBuffer = nullptr;
mGraphicsDirtyBits.set(DIRTY_BIT_RENDER_PASS);
}
// ContextVk::ScopedDescriptorSetUpdates implementation.
class ContextVk::ScopedDescriptorSetUpdates final : angle::NonCopyable
{
public:
ANGLE_INLINE ScopedDescriptorSetUpdates(ContextVk *contextVk) : mContextVk(contextVk) {}
ANGLE_INLINE ~ScopedDescriptorSetUpdates() { mContextVk->flushDescriptorSetUpdates(); }
private:
ContextVk *mContextVk;
};
ContextVk::DriverUniformsDescriptorSet::DriverUniformsDescriptorSet()
: descriptorSet(VK_NULL_HANDLE), dynamicOffset(0)
{}
ContextVk::DriverUniformsDescriptorSet::~DriverUniformsDescriptorSet() = default;
void ContextVk::DriverUniformsDescriptorSet::init(RendererVk *rendererVk)
{
size_t minAlignment = static_cast<size_t>(
rendererVk->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
dynamicBuffer.init(rendererVk, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, minAlignment,
kDriverUniformsAllocatorPageSize, true,
vk::DynamicBufferPolicy::FrequentSmallAllocations);
descriptorSetCache.clear();
}
void ContextVk::DriverUniformsDescriptorSet::destroy(RendererVk *renderer)
{
descriptorSetLayout.reset();
descriptorPoolBinding.reset();
dynamicBuffer.destroy(renderer);
descriptorSetCache.clear();
descriptorSetCache.destroy(renderer);
}
// ContextVk implementation.
ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk *renderer)
: ContextImpl(state, errorSet),
vk::Context(renderer),
mGraphicsDirtyBitHandlers{},
mComputeDirtyBitHandlers{},
mRenderPassCommandBuffer(nullptr),
mCurrentGraphicsPipeline(nullptr),
mCurrentComputePipeline(nullptr),
mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum),
mCurrentWindowSurface(nullptr),
mCurrentRotationDrawFramebuffer(SurfaceRotation::Identity),
mCurrentRotationReadFramebuffer(SurfaceRotation::Identity),
mActiveRenderPassQueries{},
mVertexArray(nullptr),
mDrawFramebuffer(nullptr),
mProgram(nullptr),
mExecutable(nullptr),
mLastIndexBufferOffset(nullptr),
mCurrentDrawElementsType(gl::DrawElementsType::InvalidEnum),
mXfbBaseVertex(0),
mXfbVertexCountPerInstance(0),
mClearColorValue{},
mClearDepthStencilValue{},
mClearColorMasks(0),
mFlipYForCurrentSurface(false),
mFlipViewportForDrawFramebuffer(false),
mFlipViewportForReadFramebuffer(false),
mIsAnyHostVisibleBufferWritten(false),
mEmulateSeamfulCubeMapSampling(false),
mOutsideRenderPassCommands(nullptr),
mRenderPassCommands(nullptr),
mGpuEventsEnabled(false),
mEGLSyncObjectPendingFlush(false),
mHasDeferredFlush(false),
mLastProgramUsesFramebufferFetch(false),
mGpuClockSync{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()},
mGpuEventTimestampOrigin(0),
mPerfCounters{},
mContextPerfCounters{},
mCumulativeContextPerfCounters{},
mContextPriority(renderer->getDriverPriority(GetContextPriority(state))),
mShareGroupVk(vk::GetImpl(state.getShareGroup()))
{
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::ContextVk");
memset(&mClearColorValue, 0, sizeof(mClearColorValue));
memset(&mClearDepthStencilValue, 0, sizeof(mClearDepthStencilValue));
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_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_BINDING};
if (getFeatures().supportsTransformFeedbackExtension.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_BINDING};
mGraphicsDirtyBitHandlers[DIRTY_BIT_MEMORY_BARRIER] =
&ContextVk::handleDirtyGraphicsMemoryBarrier;
mGraphicsDirtyBitHandlers[DIRTY_BIT_EVENT_LOG] = &ContextVk::handleDirtyGraphicsEventLog;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DEFAULT_ATTRIBS] =
&ContextVk::handleDirtyGraphicsDefaultAttribs;
mGraphicsDirtyBitHandlers[DIRTY_BIT_PIPELINE_DESC] =
&ContextVk::handleDirtyGraphicsPipelineDesc;
mGraphicsDirtyBitHandlers[DIRTY_BIT_RENDER_PASS] = &ContextVk::handleDirtyGraphicsRenderPass;
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_DRIVER_UNIFORMS] =
&ContextVk::handleDirtyGraphicsDriverUniforms;
mGraphicsDirtyBitHandlers[DIRTY_BIT_DRIVER_UNIFORMS_BINDING] =
&ContextVk::handleDirtyGraphicsDriverUniformsBinding;
mGraphicsDirtyBitHandlers[DIRTY_BIT_SHADER_RESOURCES] =
&ContextVk::handleDirtyGraphicsShaderResources;
mGraphicsDirtyBitHandlers[DIRTY_BIT_FRAMEBUFFER_FETCH_BARRIER] =
&ContextVk::handleDirtyGraphicsFramebufferFetchBarrier;
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;
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_DRIVER_UNIFORMS] =
&ContextVk::handleDirtyComputeDriverUniforms;
mComputeDirtyBitHandlers[DIRTY_BIT_DRIVER_UNIFORMS_BINDING] =
&ContextVk::handleDirtyComputeDriverUniformsBinding;
mComputeDirtyBitHandlers[DIRTY_BIT_SHADER_RESOURCES] =
&ContextVk::handleDirtyComputeShaderResources;
mComputeDirtyBitHandlers[DIRTY_BIT_DESCRIPTOR_SETS] =
&ContextVk::handleDirtyComputeDescriptorSets;
mGraphicsDirtyBits = mNewGraphicsCommandBufferDirtyBits;
mComputeDirtyBits = mNewComputeCommandBufferDirtyBits;
mActiveTextures.fill({nullptr, nullptr, true});
mActiveImages.fill(nullptr);
// 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.
//
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);
// Reserve reasonable amount of spaces so that for majority of apps we don't need to grow at all
mDescriptorBufferInfos.reserve(kDescriptorBufferInfosInitialSize);
mDescriptorImageInfos.reserve(kDescriptorImageInfosInitialSize);
mWriteDescriptorSets.reserve(kDescriptorWriteInfosInitialSize);
}
ContextVk::~ContextVk() = default;
void ContextVk::onDestroy(const gl::Context *context)
{
outputCumulativePerfCounters();
// Remove context from the share group
mShareGroupVk->getShareContextSet()->erase(this);
// 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();
VkDevice device = getDevice();
for (DriverUniformsDescriptorSet &driverUniforms : mDriverUniforms)
{
driverUniforms.destroy(mRenderer);
}
for (vk::DynamicDescriptorPool &dynamicDescriptorPool : mDriverUniformsDescriptorPools)
{
dynamicDescriptorPool.destroy(device);
}
mDefaultUniformStorage.release(mRenderer);
mEmptyBuffer.release(mRenderer);
mStagingBuffer.release(mRenderer);
for (vk::DynamicBuffer &defaultBuffer : mDefaultAttribBuffers)
{
defaultBuffer.destroy(mRenderer);
}
for (vk::DynamicQueryPool &queryPool : mQueryPools)
{
queryPool.destroy(device);
}
// Recycle current commands buffers.
mRenderer->recycleCommandBufferHelper(mOutsideRenderPassCommands);
mRenderer->recycleCommandBufferHelper(mRenderPassCommands);
mOutsideRenderPassCommands = nullptr;
mRenderPassCommands = nullptr;
mRenderer->releaseSharedResources(&mResourceUseList);
mUtils.destroy(mRenderer);
mRenderPassCache.destroy(mRenderer);
mShaderLibrary.destroy(device);
mGpuEventQueryPool.destroy(device);
mCommandPool.destroy(device);
ASSERT(mCurrentGarbage.empty());
ASSERT(mResourceUseList.empty());
}
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()
{
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::initialize");
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));
}
// Init gles to vulkan index type map
initIndexTypeMap();
// Init driver uniforms and get the descriptor set layouts.
constexpr angle::PackedEnumMap<PipelineType, VkShaderStageFlags> kPipelineStages = {
{PipelineType::Graphics, VK_SHADER_STAGE_ALL_GRAPHICS},
{PipelineType::Compute, VK_SHADER_STAGE_COMPUTE_BIT},
};
for (PipelineType pipeline : angle::AllEnums<PipelineType>())
{
mDriverUniforms[pipeline].init(mRenderer);
vk::DescriptorSetLayoutDesc desc =
getDriverUniformsDescriptorSetDesc(kPipelineStages[pipeline]);
ANGLE_TRY(getDescriptorSetLayoutCache().getDescriptorSetLayout(
this, desc, &mDriverUniforms[pipeline].descriptorSetLayout));
vk::DescriptorSetLayoutBindingVector bindingVector;
std::vector<VkSampler> immutableSamplers;
desc.unpackBindings(&bindingVector, &immutableSamplers);
std::vector<VkDescriptorPoolSize> descriptorPoolSizes;
for (const VkDescriptorSetLayoutBinding &binding : bindingVector)
{
if (binding.descriptorCount > 0)
{
VkDescriptorPoolSize poolSize = {};
poolSize.type = binding.descriptorType;
poolSize.descriptorCount = binding.descriptorCount;
descriptorPoolSizes.emplace_back(poolSize);
}
}
if (!descriptorPoolSizes.empty())
{
ANGLE_TRY(mDriverUniformsDescriptorPools[pipeline].init(
this, descriptorPoolSizes.data(), descriptorPoolSizes.size(),
mDriverUniforms[pipeline].descriptorSetLayout.get().getHandle()));
}
}
mGraphicsPipelineDesc.reset(new vk::GraphicsPipelineDesc());
mGraphicsPipelineDesc->initDefaults(this);
// Initialize current value/default attribute buffers.
for (vk::DynamicBuffer &buffer : mDefaultAttribBuffers)
{
buffer.init(mRenderer, kVertexBufferUsage, 1, kDefaultBufferSize, true,
vk::DynamicBufferPolicy::FrequentSmallAllocations);
}
#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
mOutsideRenderPassCommands = mRenderer->getCommandBufferHelper(false);
mRenderPassCommands = mRenderer->getCommandBufferHelper(true);
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());
mPerfCounters.primaryBuffers++;
EventName eventName = GetTraceEventName("Primary", mPerfCounters.primaryBuffers);
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,
vk::DynamicBufferPolicy::FrequentSmallAllocations);
// 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));
constexpr VkImageUsageFlags kStagingBufferUsageFlags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
size_t stagingBufferAlignment =
static_cast<size_t>(mRenderer->getPhysicalDeviceProperties().limits.minMemoryMapAlignment);
constexpr size_t kStagingBufferSize = 1024u * 1024u; // 1M
mStagingBuffer.init(mRenderer, kStagingBufferUsageFlags, stagingBufferAlignment,
kStagingBufferSize, true, vk::DynamicBufferPolicy::SporadicTextureUpload);
// Add context into the share group
mShareGroupVk->getShareContextSet()->insert(this);
return angle::Result::Continue;
}
angle::Result ContextVk::flush(const gl::Context *context)
{
// If a sync object has been used or this is a shared context, then we need to flush the
// commands and end the render pass to make sure the sync object (and any preceding commands)
// lands in the correct place within the command stream.
// EGL sync objects can span across context share groups, so don't defer flushes if there's one
// pending a flush.
if (getShareGroupVk()->isSyncObjectPendingFlush() && context->isShared() &&
!mEGLSyncObjectPendingFlush)
{
// Flush the commands to create a sync point in the command stream.
ANGLE_TRY(flushCommandsAndEndRenderPass());
// Move the resources to the share group, so they are released during the next vkQueueSubmit
// performed by any context in the share group. Note that this relies heavily on the global
// mutex to guarantee that no two contexts are modifying the lists at the same time.
getShareGroupVk()->acquireResourceUseList(std::move(mResourceUseList));
mHasDeferredFlush = true;
return angle::Result::Continue;
}
// EGL sync objects can span across context share groups, so don't defer flushes if there's one
// pending a flush.
if (!mEGLSyncObjectPendingFlush &&
mRenderer->getFeatures().deferFlushUntilEndRenderPass.enabled && hasStartedRenderPass())
{
mHasDeferredFlush = true;
return angle::Result::Continue;
}
return flushImpl(nullptr);
}
angle::Result ContextVk::finish(const gl::Context *context)
{
return finishImpl();
}
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.
if (mVertexArray->getStreamingVertexAttribsMask().any())
{
// All client attribs & any emulated buffered attribs will be updated
ANGLE_TRY(mVertexArray->updateStreamedAttribs(context, firstVertexOrInvalid,
vertexOrIndexCount, instanceCount,
indexTypeOrInvalid, indices));
mGraphicsDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
}
// Create a local object to ensure we flush the descriptor updates to device when we leave this
// function
ScopedDescriptorSetUpdates descriptorSetUpdates(this);
if (mProgram && mProgram->dirtyUniforms())
{
ANGLE_TRY(mProgram->updateUniforms(this));
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
else if (mProgramPipeline && mProgramPipeline->dirtyUniforms(getState()))
{
ANGLE_TRY(mProgramPipeline->updateUniforms(this));
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
// 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.none())
{
ASSERT(mRenderPassCommandBuffer);
return angle::Result::Continue;
}
// 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(mRenderPassCommandBuffer);
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));
}
const gl::Buffer *elementArrayBuffer = mVertexArray->getState().getElementArrayBuffer();
if (!elementArrayBuffer)
{
mGraphicsDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
ANGLE_TRY(mVertexArray->convertIndexBufferCPU(this, indexType, indexCount, indices));
}
else
{
if (indices != mLastIndexBufferOffset)
{
mGraphicsDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
mLastIndexBufferOffset = indices;
mVertexArray->updateCurrentElementArrayBufferOffset(mLastIndexBufferOffset);
}
if (shouldConvertUint8VkIndexType(indexType) && mGraphicsDirtyBits[DIRTY_BIT_INDEX_BUFFER])
{
ANGLE_PERF_WARNING(getDebug(), 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() &&
!bufferHelper.isCurrentlyInUse(getLastCompletedQueueSerial()))
{
uint8_t *src = nullptr;
ANGLE_TRY(bufferVk->mapImpl(this, reinterpret_cast<void **>(&src)));
src += reinterpret_cast<uintptr_t>(indices);
const size_t byteCount = static_cast<size_t>(elementArrayBuffer->getSize()) -
reinterpret_cast<uintptr_t>(indices);
ANGLE_TRY(mVertexArray->convertIndexBufferCPU(this, indexType, byteCount, src));
ANGLE_TRY(bufferVk->unmapImpl(this));
}
else
{
ANGLE_TRY(mVertexArray->convertIndexBufferGPU(this, bufferVk, indices));
}
}
}
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,
VkDeviceSize indirectBufferOffset)
{
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 (mCurrentTransformFeedbackBuffers.contains(indirectBuffer))
{
ANGLE_TRY(flushCommandsAndEndRenderPass());
}
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,
VkDeviceSize indirectBufferOffset)
{
ASSERT(mode != gl::PrimitiveMode::LineLoop);
if (indexType != mCurrentDrawElementsType)
{
mCurrentDrawElementsType = indexType;
ANGLE_TRY(onIndexBufferChange(nullptr));
}
return setupIndirectDraw(context, mode, mIndexedDirtyBitsMask, indirectBuffer,
indirectBufferOffset);
}
angle::Result ContextVk::setupLineLoopIndexedIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType indexType,
vk::BufferHelper *srcIndirectBuf,
VkDeviceSize indirectBufferOffset,
vk::BufferHelper **indirectBufferOut,
VkDeviceSize *indirectBufferOffsetOut)
{
ASSERT(mode == gl::PrimitiveMode::LineLoop);
vk::BufferHelper *dstIndirectBuf = nullptr;
VkDeviceSize dstIndirectBufOffset = 0;
ANGLE_TRY(mVertexArray->handleLineLoopIndexIndirect(this, indexType, srcIndirectBuf,
indirectBufferOffset, &dstIndirectBuf,
&dstIndirectBufOffset));
*indirectBufferOut = dstIndirectBuf;
*indirectBufferOffsetOut = dstIndirectBufOffset;
if (indexType != mCurrentDrawElementsType)
{
mCurrentDrawElementsType = indexType;
ANGLE_TRY(onIndexBufferChange(nullptr));
}
return setupIndirectDraw(context, mode, mIndexedDirtyBitsMask, dstIndirectBuf,
dstIndirectBufOffset);
}
angle::Result ContextVk::setupLineLoopIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
vk::BufferHelper *indirectBuffer,
VkDeviceSize indirectBufferOffset,
vk::BufferHelper **indirectBufferOut,
VkDeviceSize *indirectBufferOffsetOut)
{
ASSERT(mode == gl::PrimitiveMode::LineLoop);
vk::BufferHelper *indirectBufferHelperOut = nullptr;
ANGLE_TRY(mVertexArray->handleLineLoopIndirectDraw(
context, indirectBuffer, indirectBufferOffset, &indirectBufferHelperOut,
indirectBufferOffsetOut));
*indirectBufferOut = indirectBufferHelperOut;
if (gl::DrawElementsType::UnsignedInt != mCurrentDrawElementsType)
{
mCurrentDrawElementsType = gl::DrawElementsType::UnsignedInt;
ANGLE_TRY(onIndexBufferChange(nullptr));
}
return setupIndirectDraw(context, mode, mIndexedDirtyBitsMask, indirectBufferHelperOut,
*indirectBufferOffsetOut);
}
angle::Result ContextVk::setupLineLoopDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertex,
GLsizei vertexOrIndexCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
uint32_t *numIndicesOut)
{
ANGLE_TRY(mVertexArray->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());
// Create a local object to ensure we flush the descriptor updates to device when we leave this
// function
ScopedDescriptorSetUpdates descriptorSetUpdates(this);
if (mProgram && mProgram->dirtyUniforms())
{
ANGLE_TRY(mProgram->updateUniforms(this));
mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
else if (mProgramPipeline && mProgramPipeline->dirtyUniforms(getState()))
{
ANGLE_TRY(mProgramPipeline->updateUniforms(this));
mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
DirtyBits dirtyBits = mComputeDirtyBits;
// Flush any relevant dirty bits.
for (size_t dirtyBit : dirtyBits)
{
ASSERT(mComputeDirtyBitHandlers[dirtyBit]);
ANGLE_TRY((this->*mComputeDirtyBitHandlers[dirtyBit])());
}
mComputeDirtyBits.reset();
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsMemoryBarrier(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyMemoryBarrierImpl(dirtyBitsIterator, dirtyBitMask);
}
angle::Result ContextVk::handleDirtyComputeMemoryBarrier()
{
return handleDirtyMemoryBarrierImpl(nullptr, {});
}
bool ContextVk::renderPassUsesStorageResources() const
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
// 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 (IsRenderPassStartedAndUsesImage(*mRenderPassCommands, image))
{
return true;
}
}
}
gl::ShaderMap<const gl::ProgramState *> programStates;
mExecutable->fillProgramStateMap(this, &programStates);
for (const gl::ShaderType shaderType : executable->getLinkedShaderStages())
{
const gl::ProgramState *programState = programStates[shaderType];
ASSERT(programState);
// Storage buffers:
const std::vector<gl::InterfaceBlock> &blocks = programState->getShaderStorageBlocks();
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
mState.getIndexedShaderStorageBuffer(block.binding);
if (!block.isActive(shaderType) || 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 =
programState->getAtomicCounterBuffers();
for (uint32_t bufferIndex = 0; bufferIndex < atomicCounterBuffers.size(); ++bufferIndex)
{
uint32_t binding = atomicCounterBuffers[bufferIndex].binding;
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);
}
else
{
return flushCommandsAndEndRenderPass();
}
}
// 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()
{
return handleDirtyEventLogImpl(&mOutsideRenderPassCommands->getCommandBuffer());
}
angle::Result ContextVk::handleDirtyEventLogImpl(vk::CommandBuffer *commandBuffer)
{
if (mEventLog.empty())
{
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
VkDebugUtilsLabelEXT label = {VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
nullptr,
mEventLog.back().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());
for (size_t attribIndex : mDirtyDefaultAttribsMask)
{
ANGLE_TRY(updateDefaultAttribute(attribIndex));
}
mDirtyDefaultAttribsMask.reset();
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const VkPipeline previousPipeline = mCurrentGraphicsPipeline
? mCurrentGraphicsPipeline->getPipeline().getHandle()
: VK_NULL_HANDLE;
ASSERT(mExecutable);
if (!mCurrentGraphicsPipeline)
{
const vk::GraphicsPipelineDesc *descPtr;
// The desc's specialization constant depends on program's
// specConstUsageBits. We need to update it if program has changed.
SpecConstUsageBits usageBits = getCurrentProgramSpecConstUsageBits();
updateGraphicsPipelineDescWithSpecConstUsageBits(usageBits);
// Draw call shader patching, shader compilation, and pipeline cache query.
ANGLE_TRY(mExecutable->getGraphicsPipeline(
this, mCurrentDrawMode, *mGraphicsPipelineDesc,
mState.getProgramExecutable()->getNonBuiltinAttribLocationsMask(), &descPtr,
&mCurrentGraphicsPipeline));
mGraphicsPipelineTransition.reset();
}
else if (mGraphicsPipelineTransition.any())
{
ASSERT(mCurrentGraphicsPipeline->valid());
if (!mCurrentGraphicsPipeline->findTransition(
mGraphicsPipelineTransition, *mGraphicsPipelineDesc, &mCurrentGraphicsPipeline))
{
vk::PipelineHelper *oldPipeline = mCurrentGraphicsPipeline;
const vk::GraphicsPipelineDesc *descPtr;
ANGLE_TRY(mExecutable->getGraphicsPipeline(
this, mCurrentDrawMode, *mGraphicsPipelineDesc,
mState.getProgramExecutable()->getNonBuiltinAttribLocationsMask(), &descPtr,
&mCurrentGraphicsPipeline));
oldPipeline->addTransition(mGraphicsPipelineTransition, descPtr,
mCurrentGraphicsPipeline);
}
mGraphicsPipelineTransition.reset();
}
// Update the queue serial for the pipeline object.
ASSERT(mCurrentGraphicsPipeline && mCurrentGraphicsPipeline->valid());
// TODO: https://issuetracker.google.com/issues/169788986: Need to change this so that we get
// the actual serial used when this work is submitted.
mCurrentGraphicsPipeline->updateSerial(getCurrentQueueSerial());
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() && mRenderPassCommands->isTransformFeedbackActiveUnpaused())
{
ANGLE_TRY(flushDirtyGraphicsRenderPass(dirtyBitsIterator, dirtyBitMask));
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::handleDirtyGraphicsRenderPass(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
// 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}));
}
gl::Rectangle scissoredRenderArea = mDrawFramebuffer->getRotatedScissoredRenderArea(this);
bool renderPassDescChanged = false;
ANGLE_TRY(startRenderPass(scissoredRenderArea, 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::handleDirtyGraphicsPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
ASSERT(mCurrentGraphicsPipeline);
mRenderPassCommandBuffer->bindGraphicsPipeline(mCurrentGraphicsPipeline->getPipeline());
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyComputePipelineDesc()
{
if (!mCurrentComputePipeline)
{
ASSERT(mExecutable);
ANGLE_TRY(mExecutable->getComputePipeline(this, &mCurrentComputePipeline));
}
ASSERT(mComputeDirtyBits.test(DIRTY_BIT_PIPELINE_BINDING));
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyComputePipelineBinding()
{
ASSERT(mCurrentComputePipeline);
mOutsideRenderPassCommands->getCommandBuffer().bindComputePipeline(
mCurrentComputePipeline->get());
// TODO: https://issuetracker.google.com/issues/169788986: Need to change this so that we get
// the actual serial used when this work is submitted.
mCurrentComputePipeline->updateSerial(getCurrentQueueSerial());
return angle::Result::Continue;
}
ANGLE_INLINE angle::Result ContextVk::handleDirtyTexturesImpl(
vk::CommandBufferHelper *commandBufferHelper)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask();
for (size_t textureUnit : activeTextures)
{
const vk::TextureUnit &unit = mActiveTextures[textureUnit];
TextureVk *textureVk = unit.texture;
// If it's a texture buffer, get the attached buffer.
if (textureVk->getBuffer().get() != nullptr)
{
BufferVk *bufferVk = vk::GetImpl(textureVk->getBuffer().get());
vk::BufferHelper &buffer = bufferVk->getBuffer();
gl::ShaderBitSet stages =
executable->getSamplerShaderBitsForTextureUnitIndex(textureUnit);
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(this, VK_ACCESS_SHADER_READ_BIT,
vk::GetPipelineStage(stage), &buffer);
}
textureVk->retainBufferViews(&mResourceUseList);
continue;
}
vk::ImageHelper &image = textureVk->getImage();
// 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.
// Select the appropriate vk::ImageLayout depending on whether the texture is also bound as
// a GL image, and whether the program is a compute or graphics shader.
vk::ImageLayout textureLayout;
if (textureVk->hasBeenBoundAsImage())
{
textureLayout = executable->isCompute() ? vk::ImageLayout::ComputeShaderWrite
: vk::ImageLayout::AllGraphicsShadersWrite;
}
else 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.
textureLayout = vk::ImageLayout::DepthStencilReadOnly;
}
else
{
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);
if (image.hasRenderPassUseFlag(vk::RenderPassUsage::RenderTargetAttachment))
{
// Right now we set this flag only when RenderTargetAttachment is set since we do
// not track all textures in the renderpass.
image.setRenderPassUsageFlag(vk::RenderPassUsage::TextureSampler);
if (firstShader == gl::ShaderType::Fragment)
{
textureLayout = vk::ImageLayout::ColorAttachmentAndFragmentShaderRead;
}
else
{
textureLayout = vk::ImageLayout::ColorAttachmentAndAllShadersRead;
}
}
else
{
// We barrier against either:
// - Vertex only
// - Fragment only
// - Pre-fragment only (vertex, geometry and tessellation together)
if (remainingShaderBits.any() || firstShader != lastShader)
{
textureLayout = lastShader == gl::ShaderType::Fragment
? vk::ImageLayout::AllGraphicsShadersReadOnly
: vk::ImageLayout::PreFragmentShadersReadOnly;
}
else
{
textureLayout = kShaderReadOnlyImageLayouts[firstShader];
}
}
}
// Ensure the image is in the desired layout
commandBufferHelper->imageRead(this, image.getAspectFlags(), textureLayout, &image);
textureVk->retainImageViews(&mResourceUseList);
}
if (executable->hasTextures())
{
ANGLE_TRY(mExecutable->updateTexturesDescriptorSet(this));
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsTextures(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyTexturesImpl(mRenderPassCommands);
}
angle::Result ContextVk::handleDirtyComputeTextures()
{
return handleDirtyTexturesImpl(mOutsideRenderPassCommands);
}
angle::Result ContextVk::handleDirtyGraphicsVertexBuffers(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
uint32_t maxAttrib = mState.getProgramExecutable()->getMaxActiveAttribLocation();
const gl::AttribArray<VkBuffer> &bufferHandles = mVertexArray->getCurrentArrayBufferHandles();
const gl::AttribArray<VkDeviceSize> &bufferOffsets =
mVertexArray->getCurrentArrayBufferOffsets();
mRenderPassCommandBuffer->bindVertexBuffers(0, maxAttrib, bufferHandles.data(),
bufferOffsets.data());
const gl::AttribArray<vk::BufferHelper *> &arrayBufferResources =
mVertexArray->getCurrentArrayBuffers();
// Mark all active vertex buffers as accessed.
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
gl::AttributesMask attribsMask = executable->getActiveAttribLocationsMask();
for (size_t attribIndex : attribsMask)
{
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)
{
vk::BufferHelper *elementArrayBuffer = mVertexArray->getCurrentElementArrayBuffer();
ASSERT(elementArrayBuffer != nullptr);
mRenderPassCommandBuffer->bindIndexBuffer(elementArrayBuffer->getBuffer(),
mVertexArray->getCurrentElementArrayBufferOffset(),
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,
VK_DEPENDENCY_BY_REGION_BIT, 1, &memoryBarrier, 0, nullptr, 0, nullptr);
return angle::Result::Continue;
}
ANGLE_INLINE angle::Result ContextVk::handleDirtyShaderResourcesImpl(
vk::CommandBufferHelper *commandBufferHelper)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
const bool hasImages = executable->hasImages();
const bool hasStorageBuffers =
executable->hasStorageBuffers() || executable->hasAtomicCounterBuffers();
const bool hasUniformBuffers = executable->hasUniformBuffers();
if (hasImages)
{
ANGLE_TRY(updateActiveImages(commandBufferHelper));
}
if (hasUniformBuffers || hasStorageBuffers || hasImages || executable->usesFramebufferFetch())
{
ANGLE_TRY(mExecutable->updateShaderResourcesDescriptorSet(this, mDrawFramebuffer,
commandBufferHelper));
}
// Record usage of storage buffers and images in the command buffer to aid handling of
// glMemoryBarrier.
if (hasImages || hasStorageBuffers)
{
commandBufferHelper->setHasShaderStorageOutput();
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsShaderResources(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyShaderResourcesImpl(mRenderPassCommands);
}
angle::Result ContextVk::handleDirtyComputeShaderResources()
{
return handleDirtyShaderResourcesImpl(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 =
transformFeedbackVk->getBufferHelpers();
for (size_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
{
vk::BufferHelper *bufferHelper = bufferHelpers[bufferIndex];
ASSERT(bufferHelper);
mRenderPassCommands->bufferWrite(this, VK_ACCESS_SHADER_WRITE_BIT,
vk::PipelineStage::VertexShader,
vk::AliasingMode::Disallowed, bufferHelper);
}
}
// TODO(http://anglebug.com/3570): Need to update to handle Program Pipelines
vk::BufferHelper *uniformBuffer = mDefaultUniformStorage.getCurrentBuffer();
vk::UniformsAndXfbDescriptorDesc xfbBufferDesc =
transformFeedbackVk->getTransformFeedbackDesc();
xfbBufferDesc.updateDefaultUniformBuffer(uniformBuffer ? uniformBuffer->getBufferSerial()
: vk::kInvalidBufferSerial);
return mProgram->getExecutable().updateTransformFeedbackDescriptorSet(
mProgram->getState(), mProgram->getDefaultUniformBlocks(), uniformBuffer, this,
xfbBufferDesc);
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
const gl::ProgramExecutable *executable = mState.getProgramExecutable();
ASSERT(executable);
if (!executable->hasTransformFeedbackOutput() || !mState.isTransformFeedbackActive())
{
return angle::Result::Continue;
}
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(mState.getCurrentTransformFeedback());
size_t bufferCount = executable->getTransformFeedbackBufferCount();
const gl::TransformFeedbackBuffersArray<vk::BufferHelper *> &buffers =
transformFeedbackVk->getBufferHelpers();
gl::TransformFeedbackBuffersArray<vk::BufferHelper> &counterBuffers =
transformFeedbackVk->getCounterBufferHelpers();
// Issue necessary barriers for the transform feedback buffers.
for (size_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
{
vk::BufferHelper *bufferHelper = buffers[bufferIndex];
ASSERT(bufferHelper);
mRenderPassCommands->bufferWrite(this, VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT,
vk::PipelineStage::TransformFeedback,
vk::AliasingMode::Disallowed, bufferHelper);
}
// Issue necessary barriers for the transform feedback counter buffer. Note that the barrier is
// issued only on the first buffer (which uses a global memory barrier), as all the counter
// buffers of the transform feedback object are used together.
ASSERT(counterBuffers[0].valid());
mRenderPassCommands->bufferWrite(this,
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT |
VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT,
vk::PipelineStage::TransformFeedback,
vk::AliasingMode::Disallowed, &counterBuffers[0]);
const gl::TransformFeedbackBuffersArray<VkBuffer> &bufferHandles =
transformFeedbackVk->getBufferHandles();
const gl::TransformFeedbackBuffersArray<VkDeviceSize> &bufferOffsets =
transformFeedbackVk->getBufferOffsets();
const gl::TransformFeedbackBuffersArray<VkDeviceSize> &bufferSizes =
transformFeedbackVk->getBufferSizes();
mRenderPassCommandBuffer->bindTransformFeedbackBuffers(
static_cast<uint32_t>(bufferCount), bufferHandles.data(), bufferOffsets.data(),
bufferSizes.data());
if (!mState.isTransformFeedbackActiveUnpaused())
{
return angle::Result::Continue;
}
// We should have same number of counter buffers as xfb buffers have
const gl::TransformFeedbackBuffersArray<VkBuffer> &counterBufferHandles =
transformFeedbackVk->getCounterBufferHandles();
bool rebindBuffers = transformFeedbackVk->getAndResetBufferRebindState();
mRenderPassCommands->beginTransformFeedback(bufferCount, counterBufferHandles.data(),
rebindBuffers);
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackResume(
DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
if (mRenderPassCommands->isTransformFeedbackStarted())
{
mRenderPassCommands->resumeTransformFeedback();
}
return angle::Result::Continue;
}
angle::Result ContextVk::handleDirtyGraphicsDescriptorSets(DirtyBits::Iterator *dirtyBitsIterator,
DirtyBits dirtyBitMask)
{
return handleDirtyDescriptorSetsImpl(mRenderPassCommandBuffer);
}
angle::Result ContextVk::handleDirtyComputeDescriptorSets()
{
return handleDirtyDescriptorSetsImpl(&mOutsideRenderPassCommands->getCommandBuffer());
}
angle::Result ContextVk::handleDirtyDescriptorSetsImpl(vk::CommandBuffer *commandBuffer)
{
return mExecutable->updateDescriptorSets(this, commandBuffer);
}
void ContextVk::syncObjectPerfCounters()
{
uint32_t descriptorSetAllocations = 0;
// ContextVk's descriptor set allocations
ContextVkPerfCounters contextCounters = getAndResetObjectPerfCounters();
for (uint32_t count : contextCounters.descriptorSetsAllocated)
{
descriptorSetAllocations += count;
}
// UtilsVk's descriptor set allocations
descriptorSetAllocations += mUtils.getAndResetObjectPerfCounters().descriptorSetsAllocated;
// ProgramExecutableVk's descriptor set allocations
const gl::State &state = getState();
const gl::ShaderProgramManager &shadersAndPrograms = state.getShaderProgramManagerForCapture();
const gl::ResourceMap<gl::Program, gl::ShaderProgramID> &programs =
shadersAndPrograms.getProgramsForCaptureAndPerf();
for (const std::pair<GLuint, gl::Program *> &resource : programs)
{
ProgramVk *programVk = vk::GetImpl(resource.second);
ProgramExecutablePerfCounters progPerfCounters =
programVk->getExecutable().getAndResetObjectPerfCounters();
for (const uint32_t count : progPerfCounters.descriptorSetsAllocated)
{
descriptorSetAllocations += count;
}
}
mPerfCounters.descriptorSetAllocations = descriptorSetAllocations;
}
void ContextVk::updateOverlayOnPresent()
{
const gl::OverlayType *overlay = mState.getOverlay();
ASSERT(overlay->isEnabled());
syncObjectPerfCounters();
// Update overlay if active.
{
gl::RunningGraphWidget *renderPassCount =
overlay->getRunningGraphWidget(gl::WidgetId::VulkanRenderPassCount);
renderPassCount->add(mRenderPassCommands->getAndResetCounter());
renderPassCount->next();
}
{
gl::RunningGraphWidget *writeDescriptorSetCount =
overlay->getRunningGraphWidget(gl::WidgetId::VulkanWriteDescriptorSetCount);
writeDescriptorSetCount->add(mPerfCounters.writeDescriptorSets);
writeDescriptorSetCount->next();
mPerfCounters.writeDescriptorSets = 0;
}
{
gl::RunningGraphWidget *descriptorSetAllocationCount =
overlay->getRunningGraphWidget(gl::WidgetId::VulkanDescriptorSetAllocations);
descriptorSetAllocationCount->add(mPerfCounters.descriptorSetAllocations);
descriptorSetAllocationCount->next();
}
{
gl::RunningGraphWidget *dynamicBufferAllocations =
overlay->getRunningGraphWidget(gl::WidgetId::VulkanDynamicBufferAllocations);
dynamicBufferAllocations->next();
}
}
void ContextVk::addOverlayUsedBuffersCount(vk::CommandBufferHelper *commandBuffer)
{
const gl::OverlayType *overlay = mState.getOverlay();
if (!overlay->isEnabled())
{
return;
}
gl::RunningHistogramWidget *widget =
overlay->getRunningHistogramWidget(gl::WidgetId::VulkanRenderPassBufferCount);
size_t buffersCount = commandBuffer->getUsedBuffersCount();
if (buffersCount > 0)
{
widget->add(buffersCount);
widget->next();
}
}
angle::Result ContextVk::submitFrame(const vk::Semaphore *signalSemaphore)
{
if (mCurrentWindowSurface)
{
vk::Semaphore waitSemaphore = mCurrentWindowSurface->getAcquireImageSemaphore();
if (waitSemaphore.valid())
{
addWaitSemaphore(waitSemaphore.getHandle(),
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
addGarbage(&waitSemaphore);
}
}
if (vk::CommandBufferHelper::kEnableCommandStreamDiagnostics)
{
dumpCommandStreamDiagnostics();
}
getShareGroupVk()->acquireResourceUseList(std::move(mResourceUseList));
ANGLE_TRY(mRenderer->submitFrame(this, mContextPriority, std::move(mWaitSemaphores),
std::move(mWaitSemaphoreStageMasks), signalSemaphore,
getShareGroupVk()->releaseResourceUseLists(),
std::move(mCurrentGarbage), &mCommandPool));
onRenderPassFinished();
mComputeDirtyBits |= mNewComputeCommandBufferDirtyBits;
if (mGpuEventsEnabled)
{
ANGLE_TRY(checkCompletedGpuEvents());
}
return angle::Result::Continue;
}
angle::Result ContextVk::synchronizeCpuGpuTime()
{
ASSERT(mGpuEventsEnabled);
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
ASSERT(platform);
// To synchronize CPU and GPU times, we need to get the CPU timestamp as close as possible
// to the GPU timestamp. The process of getting the GPU timestamp is as follows:
//
// CPU GPU
//
// Record command buffer
// with timestamp query
//
// Submit command buffer
//
// Post-submission work Begin execution
//
// ???? Write timestamp Tgpu
//
// ???? End execution
//
// ???? Return query results
//
// ????
//
// Get query results
//
// The areas of unknown work (????) on the CPU indicate that the CPU may or may not have
// finished post-submission work while the GPU is executing in parallel. With no further
// work, querying CPU timestamps before submission and after getting query results give the
// bounds to Tgpu, which could be quite large.
//
// Using VkEvents, the GPU can be made to wait for the CPU and vice versa, in an effort to
// reduce this range. This function implements the following procedure:
//
// CPU GPU
//
// Record command buffer
// with timestamp query
//
// Submit command buffer
//
// Post-submission work Begin execution
//
// ???? Set Event GPUReady
//
// Wait on Event GPUReady Wait on Event CPUReady
//
// Get CPU Time Ts Wait on Event CPUReady
//
// Set Event CPUReady Wait on Event CPUReady
//
// Get CPU Time Tcpu Get GPU Time Tgpu
//
// Wait on Event GPUDone Set Event GPUDone
//
// Get CPU Time Te End Execution
//
// Idle Return query results
//
// Get query results
//
// If Te-Ts > epsilon, a GPU or CPU interruption can be assumed and the operation can be
// retried. Once Te-Ts < epsilon, Tcpu can be taken to presumably match Tgpu. Finding an
// epsilon that's valid for all devices may be difficult, so the loop can be performed only
// a limited number of times and the Tcpu,Tgpu pair corresponding to smallest Te-Ts used for
// calibration.
//
// Note: Once VK_EXT_calibrated_timestamps is ubiquitous, this should be redone.
// Make sure nothing is running
ASSERT(!hasRecordedCommands());
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::synchronizeCpuGpuTime");
// Create a query used to receive the GPU timestamp
vk::QueryHelper timestampQuery;
ANGLE_TRY(mGpuEventQueryPool.allocateQuery(this, &timestampQuery));
// Create the three events
VkEventCreateInfo eventCreateInfo = {};
eventCreateInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
eventCreateInfo.flags = 0;
VkDevice device = getDevice();
vk::DeviceScoped<vk::Event> cpuReady(device), gpuReady(device), gpuDone(device);
ANGLE_VK_TRY(this, cpuReady.get().init(device, eventCreateInfo));
ANGLE_VK_TRY(this, gpuReady.get().init(device, eventCreateInfo));
ANGLE_VK_TRY(this, gpuDone.get().init(device, eventCreateInfo));
constexpr uint32_t kRetries = 10;
// Time suffixes used are S for seconds and Cycles for cycles
double tightestRangeS = 1e6f;
double TcpuS = 0;
uint64_t TgpuCycles = 0;
for (uint32_t i = 0; i < kRetries; ++i)
{
// Reset the events
ANGLE_VK_TRY(this, cpuReady.get().reset(device));
ANGLE_VK_TRY(this, gpuReady.get().reset(device));
ANGLE_VK_TRY(this, gpuDone.get().reset(device));
// Record the command buffer
vk::DeviceScoped<vk::PrimaryCommandBuffer> commandBatch(device);
vk::PrimaryCommandBuffer &commandBuffer = commandBatch.get();
vk::ResourceUseList scratchResourceUseList;
ANGLE_TRY(mRenderer->getCommandBufferOneOff(this, &commandBuffer));
commandBuffer.setEvent(gpuReady.get().getHandle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
commandBuffer.waitEvents(1, cpuReady.get().ptr(), VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, nullptr, 0, nullptr, 0,
nullptr);
timestampQuery.writeTimestampToPrimary(this, &commandBuffer);
timestampQuery.retain(&scratchResourceUseList);
commandBuffer.setEvent(gpuDone.get().getHandle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT);
ANGLE_VK_TRY(this, commandBuffer.end());
Serial throwAwaySerial;
// vkEvent's are externally synchronized, therefore need work to be submitted before calling
// vkGetEventStatus
ANGLE_TRY(mRenderer->queueSubmitOneOff(this, std::move(commandBuffer), mContextPriority,
nullptr, vk::SubmitPolicy::EnsureSubmitted,
&throwAwaySerial));
scratchResourceUseList.releaseResourceUsesAndUpdateSerials(throwAwaySerial);
// Wait for GPU to be ready. This is a short busy wait.
VkResult result = VK_EVENT_RESET;
do
{
result = gpuReady.get().getStatus(device);
if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
{
ANGLE_VK_TRY(this, result);
}
} while (result == VK_EVENT_RESET);
double TsS = platform->monotonicallyIncreasingTime(platform);
// Tell the GPU to go ahead with the timestamp query.
ANGLE_VK_TRY(this, cpuReady.get().set(device));
double cpuTimestampS = platform->monotonicallyIncreasingTime(platform);
// Wait for GPU to be done. Another short busy wait.
do
{
result = gpuDone.get().getStatus(device);
if (result != VK_EVENT_SET && result != VK_EVENT_RESET)
{
ANGLE_VK_TRY(this, result);
}
} while (result == VK_EVENT_RESET);
double TeS = platform->monotonicallyIncreasingTime(platform);
// Get the query results
// Note: This LastSubmittedQueueSerial may include more work then was submitted above if
// another thread had submitted work.
ANGLE_TRY(finishToSerial(getLastSubmittedQueueSerial()));
vk::QueryResult gpuTimestampCycles(1);
ANGLE_TRY(timestampQuery.getUint64Result(this, &gpuTimestampCycles));
// Use the first timestamp queried as origin.
if (mGpuEventTimestampOrigin == 0)
{
mGpuEventTimestampOrigin =
gpuTimestampCycles.getResult(vk::QueryResult::kDefaultResultIndex);
}
// Take these CPU and GPU timestamps if there is better confidence.
double confidenceRangeS = TeS - TsS;
if (confidenceRangeS < tightestRangeS)
{
tightestRangeS = confidenceRangeS;
TcpuS = cpuTimestampS;
TgpuCycles = gpuTimestampCycles.getResult(vk::QueryResult::kDefaultResultIndex);
}
}
mGpuEventQueryPool.freeQuery(this, &timestampQuery);
// timestampPeriod gives nanoseconds/cycle.
double TgpuS =
(TgpuCycles - mGpuEventTimestampOrigin) *
static_cast<double>(getRenderer()->getPhysicalDeviceProperties().limits.timestampPeriod) /
1'000'000'000.0;
flushGpuEvents(TgpuS, TcpuS);
mGpuClockSync.gpuTimestampS = TgpuS;
mGpuClockSync.cpuTimestampS = TcpuS;
return angle::Result::Continue;
}
angle::Result ContextVk::traceGpuEventImpl(vk::CommandBuffer *commandBuffer,
char phase,
const EventName &name)
{
ASSERT(mGpuEventsEnabled);
GpuEventQuery gpuEvent;
gpuEvent.name = name;
gpuEvent.phase = phase;
ANGLE_TRY(mGpuEventQueryPool.allocateQuery(this, &gpuEvent.queryHelper));
gpuEvent.queryHelper.writeTimestamp(this, commandBuffer);
mInFlightGpuEventQueries.push_back(std::move(gpuEvent));
return angle::Result::Continue;
}
angle::Result ContextVk::checkCompletedGpuEvents()
{
ASSERT(mGpuEventsEnabled);
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
ASSERT(platform);
int finishedCount = 0;
Serial lastCompletedSerial = getLastCompletedQueueSerial();
for (GpuEventQuery &eventQuery : mInFlightGpuEventQueries)
{
// Only check the timestamp query if the submission has finished.
if (eventQuery.queryHelper.usedInRunningCommands(lastCompletedSerial))
{
break;
}
// See if the results are available.
vk::QueryResult gpuTimestampCycles(1);
bool available = false;
ANGLE_TRY(eventQuery.queryHelper.getUint64ResultNonBlocking(this, &gpuTimestampCycles,
&available));
if (!available)
{
break;
}
mGpuEventQueryPool.freeQuery(this, &eventQuery.queryHelper);
GpuEvent gpuEvent;
gpuEvent.gpuTimestampCycles =
gpuTimestampCycles.getResult(vk::QueryResult::kDefaultResultIndex);
gpuEvent.name = eventQuery.name;
gpuEvent.phase = eventQuery.phase;
mGpuEvents.emplace_back(gpuEvent);
++finishedCount;
}
mInFlightGpuEventQueries.erase(mInFlightGpuEventQueries.begin(),
mInFlightGpuEventQueries.begin() + finishedCount);
return angle::Result::Continue;
}
void ContextVk::flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuTimestampS)
{
if (mGpuEvents.empty())
{
return;
}
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
ASSERT(platform);
// Find the slope of the clock drift for adjustment
double lastGpuSyncTimeS = mGpuClockSync.gpuTimestampS;
double lastGpuSyncDiffS = mGpuClockSync.cpuTimestampS - mGpuClockSync.gpuTimestampS;
double gpuSyncDriftSlope = 0;
double nextGpuSyncTimeS = nextSyncGpuTimestampS;
double nextGpuSyncDiffS = nextSyncCpuTimestampS - nextSyncGpuTimestampS;
// No gpu trace events should have been generated before the clock sync, so if there is no
// "previous" clock sync, there should be no gpu events (i.e. the function early-outs
// above).
ASSERT(mGpuClockSync.gpuTimestampS != std::numeric_limits<double>::max() &&
mGpuClockSync.cpuTimestampS != std::numeric_limits<double>::max());
gpuSyncDriftSlope =
(nextGpuSyncDiffS - lastGpuSyncDiffS) / (nextGpuSyncTimeS - lastGpuSyncTimeS);
for (const GpuEvent &gpuEvent : mGpuEvents)
{
double gpuTimestampS =
(gpuEvent.gpuTimestampCycles - mGpuEventTimestampOrigin) *
static_cast<double>(
getRenderer()->getPhysicalDeviceProperties().limits.timestampPeriod) *
1e-9;
// Account for clock drift.
gpuTimestampS += lastGpuSyncDiffS + gpuSyncDriftSlope * (gpuTimestampS - lastGpuSyncTimeS);
// Generate the trace now that the GPU timestamp is available and clock drifts are
// accounted for.
static long long eventId = 1;
static const unsigned char *categoryEnabled =
TRACE_EVENT_API_GET_CATEGORY_ENABLED(platform, "gpu.angle.gpu");
platform->addTraceEvent(platform, gpuEvent.phase, categoryEnabled, gpuEvent.name.data(),
eventId++, gpuTimestampS, 0, nullptr, nullptr, nullptr,
TRACE_EVENT_FLAG_NONE);
}
mGpuEvents.clear();
}
void ContextVk::clearAllGarbage()
{
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::clearAllGarbage");
for (vk::GarbageObject &garbage : mCurrentGarbage)
{
garbage.destroy(mRenderer);
}
mCurrentGarbage.clear();
}
void ContextVk::handleDeviceLost()
{
mOutsideRenderPassCommands->reset();
mRenderPassCommands->reset();
mRenderer->handleDeviceLost();
clearAllGarbage();
mRenderer->notifyDeviceLost();
}
angle::Result ContextVk::drawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count)
{
uint32_t clampedVertexCount = gl::GetClampedVertexCount<uint32_t>(count);
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t numIndices;
ANGLE_TRY(setupLineLoopDraw(context, mode, first, count, gl::DrawElementsType::InvalidEnum,
nullptr, &numIndices));
vk::LineLoopHelper::Draw(numIndices, 0, mRenderPassCommandBuffer);
}
else
{
ANGLE_TRY(setupDraw(context, mode, first, count, 1, gl::DrawElementsType::InvalidEnum,
nullptr, mNonIndexedDirtyBitsMask));
mRenderPassCommandBuffer->draw(clampedVertexCount, first);
}
return angle::Result::Continue;
}
angle::Result ContextVk::drawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instances)
{
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t clampedVertexCount = gl::GetClampedVertexCount<uint32_t>(count);
uint32_t numIndices;
ANGLE_TRY(setupLineLoopDraw(context, mode, first, clampedVertexCount,
gl::DrawElementsType::InvalidEnum, nullptr, &numIndices));
mRenderPassCommandBuffer->drawIndexedInstanced(numIndices, instances);
return angle::Result::Continue;
}
ANGLE_TRY(setupDraw(context, mode, first, count, instances, gl::DrawElementsType::InvalidEnum,
nullptr, mNonIndexedDirtyBitsMask));
mRenderPassCommandBuffer->drawInstanced(gl::GetClampedVertexCount<uint32_t>(count), instances,
first);
return angle::Result::Continue;
}
angle::Result ContextVk::drawArraysInstancedBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instances,
GLuint baseInstance)
{
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t clampedVertexCount = gl::GetClampedVertexCount<uint32_t>(count);
uint32_t numIndices;
ANGLE_TRY(setupLineLoopDraw(context, mode, first, clampedVertexCount,
gl::DrawElementsType::InvalidEnum, nullptr, &numIndices));
mRenderPassCommandBuffer->drawIndexedInstancedBaseVertexBaseInstance(numIndices, instances,
0, 0, baseInstance);
return angle::Result::Continue;
}
ANGLE_TRY(setupDraw(context, mode, first, count, instances, gl::DrawElementsType::InvalidEnum,
nullptr, mNonIndexedDirtyBitsMask));
mRenderPassCommandBuffer->drawInstancedBaseInstance(gl::GetClampedVertexCount<uint32_t>(count),
instances, first, baseInstance);
return angle::Result::Continue;
}
angle::Result ContextVk::drawElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices)
{
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t indexCount;
ANGLE_TRY(setupLineLoopDraw(context, mode, 0, count, type, indices, &indexCount));
vk::LineLoopHelper::Draw(indexCount, 0, mRenderPassCommandBuffer);
}
else
{
ANGLE_TRY(setupIndexedDraw(context, mode, count, 1, type, indices));
mRenderPassCommandBuffer->drawIndexed(count);
}
return angle::Result::Continue;
}
angle::Result ContextVk::drawElementsBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLint baseVertex)
{
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t indexCount;
ANGLE_TRY(setupLineLoopDraw(context, mode, 0, count, type, indices, &indexCount));
vk::LineLoopHelper::Draw(indexCount, baseVertex, mRenderPassCommandBuffer);
}
else
{
ANGLE_TRY(setupIndexedDraw(context, mode, count, 1, type, indices));
mRenderPassCommandBuffer->drawIndexedBaseVertex(count, baseVertex);
}
return angle::Result::Continue;
}
angle::Result ContextVk::drawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances)
{
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t indexCount;
ANGLE_TRY(setupLineLoopDraw(context, mode, 0, count, type, indices, &indexCount));
count = indexCount;
}
else
{
ANGLE_TRY(setupIndexedDraw(context, mode, count, instances, type, indices));
}
mRenderPassCommandBuffer->drawIndexedInstanced(count, instances);
return angle::Result::Continue;
}
angle::Result ContextVk::drawElementsInstancedBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances,
GLint baseVertex)
{
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t indexCount;
ANGLE_TRY(setupLineLoopDraw(context, mode, 0, count, type, indices, &indexCount));
count = indexCount;
}
else
{
ANGLE_TRY(setupIndexedDraw(context, mode, count, instances, type, indices));
}
mRenderPassCommandBuffer->drawIndexedInstancedBaseVertex(count, instances, baseVertex);
return angle::Result::Continue;
}
angle::Result ContextVk::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances,
GLint baseVertex,
GLuint baseInstance)
{
if (mode == gl::PrimitiveMode::LineLoop)
{
uint32_t indexCount;
ANGLE_TRY(setupLineLoopDraw(context, mode, 0, count, type, indices, &indexCount));
mRenderPassCommandBuffer->drawIndexedInstancedBaseVertexBaseInstance(
indexCount, instances, 0, baseVertex, baseInstance);
return angle::Result::Continue;
}
ANGLE_TRY(setupIndexedDraw(context, mode, count, instances, type, indices));
mRenderPassCommandBuffer->drawIndexedInstancedBaseVertexBaseInstance(count, instances, 0,
baseVertex, baseInstance);
return angle::Result::Continue;
}
angle::Result ContextVk::drawRangeElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLuint start,
GLuint end,
GLsizei count,
gl::DrawElementsType type,
const void *indices)
{
return drawElements(context, mode, count, type, indices);
}
angle::Result ContextVk::drawRangeElementsBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLuint start,
GLuint end,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLint baseVertex)
{
return drawElementsBaseVertex(context, mode, count, type, indices, baseVertex);
}
VkDevice ContextVk::getDevice() const
{
return mRenderer->getDevice();
}
angle::Result ContextVk::drawArraysIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect)
{
gl::Buffer *indirectBuffer = mState.getTargetBuffer(gl::BufferBinding::DrawIndirect);
vk::BufferHelper *currentIndirectBuf = &vk::GetImpl(indirectBuffer)->getBuffer();
VkDeviceSize currentIndirectBufOffset = reinterpret_cast<VkDeviceSize>(indirect);
if (mVertexArray->getStreamingVertexAttribsMask().any())
{
// We have instanced vertex attributes that need to be emulated for Vulkan.
// invalidate any cache and map the buffer so that we can read the indirect data.
// Mapping the buffer will cause a flush.
ANGLE_TRY(currentIndirectBuf->invalidate(mRenderer, 0, sizeof(VkDrawIndirectCommand)));
uint8_t *buffPtr;
ANGLE_TRY(currentIndirectBuf->map(this, &buffPtr));
const VkDrawIndirectCommand *indirectData =
reinterpret_cast<VkDrawIndirectCommand *>(buffPtr + currentIndirectBufOffset);
ANGLE_TRY(drawArraysInstanced(context, mode, indirectData->firstVertex,
indirectData->vertexCount, indirectData->instanceCount));
currentIndirectBuf->unmap(mRenderer);
return angle::Result::Continue;
}
if (mode == gl::PrimitiveMode::LineLoop)
{
ASSERT(indirectBuffer);
vk::BufferHelper *dstIndirectBuf = nullptr;
VkDeviceSize dstIndirectBufOffset = 0;
ANGLE_TRY(setupLineLoopIndirectDraw(context, mode, currentIndirectBuf,
currentIndirectBufOffset, &dstIndirectBuf,
&dstIndirectBufOffset));
mRenderPassCommandBuffer->drawIndexedIndirect(dstIndirectBuf->getBuffer(),
dstIndirectBufOffset, 1, 0);
return angle::Result::Continue;
}
ANGLE_TRY(setupIndirectDraw(context, mode, mNonIndexedDirtyBitsMask, currentIndirectBuf,
currentIndirectBufOffset));
mRenderPassCommandBuffer->drawIndirect(currentIndirectBuf->getBuffer(),
currentIndirectBufOffset, 1, 0);
return angle::Result::Continue;
}
angle::Result ContextVk::drawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect)
{
VkDeviceSize currentIndirectBufOffset = reinterpret_cast<VkDeviceSize>(indirect);
gl::Buffer *indirectBuffer = mState.getTargetBuffer(gl::BufferBinding::DrawIndirect);
ASSERT(indirectBuffer);
vk::BufferHelper *currentIndirectBuf = &vk::GetImpl(indirectBuffer)->getBuffer();
if (mVertexArray->getStreamingVertexAttribsMask().any())
{
// We have instanced vertex attributes that need to be emulated for Vulkan.
// invalidate any cache and map the buffer so that we can read the indirect data.
// Mapping the buffer will cause a flush.
ANGLE_TRY(
currentIndirectBuf->invalidate(mRenderer, 0, sizeof(VkDrawIndexedIndirectCommand)));
uint8_t *buffPtr;
ANGLE_TRY(currentIndirectBuf->map(this, &buffPtr));
const VkDrawIndexedIndirectCommand *indirectData =
reinterpret_cast<VkDrawIndexedIndirectCommand *>(buffPtr + currentIndirectBufOffset);
ANGLE_TRY(drawElementsInstanced(context, mode, indirectData->indexCount, type, nullptr,
indirectData->instanceCount));
currentIndirectBuf->unmap(mRenderer);
return angle::Result::Continue;
}
if (shouldConvertUint8VkIndexType(type) && mGraphicsDirtyBits[DIRTY_BIT_INDEX_BUFFER])
{
ANGLE_PERF_WARNING(getDebug(), GL_DEBUG_SEVERITY_LOW,
"Potential inefficiency emulating uint8 vertex attributes due to lack "
"of hardware support");
vk::BufferHelper *dstIndirectBuf;
VkDeviceSize dstIndirectBufOffset;
ANGLE_TRY(mVertexArray->convertIndexBufferIndirectGPU(
this, currentIndirectBuf, currentIndirectBufOffset, &dstIndirectBuf,
&dstIndirectBufOffset));
currentIndirectBuf = dstIndirectBuf;
currentIndirectBufOffset = dstIndirectBufOffset;
}
if (mode == gl::PrimitiveMode::LineLoop)
{
vk::BufferHelper *dstIndirectBuf;
VkDeviceSize dstIndirectBufOffset;
ANGLE_TRY(setupLineLoopIndexedIndirectDraw(context, mode, type, currentIndirectBuf,
currentIndirectBufOffset, &dstIndirectBuf,
&dstIndirectBufOffset));
currentIndirectBuf = dstIndirectBuf;
currentIndirectBufOffset = dstIndirectBufOffset;
}
else
{
ANGLE_TRY(setupIndexedIndirectDraw(context, mode, type, currentIndirectBuf,
currentIndirectBufOffset));
}
mRenderPassCommandBuffer->drawIndexedIndirect(currentIndirectBuf->getBuffer(),
currentIndirectBufOffset, 1, 0);
return angle::Result::Continue;
}
angle::Result ContextVk::multiDrawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
GLsizei drawcount)
{
return rx::MultiDrawArraysGeneral(this, context, mode, firsts, counts, drawcount);
}
angle::Result ContextVk::multiDrawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
return rx::MultiDrawArraysInstancedGeneral(this, context, mode, firsts, counts, instanceCounts,
drawcount);
}
angle::Result ContextVk::multiDrawElements(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
GLsizei drawcount)
{
return rx::MultiDrawElementsGeneral(this, context, mode, counts, type, indices, drawcount);
}
angle::Result ContextVk::multiDrawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
return rx::MultiDrawElementsInstancedGeneral(this, context, mode, counts, type, indices,
instanceCounts, drawcount);
}
angle::Result ContextVk::multiDrawArraysInstancedBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
const GLsizei *instanceCounts,
const GLuint *baseInstances,
GLsizei drawcount)
{
return rx::MultiDrawArraysInstancedBaseInstanceGeneral(
this, context, mode, firsts, counts, instanceCounts, baseInstances, drawcount);
}
angle::Result ContextVk::multiDrawElementsInstancedBaseVertexBaseInstance(
const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
const GLsizei *instanceCounts,
const GLint *baseVertices,