blob: 6c4d58f0279b9ffc613268d7959ac76c08b82ffe [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.h:
// Defines the class interface for ContextVk, implementing ContextImpl.
//
#ifndef LIBANGLE_RENDERER_VULKAN_CONTEXTVK_H_
#define LIBANGLE_RENDERER_VULKAN_CONTEXTVK_H_
#include "volk.h"
#include "common/PackedEnums.h"
#include "libANGLE/renderer/ContextImpl.h"
#include "libANGLE/renderer/vulkan/OverlayVk.h"
#include "libANGLE/renderer/vulkan/PersistentCommandPool.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace angle
{
struct FeaturesVk;
} // namespace angle
namespace rx
{
class ProgramExecutableVk;
class RendererVk;
class WindowSurfaceVk;
// The possible rotations of the surface/draw framebuffer, used for pre-rotating gl_Position
// in the vertex shader.
enum class SurfaceRotationType
{
Identity,
Rotated90Degrees,
Rotated180Degrees,
Rotated270Degrees,
FlippedIdentity,
FlippedRotated90Degrees,
FlippedRotated180Degrees,
FlippedRotated270Degrees,
InvalidEnum,
EnumCount = InvalidEnum,
};
struct CommandBatch final : angle::NonCopyable
{
CommandBatch();
~CommandBatch();
CommandBatch(CommandBatch &&other);
CommandBatch &operator=(CommandBatch &&other);
void destroy(VkDevice device);
vk::PrimaryCommandBuffer primaryCommands;
// commandPool is for secondary CommandBuffer allocation
vk::CommandPool commandPool;
vk::Shared<vk::Fence> fence;
Serial serial;
};
class CommandQueue final : angle::NonCopyable
{
public:
CommandQueue();
~CommandQueue();
angle::Result init(vk::Context *context);
void destroy(VkDevice device);
void handleDeviceLost(RendererVk *renderer);
bool hasInFlightCommands() const;
angle::Result allocatePrimaryCommandBuffer(vk::Context *context,
const vk::CommandPool &commandPool,
vk::PrimaryCommandBuffer *commandBufferOut);
angle::Result releasePrimaryCommandBuffer(vk::Context *context,
vk::PrimaryCommandBuffer &&commandBuffer);
void clearAllGarbage(RendererVk *renderer);
angle::Result finishToSerial(vk::Context *context, Serial serial, uint64_t timeout);
angle::Result submitFrame(vk::Context *context,
egl::ContextPriority priority,
const VkSubmitInfo &submitInfo,
const vk::Shared<vk::Fence> &sharedFence,
vk::GarbageList *currentGarbage,
vk::CommandPool *commandPool,
vk::PrimaryCommandBuffer &&commandBuffer);
vk::Shared<vk::Fence> getLastSubmittedFence(const vk::Context *context) const;
// Check to see which batches have finished completion (forward progress for
// mLastCompletedQueueSerial, for example for when the application busy waits on a query
// result). It would be nice if we didn't have to expose this for QueryVk::getResult.
angle::Result checkCompletedCommands(vk::Context *context);
private:
angle::Result releaseToCommandBatch(vk::Context *context,
vk::PrimaryCommandBuffer &&commandBuffer,
vk::CommandPool *commandPool,
CommandBatch *batch);
vk::GarbageQueue mGarbageQueue;
std::vector<CommandBatch> mInFlightCommands;
// Keeps a free list of reusable primary command buffers.
vk::PersistentCommandPool mPrimaryCommandPool;
};
struct CommandBufferHelper : angle::NonCopyable
{
public:
void bufferRead(vk::ResourceUseList *resourceUseList,
VkAccessFlags readAccessType,
vk::BufferHelper *buffer);
void bufferWrite(vk::ResourceUseList *resourceUseList,
VkAccessFlags writeAccessType,
vk::BufferHelper *buffer);
void imageRead(vk::ResourceUseList *resourceUseList,
VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
void imageWrite(vk::ResourceUseList *resourceUseList,
VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
void imageBarrier(VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
const VkImageMemoryBarrier &imageMemoryBarrier);
vk::CommandBuffer &getCommandBuffer() { return mCommandBuffer; }
protected:
CommandBufferHelper();
~CommandBufferHelper();
void executeBarriers(vk::PrimaryCommandBuffer *primary);
VkPipelineStageFlags mImageBarrierSrcStageMask;
VkPipelineStageFlags mImageBarrierDstStageMask;
std::vector<VkImageMemoryBarrier> mImageMemoryBarriers;
VkFlags mGlobalMemoryBarrierSrcAccess;
VkFlags mGlobalMemoryBarrierDstAccess;
VkPipelineStageFlags mGlobalMemoryBarrierStages;
vk::CommandBuffer mCommandBuffer;
};
class OutsideRenderPassCommandBuffer final : public CommandBufferHelper
{
public:
OutsideRenderPassCommandBuffer();
~OutsideRenderPassCommandBuffer();
void flushToPrimary(ContextVk *contextVk, vk::PrimaryCommandBuffer *primary);
bool empty() const { return mCommandBuffer.empty(); }
void reset();
};
class RenderPassCommandBuffer final : public CommandBufferHelper
{
public:
RenderPassCommandBuffer();
~RenderPassCommandBuffer();
void initialize(angle::PoolAllocator *poolAllocator);
void beginRenderPass(const vk::Framebuffer &framebuffer,
const gl::Rectangle &renderArea,
const vk::RenderPassDesc &renderPassDesc,
const vk::AttachmentOpsArray &renderPassAttachmentOps,
const std::vector<VkClearValue> &clearValues,
vk::CommandBuffer **commandBufferOut);
void beginTransformFeedback(size_t validBufferCount,
const VkBuffer *counterBuffers,
bool rebindBuffer);
void clearRenderPassColorAttachment(size_t attachmentIndex, const VkClearColorValue &clearValue)
{
SetBitField(mAttachmentOps[attachmentIndex].loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR);
mClearValues[attachmentIndex].color = clearValue;
}
void clearRenderPassDepthAttachment(size_t attachmentIndex, float depth)
{
SetBitField(mAttachmentOps[attachmentIndex].loadOp, VK_ATTACHMENT_LOAD_OP_CLEAR);
SetBitField(mClearValues[attachmentIndex].depthStencil.depth, depth);
}
void clearRenderPassStencilAttachment(size_t attachmentIndex, uint32_t stencil)
{
SetBitField(mAttachmentOps[attachmentIndex].stencilLoadOp, VK_ATTACHMENT_LOAD_OP_CLEAR);
SetBitField(mClearValues[attachmentIndex].depthStencil.stencil, stencil);
}
void invalidateRenderPassColorAttachment(size_t attachmentIndex)
{
SetBitField(mAttachmentOps[attachmentIndex].storeOp, VK_ATTACHMENT_STORE_OP_DONT_CARE);
}
void invalidateRenderPassDepthAttachment(size_t attachmentIndex)
{
SetBitField(mAttachmentOps[attachmentIndex].storeOp, VK_ATTACHMENT_STORE_OP_DONT_CARE);
}
void invalidateRenderPassStencilAttachment(size_t attachmentIndex)
{
SetBitField(mAttachmentOps[attachmentIndex].stencilStoreOp,
VK_ATTACHMENT_STORE_OP_DONT_CARE);
}
void updateRenderPassAttachmentFinalLayout(size_t attachmentIndex, vk::ImageLayout finalLayout)
{
SetBitField(mAttachmentOps[attachmentIndex].finalLayout, finalLayout);
}
const gl::Rectangle &getRenderArea() const { return mRenderArea; }
angle::Result flushToPrimary(ContextVk *contextVk, vk::PrimaryCommandBuffer *primary);
bool empty() const { return !started() && mCommandBuffer.empty(); }
bool started() const { return mRenderPassStarted; }
void reset();
void resumeTransformFeedbackIfStarted();
void pauseTransformFeedbackIfStarted();
uint32_t getAndResetCounter()
{
uint32_t count = mCounter;
mCounter = 0;
return count;
}
VkFramebuffer getFramebufferHandle() const { return mFramebuffer.getHandle(); }
private:
void addRenderPassCommandDiagnostics(ContextVk *contextVk);
uint32_t mCounter;
vk::RenderPassDesc mRenderPassDesc;
vk::AttachmentOpsArray mAttachmentOps;
vk::Framebuffer mFramebuffer;
gl::Rectangle mRenderArea;
gl::AttachmentArray<VkClearValue> mClearValues;
bool mRenderPassStarted;
// Transform feedback state
gl::TransformFeedbackBuffersArray<VkBuffer> mTransformFeedbackCounterBuffers;
uint32_t mValidTransformFeedbackBufferCount;
bool mRebindTransformFeedbackBuffers;
};
static constexpr uint32_t kMaxGpuEventNameLen = 32;
using EventName = std::array<char, kMaxGpuEventNameLen>;
class ContextVk : public ContextImpl, public vk::Context
{
public:
ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk *renderer);
~ContextVk() override;
angle::Result initialize() override;
void onDestroy(const gl::Context *context) override;
// Flush and finish.
angle::Result flush(const gl::Context *context) override;
angle::Result finish(const gl::Context *context) override;
// Drawing methods.
angle::Result drawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count) override;
angle::Result drawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount) override;
angle::Result drawArraysInstancedBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount,
GLuint baseInstance) override;
angle::Result drawElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices) override;
angle::Result drawElementsBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLint baseVertex) override;
angle::Result drawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instanceCount) override;
angle::Result drawElementsInstancedBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instanceCount,
GLint baseVertex) override;
angle::Result drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances,
GLint baseVertex,
GLuint baseInstance) override;
angle::Result drawRangeElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLuint start,
GLuint end,
GLsizei count,
gl::DrawElementsType type,
const void *indices) override;
angle::Result drawRangeElementsBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLuint start,
GLuint end,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLint baseVertex) override;
angle::Result drawArraysIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect) override;
angle::Result drawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect) override;
angle::Result clearWithRenderPassOp(const gl::Rectangle &clearArea,
gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
bool clearStencil,
const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue);
// Device loss
gl::GraphicsResetStatus getResetStatus() override;
// Vendor and description strings.
std::string getVendorString() const override;
std::string getRendererDescription() const override;
// EXT_debug_marker
angle::Result insertEventMarker(GLsizei length, const char *marker) override;
angle::Result pushGroupMarker(GLsizei length, const char *marker) override;
angle::Result popGroupMarker() override;
// KHR_debug
angle::Result pushDebugGroup(const gl::Context *context,
GLenum source,
GLuint id,
const std::string &message) override;
angle::Result popDebugGroup(const gl::Context *context) override;
bool isViewportFlipEnabledForDrawFBO() const;
bool isViewportFlipEnabledForReadFBO() const;
// When the device/surface is rotated such that the surface's aspect ratio is different than
// the native device (e.g. 90 degrees), the width and height of the viewport, scissor, and
// render area must be swapped.
bool isRotatedAspectRatioForDrawFBO() const;
bool isRotatedAspectRatioForReadFBO() const;
void invalidateProgramBindingHelper(const gl::State &glState);
angle::Result invalidateProgramExecutableHelper(const gl::Context *context);
// State sync with dirty bits.
angle::Result syncState(const gl::Context *context,
const gl::State::DirtyBits &dirtyBits,
const gl::State::DirtyBits &bitMask) override;
// Disjoint timer queries
GLint getGPUDisjoint() override;
GLint64 getTimestamp() override;
// Context switching
angle::Result onMakeCurrent(const gl::Context *context) override;
angle::Result onUnMakeCurrent(const gl::Context *context) override;
// Native capabilities, unmodified by gl::Context.
gl::Caps getNativeCaps() const override;
const gl::TextureCapsMap &getNativeTextureCaps() const override;
const gl::Extensions &getNativeExtensions() const override;
const gl::Limitations &getNativeLimitations() const override;
// Shader creation
CompilerImpl *createCompiler() override;
ShaderImpl *createShader(const gl::ShaderState &state) override;
ProgramImpl *createProgram(const gl::ProgramState &state) override;
// Framebuffer creation
FramebufferImpl *createFramebuffer(const gl::FramebufferState &state) override;
// Texture creation
TextureImpl *createTexture(const gl::TextureState &state) override;
// Renderbuffer creation
RenderbufferImpl *createRenderbuffer(const gl::RenderbufferState &state) override;
// Buffer creation
BufferImpl *createBuffer(const gl::BufferState &state) override;
// Vertex Array creation
VertexArrayImpl *createVertexArray(const gl::VertexArrayState &state) override;
// Query and Fence creation
QueryImpl *createQuery(gl::QueryType type) override;
FenceNVImpl *createFenceNV() override;
SyncImpl *createSync() override;
// Transform Feedback creation
TransformFeedbackImpl *createTransformFeedback(
const gl::TransformFeedbackState &state) override;
// Sampler object creation
SamplerImpl *createSampler(const gl::SamplerState &state) override;
// Program Pipeline object creation
ProgramPipelineImpl *createProgramPipeline(const gl::ProgramPipelineState &data) override;
// Memory object creation.
MemoryObjectImpl *createMemoryObject() override;
// Semaphore creation.
SemaphoreImpl *createSemaphore() override;
// Overlay creation.
OverlayImpl *createOverlay(const gl::OverlayState &state) override;
angle::Result dispatchCompute(const gl::Context *context,
GLuint numGroupsX,
GLuint numGroupsY,
GLuint numGroupsZ) override;
angle::Result dispatchComputeIndirect(const gl::Context *context, GLintptr indirect) override;
angle::Result memoryBarrier(const gl::Context *context, GLbitfield barriers) override;
angle::Result memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers) override;
VkDevice getDevice() const;
egl::ContextPriority getPriority() const { return mContextPriority; }
ANGLE_INLINE const angle::FeaturesVk &getFeatures() const { return mRenderer->getFeatures(); }
ANGLE_INLINE void invalidateVertexAndIndexBuffers()
{
// TODO: Make the pipeline invalidate more fine-grained. Only need to dirty here if PSO
// VtxInput state (stride, fmt, inputRate...) has changed. http://anglebug.com/3256
invalidateCurrentGraphicsPipeline();
mGraphicsDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
mGraphicsDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
}
ANGLE_INLINE void invalidateVertexBuffers()
{
mGraphicsDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
}
ANGLE_INLINE void onVertexAttributeChange(size_t attribIndex,
GLuint stride,
GLuint divisor,
angle::FormatID format,
GLuint relativeOffset)
{
invalidateVertexAndIndexBuffers();
// Set divisor to 1 for attribs with emulated divisor
mGraphicsPipelineDesc->updateVertexInput(
&mGraphicsPipelineTransition, static_cast<uint32_t>(attribIndex), stride,
divisor > mRenderer->getMaxVertexAttribDivisor() ? 1 : divisor, format, relativeOffset);
}
void invalidateDefaultAttribute(size_t attribIndex);
void invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask);
void onDrawFramebufferChange(FramebufferVk *framebufferVk);
void onHostVisibleBufferWrite() { mIsAnyHostVisibleBufferWritten = true; }
void invalidateCurrentTransformFeedbackBuffers();
void invalidateCurrentTransformFeedbackState();
void onTransformFeedbackStateChanged();
// When UtilsVk issues draw or dispatch calls, it binds descriptor sets that the context is not
// aware of. This function is called to make sure affected descriptor set bindings are dirtied
// for the next application draw/dispatch call.
void invalidateGraphicsDescriptorSet(uint32_t usedDescriptorSet);
void invalidateComputeDescriptorSet(uint32_t usedDescriptorSet);
void optimizeRenderPassForPresent(VkFramebuffer framebufferHandle);
vk::DynamicQueryPool *getQueryPool(gl::QueryType queryType);
const VkClearValue &getClearColorValue() const;
const VkClearValue &getClearDepthStencilValue() const;
VkColorComponentFlags getClearColorMask() const;
angle::Result getIncompleteTexture(const gl::Context *context,
gl::TextureType type,
gl::Texture **textureOut);
void updateColorMask(const gl::BlendState &blendState);
void updateSampleMask(const gl::State &glState);
void handleError(VkResult errorCode,
const char *file,
const char *function,
unsigned int line) override;
const gl::ActiveTextureArray<vk::TextureUnit> &getActiveTextures() const
{
return mActiveTextures;
}
const gl::ActiveTextureArray<TextureVk *> &getActiveImages() const { return mActiveImages; }
void setIndexBufferDirty()
{
mGraphicsDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
mLastIndexBufferOffset = reinterpret_cast<const void *>(angle::DirtyPointer);
}
void insertWaitSemaphore(const vk::Semaphore *waitSemaphore);
bool shouldFlush();
angle::Result flushImpl(const vk::Semaphore *semaphore);
angle::Result finishImpl();
void addWaitSemaphore(VkSemaphore semaphore);
const vk::CommandPool &getCommandPool() const;
Serial getCurrentQueueSerial() const { return mRenderer->getCurrentQueueSerial(); }
Serial getLastSubmittedQueueSerial() const { return mRenderer->getLastSubmittedQueueSerial(); }
Serial getLastCompletedQueueSerial() const { return mRenderer->getLastCompletedQueueSerial(); }
bool isSerialInUse(Serial serial) const;
template <typename T>
void addGarbage(T *object)
{
if (object->valid())
{
mCurrentGarbage.emplace_back(vk::GetGarbage(object));
}
}
// It would be nice if we didn't have to expose this for QueryVk::getResult.
angle::Result checkCompletedCommands();
// Wait for completion of batches until (at least) batch with given serial is finished.
angle::Result finishToSerial(Serial serial);
angle::Result getCompatibleRenderPass(const vk::RenderPassDesc &desc,
vk::RenderPass **renderPassOut);
angle::Result getRenderPassWithOps(const vk::RenderPassDesc &desc,
const vk::AttachmentOpsArray &ops,
vk::RenderPass **renderPassOut);
// Get (or allocate) the fence that will be signaled on next submission.
angle::Result getNextSubmitFence(vk::Shared<vk::Fence> *sharedFenceOut);
vk::Shared<vk::Fence> getLastSubmittedFence() const;
vk::ShaderLibrary &getShaderLibrary() { return mShaderLibrary; }
UtilsVk &getUtils() { return mUtils; }
angle::Result getTimestamp(uint64_t *timestampOut);
// Create Begin/End/Instant GPU trace events, which take their timestamps from GPU queries.
// The events are queued until the query results are available. Possible values for `phase`
// are TRACE_EVENT_PHASE_*
ANGLE_INLINE angle::Result traceGpuEvent(vk::PrimaryCommandBuffer *commandBuffer,
char phase,
const EventName &name)
{
if (mGpuEventsEnabled)
return traceGpuEventImpl(commandBuffer, phase, name);
return angle::Result::Continue;
}
RenderPassCache &getRenderPassCache() { return mRenderPassCache; }
vk::DescriptorSetLayoutDesc getDriverUniformsDescriptorSetDesc(
VkShaderStageFlags shaderStages) const;
// We use texture serials to optimize texture binding updates. Each permutation of a
// {VkImage/VkSampler} generates a unique serial. These serials are combined to form a unique
// signature for each descriptor set. This allows us to keep a cache of descriptor sets and
// avoid calling vkAllocateDesctiporSets each texture update.
Serial generateTextureSerial() { return mTextureSerialFactory.generate(); }
const vk::TextureDescriptorDesc &getActiveTexturesDesc() const { return mActiveTexturesDesc; }
Serial generateAttachmentImageSerial() { return mAttachmentImageSerialFactory.generate(); }
angle::Result updateScissor(const gl::State &glState);
bool emulateSeamfulCubeMapSampling() const { return mEmulateSeamfulCubeMapSampling; }
bool useOldRewriteStructSamplers() const { return mUseOldRewriteStructSamplers; }
const gl::OverlayType *getOverlay() const { return mState.getOverlay(); }
vk::ResourceUseList &getResourceUseList() { return mResourceUseList; }
angle::Result onBufferRead(VkAccessFlags readAccessType, vk::BufferHelper *buffer);
angle::Result onBufferWrite(VkAccessFlags writeAccessType, vk::BufferHelper *buffer);
angle::Result onImageRead(VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
angle::Result onImageWrite(VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
void onRenderPassImageWrite(VkImageAspectFlags aspectFlags,
vk::ImageLayout imageLayout,
vk::ImageHelper *image);
angle::Result endRenderPassAndGetCommandBuffer(vk::CommandBuffer **commandBufferOut)
{
ANGLE_TRY(endRenderPass());
*commandBufferOut = &mOutsideRenderPassCommands.getCommandBuffer();
return angle::Result::Continue;
}
angle::Result flushAndBeginRenderPass(const vk::Framebuffer &framebuffer,
const gl::Rectangle &renderArea,
const vk::RenderPassDesc &renderPassDesc,
const vk::AttachmentOpsArray &renderPassAttachmentOps,
const std::vector<VkClearValue> &clearValues,
vk::CommandBuffer **commandBufferOut);
bool hasStartedRenderPass() const { return !mRenderPassCommands.empty(); }
RenderPassCommandBuffer &getStartedRenderPassCommands()
{
ASSERT(hasStartedRenderPass());
return mRenderPassCommands;
}
angle::Result flushAndGetPrimaryCommandBuffer(vk::PrimaryCommandBuffer **primaryCommands)
{
flushOutsideRenderPassCommands();
ANGLE_TRY(endRenderPass());
*primaryCommands = &mPrimaryCommands;
// We assume any calling code is going to record primary commands.
mHasPrimaryCommands = true;
return angle::Result::Continue;
}
egl::ContextPriority getContextPriority() const override { return mContextPriority; }
angle::Result startRenderPass(gl::Rectangle renderArea);
angle::Result endRenderPass();
angle::Result syncExternalMemory();
void addCommandBufferDiagnostics(const std::string &commandBufferDiagnostics);
VkIndexType getVkIndexType(gl::DrawElementsType glIndexType) const;
size_t getVkIndexTypeSize(gl::DrawElementsType glIndexType) const;
bool shouldConvertUint8VkIndexType(gl::DrawElementsType glIndexType) const;
ANGLE_INLINE bool isBresenhamEmulationEnabled(const gl::PrimitiveMode mode)
{
return getFeatures().basicGLLineRasterization.enabled && gl::IsLineMode(mode);
}
const ProgramExecutableVk *getExecutable() const { return mExecutable; }
ProgramExecutableVk *getExecutable() { return mExecutable; }
bool isRobustResourceInitEnabled() const override;
// occlusion query
void beginOcclusionQuery(QueryVk *queryVk);
void endOcclusionQuery(QueryVk *queryVk);
private:
// Dirty bits.
enum DirtyBitType : size_t
{
DIRTY_BIT_DEFAULT_ATTRIBS,
DIRTY_BIT_PIPELINE,
DIRTY_BIT_TEXTURES,
DIRTY_BIT_VERTEX_BUFFERS,
DIRTY_BIT_INDEX_BUFFER,
DIRTY_BIT_DRIVER_UNIFORMS,
DIRTY_BIT_DRIVER_UNIFORMS_BINDING,
DIRTY_BIT_SHADER_RESOURCES, // excluding textures, which are handled separately.
DIRTY_BIT_TRANSFORM_FEEDBACK_BUFFERS,
DIRTY_BIT_TRANSFORM_FEEDBACK_STATE,
DIRTY_BIT_TRANSFORM_FEEDBACK_RESUME,
DIRTY_BIT_DESCRIPTOR_SETS,
DIRTY_BIT_MAX,
};
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
using DirtyBitHandler = angle::Result (ContextVk::*)(const gl::Context *,
vk::CommandBuffer *commandBuffer);
struct DriverUniformsDescriptorSet
{
vk::DynamicBuffer dynamicBuffer;
VkDescriptorSet descriptorSet;
uint32_t dynamicOffset;
vk::BindingPointer<vk::DescriptorSetLayout> descriptorSetLayout;
vk::RefCountedDescriptorPoolBinding descriptorPoolBinding;
DriverUniformsDescriptorSet();
~DriverUniformsDescriptorSet();
void init(RendererVk *rendererVk);
void destroy(RendererVk *rendererVk);
};
enum class PipelineType
{
Graphics = 0,
Compute = 1,
InvalidEnum = 2,
EnumCount = 2,
};
// The GpuEventQuery struct holds together a timestamp query and enough data to create a
// trace event based on that. Use traceGpuEvent to insert such queries. They will be readback
// when the results are available, without inserting a GPU bubble.
//
// - eventName will be the reported name of the event
// - phase is either 'B' (duration begin), 'E' (duration end) or 'i' (instant // event).
// See Google's "Trace Event Format":
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
// - serial is the serial of the batch the query was submitted on. Until the batch is
// submitted, the query is not checked to avoid incuring a flush.
struct GpuEventQuery final
{
EventName name;
char phase;
vk::QueryHelper queryHelper;
};
// Once a query result is available, the timestamp is read and a GpuEvent object is kept until
// the next clock sync, at which point the clock drift is compensated in the results before
// handing them off to the application.
struct GpuEvent final
{
uint64_t gpuTimestampCycles;
std::array<char, kMaxGpuEventNameLen> name;
char phase;
};
struct GpuClockSyncInfo
{
double gpuTimestampS;
double cpuTimestampS;
};
angle::Result setupDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertexOrInvalid,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
DirtyBits dirtyBitMask,
vk::CommandBuffer **commandBufferOut);
angle::Result setupIndexedDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei indexCount,
GLsizei instanceCount,
gl::DrawElementsType indexType,
const void *indices,
vk::CommandBuffer **commandBufferOut);
angle::Result setupIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
DirtyBits dirtyBitMask,
vk::BufferHelper *indirectBuffer,
VkDeviceSize indirectBufferOffset,
vk::CommandBuffer **commandBufferOut);
angle::Result setupIndexedIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType indexType,
vk::BufferHelper *indirectBuffer,
VkDeviceSize indirectBufferOffset,
vk::CommandBuffer **commandBufferOut);
angle::Result setupLineLoopIndexedIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType indexType,
vk::BufferHelper *srcIndirectBuf,
VkDeviceSize indirectBufferOffset,
vk::CommandBuffer **commandBufferOut,
vk::BufferHelper **indirectBufferOut,
VkDeviceSize *indirectBufferOffsetOut);
angle::Result setupLineLoopIndirectDraw(const gl::Context *context,
gl::PrimitiveMode mode,
vk::BufferHelper *indirectBuffer,
VkDeviceSize indirectBufferOffset,
vk::CommandBuffer **commandBufferOut,
vk::BufferHelper **indirectBufferOut,
VkDeviceSize *indirectBufferOffsetOut);
angle::Result setupLineLoopDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertex,
GLsizei vertexOrIndexCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
vk::CommandBuffer **commandBufferOut,
uint32_t *numIndicesOut);
angle::Result setupDispatch(const gl::Context *context, vk::CommandBuffer **commandBufferOut);
void updateViewport(FramebufferVk *framebufferVk,
const gl::Rectangle &viewport,
float nearPlane,
float farPlane,
bool invertViewport);
void updateDepthRange(float nearPlane, float farPlane);
void updateFlipViewportDrawFramebuffer(const gl::State &glState);
void updateFlipViewportReadFramebuffer(const gl::State &glState);
void updateSurfaceRotationDrawFramebuffer(const gl::State &glState);
void updateSurfaceRotationReadFramebuffer(const gl::State &glState);
angle::Result updateActiveTextures(const gl::Context *context);
angle::Result updateActiveImages(const gl::Context *context,
CommandBufferHelper *commandBufferHelper);
angle::Result updateDefaultAttribute(size_t attribIndex);
ANGLE_INLINE void invalidateCurrentGraphicsPipeline()
{
mGraphicsDirtyBits |= mNewGraphicsPipelineDirtyBits;
// The draw mode may have changed, toggling whether line rasterization is
// enabled or not, which means we need to recreate the graphics pipeline.
mCurrentGraphicsPipeline = nullptr;
}
ANGLE_INLINE void invalidateCurrentComputePipeline()
{
mComputeDirtyBits.set(DIRTY_BIT_PIPELINE);
mCurrentComputePipeline = nullptr;
}
void invalidateCurrentDefaultUniforms();
angle::Result invalidateCurrentTextures(const gl::Context *context);
void invalidateCurrentShaderResources();
void invalidateGraphicsDriverUniforms();
void invalidateDriverUniforms();
// Handlers for graphics pipeline dirty bits.
angle::Result handleDirtyGraphicsDefaultAttribs(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsPipeline(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsTextures(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsVertexBuffers(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsIndexBuffer(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsDriverUniforms(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsDriverUniformsBinding(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsShaderResources(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsTransformFeedbackBuffersEmulation(
const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsTransformFeedbackBuffersExtension(
const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsTransformFeedbackState(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyGraphicsTransformFeedbackResume(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
// Handlers for compute pipeline dirty bits.
angle::Result handleDirtyComputePipeline(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyComputeTextures(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyComputeDriverUniforms(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyComputeDriverUniformsBinding(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result handleDirtyComputeShaderResources(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
// Common parts of the common dirty bit handlers.
angle::Result handleDirtyTexturesImpl(CommandBufferHelper *commandBufferHelper);
angle::Result handleDirtyShaderResourcesImpl(const gl::Context *context,
CommandBufferHelper *commandBufferHelper);
void handleDirtyDriverUniformsBindingImpl(vk::CommandBuffer *commandBuffer,
VkPipelineBindPoint bindPoint,
const DriverUniformsDescriptorSet &driverUniforms);
angle::Result handleDirtyDescriptorSets(const gl::Context *context,
vk::CommandBuffer *commandBuffer);
angle::Result allocateDriverUniforms(size_t driverUniformsSize,
DriverUniformsDescriptorSet *driverUniforms,
VkBuffer *bufferOut,
uint8_t **ptrOut,
bool *newBufferOut);
angle::Result updateDriverUniformsDescriptorSet(VkBuffer buffer,
bool newBuffer,
size_t driverUniformsSize,
DriverUniformsDescriptorSet *driverUniforms);
void writeAtomicCounterBufferDriverUniformOffsets(uint32_t *offsetsOut, size_t offsetsSize);
angle::Result submitFrame(const VkSubmitInfo &submitInfo,
vk::PrimaryCommandBuffer &&commandBuffer);
angle::Result memoryBarrierImpl(GLbitfield barriers, VkPipelineStageFlags stageMask);
angle::Result synchronizeCpuGpuTime();
angle::Result traceGpuEventImpl(vk::PrimaryCommandBuffer *commandBuffer,
char phase,
const EventName &name);
angle::Result checkCompletedGpuEvents();
void flushGpuEvents(double nextSyncGpuTimestampS, double nextSyncCpuTimestampS);
void handleDeviceLost();
void waitForSwapchainImageIfNecessary();
bool shouldEmulateSeamfulCubeMapSampling() const;
bool shouldUseOldRewriteStructSamplers() const;
void clearAllGarbage();
angle::Result ensureSubmitFenceInitialized();
angle::Result startPrimaryCommandBuffer();
bool hasRecordedCommands();
void dumpCommandStreamDiagnostics();
void flushOutsideRenderPassCommands();
ANGLE_INLINE void onRenderPassFinished() { mRenderPassCommandBuffer = nullptr; }
void initIndexTypeMap();
std::array<DirtyBitHandler, DIRTY_BIT_MAX> mGraphicsDirtyBitHandlers;
std::array<DirtyBitHandler, DIRTY_BIT_MAX> mComputeDirtyBitHandlers;
vk::CommandBuffer *mRenderPassCommandBuffer;
vk::PipelineHelper *mCurrentGraphicsPipeline;
vk::PipelineAndSerial *mCurrentComputePipeline;
gl::PrimitiveMode mCurrentDrawMode;
WindowSurfaceVk *mCurrentWindowSurface;
// Records the current rotation of the surface (draw/read) framebuffer, derived from
// mCurrentWindowSurface->getPreTransform().
SurfaceRotationType mCurrentRotationDrawFramebuffer;
SurfaceRotationType mCurrentRotationReadFramebuffer;
// Keep a cached pipeline description structure that can be used to query the pipeline cache.
// Kept in a pointer so allocations can be aligned, and structs can be portably packed.
std::unique_ptr<vk::GraphicsPipelineDesc> mGraphicsPipelineDesc;
vk::GraphicsPipelineTransitionBits mGraphicsPipelineTransition;
// These pools are externally sychronized, so cannot be accessed from different
// threads simultaneously. Hence, we keep them in the ContextVk instead of the RendererVk.
// Note that this implementation would need to change in shared resource scenarios. Likely
// we'd instead share a single set of pools between the share groups.
vk::DynamicDescriptorPool mDriverUniformsDescriptorPool;
angle::PackedEnumMap<gl::QueryType, vk::DynamicQueryPool> mQueryPools;
// Dirty bits.
DirtyBits mGraphicsDirtyBits;
DirtyBits mComputeDirtyBits;
DirtyBits mNonIndexedDirtyBitsMask;
DirtyBits mIndexedDirtyBitsMask;
DirtyBits mNewGraphicsCommandBufferDirtyBits;
DirtyBits mNewComputeCommandBufferDirtyBits;
DirtyBits mNewGraphicsPipelineDirtyBits;
// Cached back-end objects.
VertexArrayVk *mVertexArray;
FramebufferVk *mDrawFramebuffer;
ProgramVk *mProgram;
ProgramPipelineVk *mProgramPipeline;
ProgramExecutableVk *mExecutable;
// occlusion query
QueryVk *mActiveQueryAnySamples;
QueryVk *mActiveQueryAnySamplesConservative;
// Graph resource used to record dispatch commands and hold resource dependencies.
vk::DispatchHelper mDispatcher;
// The offset we had the last time we bound the index buffer.
const GLvoid *mLastIndexBufferOffset;
gl::DrawElementsType mCurrentDrawElementsType;
angle::PackedEnumMap<gl::DrawElementsType, VkIndexType> mIndexTypeMap;
// Cache the current draw call's firstVertex to be passed to
// TransformFeedbackVk::getBufferOffsets. Unfortunately, gl_BaseVertex support in Vulkan is
// not yet ubiquitous, which would have otherwise removed the need for this value to be passed
// as a uniform.
GLint mXfbBaseVertex;
// Cache the current draw call's vertex count as well to support instanced draw calls
GLuint mXfbVertexCountPerInstance;
// Cached clear value/mask for color and depth/stencil.
VkClearValue mClearColorValue;
VkClearValue mClearDepthStencilValue;
VkColorComponentFlags mClearColorMask;
IncompleteTextureSet mIncompleteTextures;
// If the current surface bound to this context wants to have all rendering flipped vertically.
// Updated on calls to onMakeCurrent.
bool mFlipYForCurrentSurface;
bool mFlipViewportForDrawFramebuffer;
bool mFlipViewportForReadFramebuffer;
// If any host-visible buffer is written by the GPU since last submission, a barrier is inserted
// at the end of the command buffer to make that write available to the host.
bool mIsAnyHostVisibleBufferWritten;
// Whether this context should do seamful cube map sampling emulation.
bool mEmulateSeamfulCubeMapSampling;
// Whether this context should use the old version of the
// RewriteStructSamplers pass.
bool mUseOldRewriteStructSamplers;
angle::PackedEnumMap<PipelineType, DriverUniformsDescriptorSet> mDriverUniforms;
// This cache should also probably include the texture index (shader location) and array
// index (also in the shader). This info is used in the descriptor update step.
gl::ActiveTextureArray<vk::TextureUnit> mActiveTextures;
vk::TextureDescriptorDesc mActiveTexturesDesc;
gl::ActiveTextureArray<TextureVk *> mActiveImages;
// "Current Value" aka default vertex attribute state.
gl::AttributesMask mDirtyDefaultAttribsMask;
gl::AttribArray<vk::DynamicBuffer> mDefaultAttribBuffers;
// We use a single pool for recording commands. We also keep a free list for pool recycling.
vk::CommandPool mCommandPool;
CommandQueue mCommandQueue;
vk::GarbageList mCurrentGarbage;
RenderPassCache mRenderPassCache;
// mSubmitFence is the fence that's going to be signaled at the next submission. This is used
// to support SyncVk objects, which may outlive the context (as EGLSync objects).
//
// TODO(geofflang): this is in preparation for moving RendererVk functionality to ContextVk, and
// is otherwise unnecessary as the SyncVk objects don't actually outlive the renderer currently.
// http://anglebug.com/2701
vk::Shared<vk::Fence> mSubmitFence;
// Pool allocator used for command graph but may be expanded to other allocations
angle::PoolAllocator mPoolAllocator;
// When the command graph is disabled we record commands completely linearly. We have plans to
// reorder independent draws so that we can create fewer RenderPasses in some scenarios.
OutsideRenderPassCommandBuffer mOutsideRenderPassCommands;
RenderPassCommandBuffer mRenderPassCommands;
vk::PrimaryCommandBuffer mPrimaryCommands;
bool mHasPrimaryCommands;
// Internal shader library.
vk::ShaderLibrary mShaderLibrary;
UtilsVk mUtils;
bool mGpuEventsEnabled;
vk::DynamicQueryPool mGpuEventQueryPool;
// A list of queries that have yet to be turned into an event (their result is not yet
// available).
std::vector<GpuEventQuery> mInFlightGpuEventQueries;
// A list of gpu events since the last clock sync.
std::vector<GpuEvent> mGpuEvents;
// Semaphores that must be waited on in the next submission.
std::vector<VkSemaphore> mWaitSemaphores;
std::vector<VkPipelineStageFlags> mWaitSemaphoreStageMasks;
// Hold information from the last gpu clock sync for future gpu-to-cpu timestamp conversions.
GpuClockSyncInfo mGpuClockSync;
// The very first timestamp queried for a GPU event is used as origin, so event timestamps would
// have a value close to zero, to avoid losing 12 bits when converting these 64 bit values to
// double.
uint64_t mGpuEventTimestampOrigin;
// Used to count events for tracing.
uint32_t mPrimaryBufferCounter;
uint32_t mRenderPassCounter;
// Generators for texture & framebuffer serials.
SerialFactory mTextureSerialFactory;
SerialFactory mAttachmentImageSerialFactory;
gl::State::DirtyBits mPipelineDirtyBitsMask;
// List of all resources currently being used by this ContextVk's recorded commands.
vk::ResourceUseList mResourceUseList;
egl::ContextPriority mContextPriority;
std::vector<std::string> mCommandBufferDiagnostics;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_CONTEXTVK_H_