3421 lines
147 KiB
C++
3421 lines
147 KiB
C++
//
|
|
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
//
|
|
// vk_helpers:
|
|
// Helper utility classes that manage Vulkan resources.
|
|
|
|
#ifndef LIBANGLE_RENDERER_VULKAN_VK_HELPERS_H_
|
|
#define LIBANGLE_RENDERER_VULKAN_VK_HELPERS_H_
|
|
|
|
#include "common/MemoryBuffer.h"
|
|
#include "libANGLE/renderer/vulkan/Suballocation.h"
|
|
#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
|
|
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
|
|
|
|
#include <functional>
|
|
|
|
namespace gl
|
|
{
|
|
class ImageIndex;
|
|
} // namespace gl
|
|
|
|
namespace rx
|
|
{
|
|
namespace vk
|
|
{
|
|
constexpr VkBufferUsageFlags kVertexBufferUsageFlags =
|
|
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
constexpr VkBufferUsageFlags kIndexBufferUsageFlags =
|
|
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
constexpr VkBufferUsageFlags kIndirectBufferUsageFlags =
|
|
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
constexpr size_t kVertexBufferAlignment = 4;
|
|
constexpr size_t kIndexBufferAlignment = 4;
|
|
constexpr size_t kIndirectBufferAlignment = 4;
|
|
|
|
constexpr VkBufferUsageFlags kStagingBufferFlags =
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
constexpr size_t kStagingBufferSize = 1024 * 16;
|
|
|
|
constexpr VkImageCreateFlags kVkImageCreateFlagsNone = 0;
|
|
|
|
constexpr VkFilter kDefaultYCbCrChromaFilter = VK_FILTER_NEAREST;
|
|
|
|
constexpr VkPipelineStageFlags kSwapchainAcquireImageWaitStageFlags =
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | // First use is a blit command.
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | // First use is a draw command.
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT; // First use is a clear without scissor.
|
|
|
|
using StagingBufferOffsetArray = std::array<VkDeviceSize, 2>;
|
|
|
|
// A dynamic buffer is conceptually an infinitely long buffer. Each time you write to the buffer,
|
|
// you will always write to a previously unused portion. After a series of writes, you must flush
|
|
// the buffer data to the device. Buffer lifetime currently assumes that each new allocation will
|
|
// last as long or longer than each prior allocation.
|
|
//
|
|
// Dynamic buffers are used to implement a variety of data streaming operations in Vulkan, such
|
|
// as for immediate vertex array and element array data, uniform updates, and other dynamic data.
|
|
//
|
|
// Internally dynamic buffers keep a collection of VkBuffers. When we write past the end of a
|
|
// currently active VkBuffer we keep it until it is no longer in use. We then mark it available
|
|
// for future allocations in a free list.
|
|
class BufferHelper;
|
|
using BufferHelperPointerVector = std::vector<std::unique_ptr<BufferHelper>>;
|
|
|
|
class DynamicBuffer : angle::NonCopyable
|
|
{
|
|
public:
|
|
DynamicBuffer();
|
|
DynamicBuffer(DynamicBuffer &&other);
|
|
~DynamicBuffer();
|
|
|
|
void init(RendererVk *renderer,
|
|
VkBufferUsageFlags usage,
|
|
size_t alignment,
|
|
size_t initialSize,
|
|
bool hostVisible);
|
|
|
|
// This call will allocate a new region at the end of the current buffer. If it can't find
|
|
// enough space in the current buffer, it returns false. This gives caller a chance to deal with
|
|
// buffer switch that may occur with allocate call.
|
|
bool allocateFromCurrentBuffer(size_t sizeInBytes, BufferHelper **bufferHelperOut);
|
|
|
|
// This call will allocate a new region at the end of the buffer with default alignment. It
|
|
// internally may trigger a new buffer to be created (which is returned in the optional
|
|
// parameter `newBufferAllocatedOut`). The new region will be in the returned buffer at given
|
|
// offset.
|
|
angle::Result allocate(Context *context,
|
|
size_t sizeInBytes,
|
|
BufferHelper **bufferHelperOut,
|
|
bool *newBufferAllocatedOut);
|
|
|
|
// This releases resources when they might currently be in use.
|
|
void release(RendererVk *renderer);
|
|
|
|
// This adds in-flight buffers to the mResourceUseList in the share group and then releases
|
|
// them.
|
|
void updateQueueSerialAndReleaseInFlightBuffers(ContextVk *contextVk,
|
|
const QueueSerial &queueSerial);
|
|
|
|
// This frees resources immediately.
|
|
void destroy(RendererVk *renderer);
|
|
|
|
BufferHelper *getCurrentBuffer() const { return mBuffer.get(); }
|
|
|
|
// **Accumulate** an alignment requirement. A dynamic buffer is used as the staging buffer for
|
|
// image uploads, which can contain updates to unrelated mips, possibly with different formats.
|
|
// The staging buffer should have an alignment that can satisfy all those formats, i.e. it's the
|
|
// lcm of all alignments set in its lifetime.
|
|
void requireAlignment(RendererVk *renderer, size_t alignment);
|
|
size_t getAlignment() const { return mAlignment; }
|
|
|
|
// For testing only!
|
|
void setMinimumSizeForTesting(size_t minSize);
|
|
|
|
bool isCoherent() const
|
|
{
|
|
return (mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
|
|
}
|
|
|
|
bool valid() const { return mSize != 0; }
|
|
|
|
private:
|
|
void reset();
|
|
angle::Result allocateNewBuffer(Context *context);
|
|
|
|
VkBufferUsageFlags mUsage;
|
|
bool mHostVisible;
|
|
size_t mInitialSize;
|
|
std::unique_ptr<BufferHelper> mBuffer;
|
|
uint32_t mNextAllocationOffset;
|
|
size_t mSize;
|
|
size_t mAlignment;
|
|
VkMemoryPropertyFlags mMemoryPropertyFlags;
|
|
|
|
BufferHelperPointerVector mInFlightBuffers;
|
|
BufferHelperPointerVector mBufferFreeList;
|
|
};
|
|
|
|
// Class DescriptorSetHelper. This is a wrapper of VkDescriptorSet with GPU resource use tracking.
|
|
class DescriptorSetHelper final : public Resource
|
|
{
|
|
public:
|
|
DescriptorSetHelper(const VkDescriptorSet &descriptorSet) { mDescriptorSet = descriptorSet; }
|
|
DescriptorSetHelper(const ResourceUse &use, const VkDescriptorSet &descriptorSet)
|
|
{
|
|
mUse = use;
|
|
mDescriptorSet = descriptorSet;
|
|
}
|
|
DescriptorSetHelper(DescriptorSetHelper &&other) : Resource(std::move(other))
|
|
{
|
|
mDescriptorSet = other.mDescriptorSet;
|
|
other.mDescriptorSet = VK_NULL_HANDLE;
|
|
}
|
|
|
|
VkDescriptorSet getDescriptorSet() const { return mDescriptorSet; }
|
|
|
|
private:
|
|
VkDescriptorSet mDescriptorSet;
|
|
};
|
|
using DescriptorSetList = std::deque<DescriptorSetHelper>;
|
|
|
|
// Uses DescriptorPool to allocate descriptor sets as needed. If a descriptor pool becomes full, we
|
|
// allocate new pools internally as needed. RendererVk takes care of the lifetime of the discarded
|
|
// pools. Note that we used a fixed layout for descriptor pools in ANGLE.
|
|
|
|
// Shared handle to a descriptor pool. Each helper is allocated from the dynamic descriptor pool.
|
|
// Can be used to share descriptor pools between multiple ProgramVks and the ContextVk.
|
|
class CommandBufferHelperCommon;
|
|
|
|
class DescriptorPoolHelper final : public Resource
|
|
{
|
|
public:
|
|
DescriptorPoolHelper();
|
|
~DescriptorPoolHelper() override;
|
|
|
|
bool valid() { return mDescriptorPool.valid(); }
|
|
|
|
angle::Result init(Context *context,
|
|
const std::vector<VkDescriptorPoolSize> &poolSizesIn,
|
|
uint32_t maxSets);
|
|
void destroy(RendererVk *renderer);
|
|
void release(RendererVk *renderer);
|
|
|
|
bool allocateDescriptorSet(Context *context,
|
|
const DescriptorSetLayout &descriptorSetLayout,
|
|
VkDescriptorSet *descriptorSetsOut);
|
|
|
|
void addGarbage(DescriptorSetHelper &&garbage)
|
|
{
|
|
mValidDescriptorSets--;
|
|
mDescriptorSetGarbageList.emplace_back(std::move(garbage));
|
|
}
|
|
|
|
void onNewDescriptorSetAllocated(const vk::SharedDescriptorSetCacheKey &sharedCacheKey)
|
|
{
|
|
mDescriptorSetCacheManager.addKey(sharedCacheKey);
|
|
}
|
|
bool hasValidDescriptorSet() const { return mValidDescriptorSets != 0; }
|
|
|
|
private:
|
|
// Track the number of descriptorSets allocated out of this pool that are valid. DescriptorSets
|
|
// that have been allocated but in the mDescriptorSetGarbageList is considered as inactive.
|
|
uint32_t mValidDescriptorSets;
|
|
// Track the number of remaining descriptorSets in the pool that can be allocated.
|
|
uint32_t mFreeDescriptorSets;
|
|
DescriptorPool mDescriptorPool;
|
|
// Keeps track descriptorSets that has been released. Because freeing descriptorSet require
|
|
// DescriptorPool, we store individually released descriptor sets here instead of usual garbage
|
|
// list in the renderer to avoid complicated threading issues and other weirdness associated
|
|
// with pooled object destruction. This list is mutually exclusive with mDescriptorSetCache.
|
|
DescriptorSetList mDescriptorSetGarbageList;
|
|
// Manages the texture descriptor set cache that allocated from this pool
|
|
vk::DescriptorSetCacheManager mDescriptorSetCacheManager;
|
|
};
|
|
|
|
using RefCountedDescriptorPoolBinding = BindingPointer<DescriptorPoolHelper>;
|
|
|
|
class DynamicDescriptorPool final : angle::NonCopyable
|
|
{
|
|
public:
|
|
DynamicDescriptorPool();
|
|
~DynamicDescriptorPool();
|
|
|
|
DynamicDescriptorPool(DynamicDescriptorPool &&other);
|
|
DynamicDescriptorPool &operator=(DynamicDescriptorPool &&other);
|
|
|
|
// The DynamicDescriptorPool only handles one pool size at this time.
|
|
// Note that setSizes[i].descriptorCount is expected to be the number of descriptors in
|
|
// an individual set. The pool size will be calculated accordingly.
|
|
angle::Result init(Context *context,
|
|
const VkDescriptorPoolSize *setSizes,
|
|
size_t setSizeCount,
|
|
const DescriptorSetLayout &descriptorSetLayout);
|
|
void destroy(RendererVk *renderer);
|
|
|
|
bool valid() const { return !mDescriptorPools.empty(); }
|
|
|
|
// We use the descriptor type to help count the number of free sets.
|
|
// By convention, sets are indexed according to the constants in vk_cache_utils.h.
|
|
angle::Result allocateDescriptorSet(Context *context,
|
|
const DescriptorSetLayout &descriptorSetLayout,
|
|
RefCountedDescriptorPoolBinding *bindingOut,
|
|
VkDescriptorSet *descriptorSetOut);
|
|
|
|
angle::Result getOrAllocateDescriptorSet(Context *context,
|
|
CommandBufferHelperCommon *commandBufferHelper,
|
|
const DescriptorSetDesc &desc,
|
|
const DescriptorSetLayout &descriptorSetLayout,
|
|
RefCountedDescriptorPoolBinding *bindingOut,
|
|
VkDescriptorSet *descriptorSetOut,
|
|
SharedDescriptorSetCacheKey *sharedCacheKeyOut);
|
|
|
|
void releaseCachedDescriptorSet(ContextVk *contextVk, const DescriptorSetDesc &desc);
|
|
void destroyCachedDescriptorSet(RendererVk *renderer, const DescriptorSetDesc &desc);
|
|
|
|
template <typename Accumulator>
|
|
void accumulateDescriptorCacheStats(VulkanCacheType cacheType, Accumulator *accum) const
|
|
{
|
|
accum->accumulateCacheStats(cacheType, mCacheStats);
|
|
}
|
|
void resetDescriptorCacheStats() { mCacheStats.resetHitAndMissCount(); }
|
|
size_t getTotalCacheKeySizeBytes() const
|
|
{
|
|
return mDescriptorSetCache.getTotalCacheKeySizeBytes();
|
|
}
|
|
|
|
// Release the pool if it is no longer been used and contains no valid descriptorSet.
|
|
void checkAndReleaseUnusedPool(RendererVk *renderer, RefCountedDescriptorPoolHelper *pool);
|
|
|
|
// For testing only!
|
|
static uint32_t GetMaxSetsPerPoolForTesting();
|
|
static void SetMaxSetsPerPoolForTesting(uint32_t maxSetsPerPool);
|
|
static uint32_t GetMaxSetsPerPoolMultiplierForTesting();
|
|
static void SetMaxSetsPerPoolMultiplierForTesting(uint32_t maxSetsPerPool);
|
|
|
|
private:
|
|
angle::Result allocateNewPool(Context *context);
|
|
|
|
static constexpr uint32_t kMaxSetsPerPoolMax = 512;
|
|
static uint32_t mMaxSetsPerPool;
|
|
static uint32_t mMaxSetsPerPoolMultiplier;
|
|
size_t mCurrentPoolIndex;
|
|
std::vector<std::unique_ptr<RefCountedDescriptorPoolHelper>> mDescriptorPools;
|
|
std::vector<VkDescriptorPoolSize> mPoolSizes;
|
|
// This cached handle is used for verifying the layout being used to allocate descriptor sets
|
|
// from the pool matches the layout that the pool was created for, to ensure that the free
|
|
// descriptor count is accurate and new pools are created appropriately.
|
|
VkDescriptorSetLayout mCachedDescriptorSetLayout;
|
|
// Tracks cache for descriptorSet. Note that cached DescriptorSet can be reuse even if it is GPU
|
|
// busy.
|
|
DescriptorSetCache mDescriptorSetCache;
|
|
// Statistics for the cache.
|
|
CacheStats mCacheStats;
|
|
};
|
|
|
|
using RefCountedDescriptorPool = RefCounted<DynamicDescriptorPool>;
|
|
using DescriptorPoolPointer = BindingPointer<DynamicDescriptorPool>;
|
|
|
|
// Maps from a descriptor set layout (represented by DescriptorSetLayoutDesc) to a set of
|
|
// DynamicDescriptorPools. The purpose of the class is so multiple GL Programs can share descriptor
|
|
// set caches. We need to stratify the sets by the descriptor set layout to ensure compatibility.
|
|
class MetaDescriptorPool final : angle::NonCopyable
|
|
{
|
|
public:
|
|
MetaDescriptorPool();
|
|
~MetaDescriptorPool();
|
|
|
|
void destroy(RendererVk *rendererVk);
|
|
|
|
angle::Result bindCachedDescriptorPool(Context *context,
|
|
const DescriptorSetLayoutDesc &descriptorSetLayoutDesc,
|
|
uint32_t descriptorCountMultiplier,
|
|
DescriptorSetLayoutCache *descriptorSetLayoutCache,
|
|
DescriptorPoolPointer *descriptorPoolOut);
|
|
|
|
template <typename Accumulator>
|
|
void accumulateDescriptorCacheStats(VulkanCacheType cacheType, Accumulator *accum) const
|
|
{
|
|
for (const auto &iter : mPayload)
|
|
{
|
|
const vk::RefCountedDescriptorPool &pool = iter.second;
|
|
pool.get().accumulateDescriptorCacheStats(cacheType, accum);
|
|
}
|
|
}
|
|
|
|
void resetDescriptorCacheStats()
|
|
{
|
|
for (auto &iter : mPayload)
|
|
{
|
|
vk::RefCountedDescriptorPool &pool = iter.second;
|
|
pool.get().resetDescriptorCacheStats();
|
|
}
|
|
}
|
|
|
|
size_t getTotalCacheKeySizeBytes() const
|
|
{
|
|
size_t totalSize = 0;
|
|
|
|
for (const auto &iter : mPayload)
|
|
{
|
|
const RefCountedDescriptorPool &pool = iter.second;
|
|
totalSize += pool.get().getTotalCacheKeySizeBytes();
|
|
}
|
|
|
|
return totalSize;
|
|
}
|
|
|
|
private:
|
|
std::unordered_map<DescriptorSetLayoutDesc, RefCountedDescriptorPool> mPayload;
|
|
};
|
|
|
|
template <typename Pool>
|
|
class DynamicallyGrowingPool : angle::NonCopyable
|
|
{
|
|
public:
|
|
DynamicallyGrowingPool();
|
|
virtual ~DynamicallyGrowingPool();
|
|
|
|
bool isValid() { return mPoolSize > 0; }
|
|
|
|
protected:
|
|
angle::Result initEntryPool(Context *contextVk, uint32_t poolSize);
|
|
|
|
virtual void destroyPoolImpl(VkDevice device, Pool &poolToDestroy) = 0;
|
|
void destroyEntryPool(VkDevice device);
|
|
|
|
// Checks to see if any pool is already free, in which case it sets it as current pool and
|
|
// returns true.
|
|
bool findFreeEntryPool(ContextVk *contextVk);
|
|
|
|
// Allocates a new entry and initializes it with the given pool.
|
|
angle::Result allocateNewEntryPool(ContextVk *contextVk, Pool &&pool);
|
|
|
|
// Called by the implementation whenever an entry is freed.
|
|
void onEntryFreed(ContextVk *contextVk, size_t poolIndex, const ResourceUse &use);
|
|
|
|
const Pool &getPool(size_t index) const
|
|
{
|
|
return const_cast<DynamicallyGrowingPool *>(this)->getPool(index);
|
|
}
|
|
|
|
Pool &getPool(size_t index)
|
|
{
|
|
ASSERT(index < mPools.size());
|
|
return mPools[index].pool;
|
|
}
|
|
|
|
uint32_t getPoolSize() const { return mPoolSize; }
|
|
|
|
virtual angle::Result allocatePoolImpl(ContextVk *contextVk,
|
|
Pool &poolToAllocate,
|
|
uint32_t entriesToAllocate) = 0;
|
|
angle::Result allocatePoolEntries(ContextVk *contextVk,
|
|
uint32_t entryCount,
|
|
uint32_t *poolIndexOut,
|
|
uint32_t *currentEntryOut);
|
|
|
|
private:
|
|
// The pool size, to know when a pool is completely freed.
|
|
uint32_t mPoolSize;
|
|
|
|
struct PoolResource : public Resource
|
|
{
|
|
PoolResource(Pool &&poolIn, uint32_t freedCountIn);
|
|
PoolResource(PoolResource &&other);
|
|
|
|
Pool pool;
|
|
|
|
// A count corresponding to each pool indicating how many of its allocated entries
|
|
// have been freed. Once that value reaches mPoolSize for each pool, that pool is considered
|
|
// free and reusable. While keeping a bitset would allow allocation of each index, the
|
|
// slight runtime overhead of finding free indices is not worth the slight memory overhead
|
|
// of creating new pools when unnecessary.
|
|
uint32_t freedCount;
|
|
};
|
|
std::vector<PoolResource> mPools;
|
|
|
|
// Index into mPools indicating pool we are currently allocating from.
|
|
size_t mCurrentPool;
|
|
// Index inside mPools[mCurrentPool] indicating which index can be allocated next.
|
|
uint32_t mCurrentFreeEntry;
|
|
};
|
|
|
|
// DynamicQueryPool allocates indices out of QueryPool as needed. Once a QueryPool is exhausted,
|
|
// another is created. The query pools live permanently, but are recycled as indices get freed.
|
|
|
|
// These are arbitrary default sizes for query pools.
|
|
constexpr uint32_t kDefaultOcclusionQueryPoolSize = 64;
|
|
constexpr uint32_t kDefaultTimestampQueryPoolSize = 64;
|
|
constexpr uint32_t kDefaultTransformFeedbackQueryPoolSize = 128;
|
|
constexpr uint32_t kDefaultPrimitivesGeneratedQueryPoolSize = 128;
|
|
|
|
class QueryHelper;
|
|
|
|
class DynamicQueryPool final : public DynamicallyGrowingPool<QueryPool>
|
|
{
|
|
public:
|
|
DynamicQueryPool();
|
|
~DynamicQueryPool() override;
|
|
|
|
angle::Result init(ContextVk *contextVk, VkQueryType type, uint32_t poolSize);
|
|
void destroy(VkDevice device);
|
|
|
|
angle::Result allocateQuery(ContextVk *contextVk, QueryHelper *queryOut, uint32_t queryCount);
|
|
void freeQuery(ContextVk *contextVk, QueryHelper *query);
|
|
|
|
const QueryPool &getQueryPool(size_t index) const { return getPool(index); }
|
|
|
|
private:
|
|
angle::Result allocatePoolImpl(ContextVk *contextVk,
|
|
QueryPool &poolToAllocate,
|
|
uint32_t entriesToAllocate) override;
|
|
void destroyPoolImpl(VkDevice device, QueryPool &poolToDestroy) override;
|
|
|
|
// Information required to create new query pools
|
|
VkQueryType mQueryType;
|
|
};
|
|
|
|
// Stores the result of a Vulkan query call. XFB queries in particular store two result values.
|
|
class QueryResult final
|
|
{
|
|
public:
|
|
QueryResult(uint32_t intsPerResult) : mIntsPerResult(intsPerResult), mResults{} {}
|
|
|
|
void operator+=(const QueryResult &rhs)
|
|
{
|
|
mResults[0] += rhs.mResults[0];
|
|
mResults[1] += rhs.mResults[1];
|
|
}
|
|
|
|
size_t getDataSize() const { return mIntsPerResult * sizeof(uint64_t); }
|
|
void setResults(uint64_t *results, uint32_t queryCount);
|
|
uint64_t getResult(size_t index) const
|
|
{
|
|
ASSERT(index < mIntsPerResult);
|
|
return mResults[index];
|
|
}
|
|
|
|
static constexpr size_t kDefaultResultIndex = 0;
|
|
static constexpr size_t kTransformFeedbackPrimitivesWrittenIndex = 0;
|
|
static constexpr size_t kPrimitivesGeneratedIndex = 1;
|
|
|
|
private:
|
|
uint32_t mIntsPerResult;
|
|
std::array<uint64_t, 2> mResults;
|
|
};
|
|
|
|
// Queries in Vulkan are identified by the query pool and an index for a query within that pool.
|
|
// Unlike other pools, such as descriptor pools where an allocation returns an independent object
|
|
// from the pool, the query allocations are not done through a Vulkan function and are only an
|
|
// integer index.
|
|
//
|
|
// Furthermore, to support arbitrarily large number of queries, DynamicQueryPool creates query pools
|
|
// of a fixed size as needed and allocates indices within those pools.
|
|
//
|
|
// The QueryHelper class below keeps the pool and index pair together. For multiview, multiple
|
|
// consecutive query indices are implicitly written to by the driver, so the query count is
|
|
// additionally kept.
|
|
class QueryHelper final : public Resource
|
|
{
|
|
public:
|
|
QueryHelper();
|
|
~QueryHelper() override;
|
|
QueryHelper(QueryHelper &&rhs);
|
|
QueryHelper &operator=(QueryHelper &&rhs);
|
|
void init(const DynamicQueryPool *dynamicQueryPool,
|
|
const size_t queryPoolIndex,
|
|
uint32_t query,
|
|
uint32_t queryCount);
|
|
void deinit();
|
|
|
|
bool valid() const { return mDynamicQueryPool != nullptr; }
|
|
|
|
// Begin/end queries. These functions break the render pass.
|
|
angle::Result beginQuery(ContextVk *contextVk);
|
|
angle::Result endQuery(ContextVk *contextVk);
|
|
// Begin/end queries within a started render pass.
|
|
angle::Result beginRenderPassQuery(ContextVk *contextVk);
|
|
void endRenderPassQuery(ContextVk *contextVk);
|
|
|
|
angle::Result flushAndWriteTimestamp(ContextVk *contextVk);
|
|
// When syncing gpu/cpu time, main thread accesses primary directly
|
|
void writeTimestampToPrimary(ContextVk *contextVk, PrimaryCommandBuffer *primary);
|
|
// All other timestamp accesses should be made on outsideRenderPassCommandBuffer
|
|
void writeTimestamp(ContextVk *contextVk,
|
|
OutsideRenderPassCommandBuffer *outsideRenderPassCommandBuffer);
|
|
|
|
// Whether this query helper has generated and submitted any commands.
|
|
bool hasSubmittedCommands() const;
|
|
|
|
angle::Result getUint64ResultNonBlocking(ContextVk *contextVk,
|
|
QueryResult *resultOut,
|
|
bool *availableOut);
|
|
angle::Result getUint64Result(ContextVk *contextVk, QueryResult *resultOut);
|
|
|
|
private:
|
|
friend class DynamicQueryPool;
|
|
const QueryPool &getQueryPool() const
|
|
{
|
|
ASSERT(valid());
|
|
return mDynamicQueryPool->getQueryPool(mQueryPoolIndex);
|
|
}
|
|
|
|
// Reset needs to always be done outside a render pass, which may be different from the
|
|
// passed-in command buffer (which could be the render pass').
|
|
template <typename CommandBufferT>
|
|
void beginQueryImpl(ContextVk *contextVk,
|
|
OutsideRenderPassCommandBuffer *resetCommandBuffer,
|
|
CommandBufferT *commandBuffer);
|
|
template <typename CommandBufferT>
|
|
void endQueryImpl(ContextVk *contextVk, CommandBufferT *commandBuffer);
|
|
template <typename CommandBufferT>
|
|
void resetQueryPoolImpl(ContextVk *contextVk,
|
|
const QueryPool &queryPool,
|
|
CommandBufferT *commandBuffer);
|
|
VkResult getResultImpl(ContextVk *contextVk,
|
|
const VkQueryResultFlags flags,
|
|
QueryResult *resultOut);
|
|
|
|
const DynamicQueryPool *mDynamicQueryPool;
|
|
size_t mQueryPoolIndex;
|
|
uint32_t mQuery;
|
|
uint32_t mQueryCount;
|
|
|
|
enum class QueryStatus
|
|
{
|
|
Inactive,
|
|
Active,
|
|
Ended
|
|
};
|
|
QueryStatus mStatus;
|
|
};
|
|
|
|
// Semaphores that are allocated from the semaphore pool are encapsulated in a helper object,
|
|
// keeping track of where in the pool they are allocated from.
|
|
class SemaphoreHelper final : angle::NonCopyable
|
|
{
|
|
public:
|
|
SemaphoreHelper();
|
|
~SemaphoreHelper();
|
|
|
|
SemaphoreHelper(SemaphoreHelper &&other);
|
|
SemaphoreHelper &operator=(SemaphoreHelper &&other);
|
|
|
|
void init(const size_t semaphorePoolIndex, const Semaphore *semaphore);
|
|
void deinit();
|
|
|
|
const Semaphore *getSemaphore() const { return mSemaphore; }
|
|
|
|
// Used only by DynamicSemaphorePool.
|
|
size_t getSemaphorePoolIndex() const { return mSemaphorePoolIndex; }
|
|
|
|
private:
|
|
size_t mSemaphorePoolIndex;
|
|
const Semaphore *mSemaphore;
|
|
};
|
|
|
|
// This defines enum for VkPipelineStageFlagBits so that we can use it to compare and index into
|
|
// array.
|
|
enum class PipelineStage : uint16_t
|
|
{
|
|
// Bellow are ordered based on Graphics Pipeline Stages
|
|
TopOfPipe = 0,
|
|
DrawIndirect = 1,
|
|
VertexInput = 2,
|
|
VertexShader = 3,
|
|
TessellationControl = 4,
|
|
TessellationEvaluation = 5,
|
|
GeometryShader = 6,
|
|
TransformFeedback = 7,
|
|
EarlyFragmentTest = 8,
|
|
FragmentShader = 9,
|
|
LateFragmentTest = 10,
|
|
ColorAttachmentOutput = 11,
|
|
|
|
// Compute specific pipeline Stage
|
|
ComputeShader = 12,
|
|
|
|
// Transfer specific pipeline Stage
|
|
Transfer = 13,
|
|
BottomOfPipe = 14,
|
|
|
|
// Host specific pipeline stage
|
|
Host = 15,
|
|
|
|
InvalidEnum = 16,
|
|
EnumCount = InvalidEnum,
|
|
};
|
|
using PipelineStagesMask = angle::PackedEnumBitSet<PipelineStage, uint16_t>;
|
|
|
|
PipelineStage GetPipelineStage(gl::ShaderType stage);
|
|
|
|
// This wraps data and API for vkCmdPipelineBarrier call
|
|
class PipelineBarrier : angle::NonCopyable
|
|
{
|
|
public:
|
|
PipelineBarrier()
|
|
: mSrcStageMask(0),
|
|
mDstStageMask(0),
|
|
mMemoryBarrierSrcAccess(0),
|
|
mMemoryBarrierDstAccess(0),
|
|
mImageMemoryBarriers()
|
|
{}
|
|
~PipelineBarrier() = default;
|
|
|
|
bool isEmpty() const { return mImageMemoryBarriers.empty() && mMemoryBarrierDstAccess == 0; }
|
|
|
|
void execute(PrimaryCommandBuffer *primary)
|
|
{
|
|
if (isEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Issue vkCmdPipelineBarrier call
|
|
VkMemoryBarrier memoryBarrier = {};
|
|
uint32_t memoryBarrierCount = 0;
|
|
if (mMemoryBarrierDstAccess != 0)
|
|
{
|
|
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
|
memoryBarrier.srcAccessMask = mMemoryBarrierSrcAccess;
|
|
memoryBarrier.dstAccessMask = mMemoryBarrierDstAccess;
|
|
memoryBarrierCount++;
|
|
}
|
|
primary->pipelineBarrier(
|
|
mSrcStageMask, mDstStageMask, 0, memoryBarrierCount, &memoryBarrier, 0, nullptr,
|
|
static_cast<uint32_t>(mImageMemoryBarriers.size()), mImageMemoryBarriers.data());
|
|
|
|
reset();
|
|
}
|
|
|
|
// merge two barriers into one
|
|
void merge(PipelineBarrier *other)
|
|
{
|
|
mSrcStageMask |= other->mSrcStageMask;
|
|
mDstStageMask |= other->mDstStageMask;
|
|
mMemoryBarrierSrcAccess |= other->mMemoryBarrierSrcAccess;
|
|
mMemoryBarrierDstAccess |= other->mMemoryBarrierDstAccess;
|
|
mImageMemoryBarriers.insert(mImageMemoryBarriers.end(), other->mImageMemoryBarriers.begin(),
|
|
other->mImageMemoryBarriers.end());
|
|
other->reset();
|
|
}
|
|
|
|
void mergeMemoryBarrier(VkPipelineStageFlags srcStageMask,
|
|
VkPipelineStageFlags dstStageMask,
|
|
VkAccessFlags srcAccess,
|
|
VkAccessFlags dstAccess)
|
|
{
|
|
mSrcStageMask |= srcStageMask;
|
|
mDstStageMask |= dstStageMask;
|
|
mMemoryBarrierSrcAccess |= srcAccess;
|
|
mMemoryBarrierDstAccess |= dstAccess;
|
|
}
|
|
|
|
void mergeImageBarrier(VkPipelineStageFlags srcStageMask,
|
|
VkPipelineStageFlags dstStageMask,
|
|
const VkImageMemoryBarrier &imageMemoryBarrier)
|
|
{
|
|
ASSERT(imageMemoryBarrier.pNext == nullptr);
|
|
mSrcStageMask |= srcStageMask;
|
|
mDstStageMask |= dstStageMask;
|
|
mImageMemoryBarriers.push_back(imageMemoryBarrier);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
mSrcStageMask = 0;
|
|
mDstStageMask = 0;
|
|
mMemoryBarrierSrcAccess = 0;
|
|
mMemoryBarrierDstAccess = 0;
|
|
mImageMemoryBarriers.clear();
|
|
}
|
|
|
|
void addDiagnosticsString(std::ostringstream &out) const;
|
|
|
|
private:
|
|
VkPipelineStageFlags mSrcStageMask;
|
|
VkPipelineStageFlags mDstStageMask;
|
|
VkAccessFlags mMemoryBarrierSrcAccess;
|
|
VkAccessFlags mMemoryBarrierDstAccess;
|
|
std::vector<VkImageMemoryBarrier> mImageMemoryBarriers;
|
|
};
|
|
using PipelineBarrierArray = angle::PackedEnumMap<PipelineStage, PipelineBarrier>;
|
|
|
|
enum class MemoryCoherency
|
|
{
|
|
NonCoherent,
|
|
Coherent
|
|
};
|
|
|
|
enum class MemoryHostVisibility
|
|
{
|
|
NonVisible,
|
|
Visible
|
|
};
|
|
|
|
class BufferHelper : public ReadWriteResource
|
|
{
|
|
public:
|
|
BufferHelper();
|
|
~BufferHelper() override;
|
|
|
|
BufferHelper(BufferHelper &&other);
|
|
BufferHelper &operator=(BufferHelper &&other);
|
|
|
|
angle::Result init(vk::Context *context,
|
|
const VkBufferCreateInfo &createInfo,
|
|
VkMemoryPropertyFlags memoryPropertyFlags);
|
|
angle::Result initExternal(ContextVk *contextVk,
|
|
VkMemoryPropertyFlags memoryProperties,
|
|
const VkBufferCreateInfo &requestedCreateInfo,
|
|
GLeglClientBufferEXT clientBuffer);
|
|
angle::Result initSuballocation(ContextVk *contextVk,
|
|
uint32_t memoryTypeIndex,
|
|
size_t size,
|
|
size_t alignment,
|
|
BufferUsageType usageType);
|
|
|
|
// Helper functions to initialize a buffer for a specific usage
|
|
// Suballocate a buffer with alignment good for shader storage or copyBuffer .
|
|
angle::Result allocateForVertexConversion(ContextVk *contextVk,
|
|
size_t size,
|
|
MemoryHostVisibility hostVisibility);
|
|
// Suballocate a host visible buffer with alignment good for copyBuffer .
|
|
angle::Result allocateForCopyBuffer(ContextVk *contextVk,
|
|
size_t size,
|
|
MemoryCoherency coherency);
|
|
// Suballocate a host visible buffer with alignment good for copyImage .
|
|
angle::Result allocateForCopyImage(ContextVk *contextVk,
|
|
size_t size,
|
|
MemoryCoherency coherency,
|
|
angle::FormatID formatId,
|
|
VkDeviceSize *offset,
|
|
uint8_t **dataPtr);
|
|
|
|
void destroy(RendererVk *renderer);
|
|
void release(RendererVk *renderer);
|
|
void releaseBufferAndDescriptorSetCache(ContextVk *contextVk);
|
|
|
|
BufferSerial getBufferSerial() const { return mSerial; }
|
|
BufferSerial getBlockSerial() const
|
|
{
|
|
ASSERT(mSuballocation.valid());
|
|
return mSuballocation.getBlockSerial();
|
|
}
|
|
BufferBlock *getBufferBlock() const { return mSuballocation.getBufferBlock(); }
|
|
bool valid() const { return mSuballocation.valid(); }
|
|
const Buffer &getBuffer() const { return mSuballocation.getBuffer(); }
|
|
VkDeviceSize getOffset() const { return mSuballocation.getOffset(); }
|
|
VkDeviceSize getSize() const { return mSuballocation.getSize(); }
|
|
VkMemoryMapFlags getMemoryPropertyFlags() const
|
|
{
|
|
return mSuballocation.getMemoryPropertyFlags();
|
|
}
|
|
uint8_t *getMappedMemory() const
|
|
{
|
|
ASSERT(isMapped());
|
|
return mSuballocation.getMappedMemory();
|
|
}
|
|
// Returns the main buffer block's pointer.
|
|
uint8_t *getBlockMemory() const { return mSuballocation.getBlockMemory(); }
|
|
VkDeviceSize getBlockMemorySize() const { return mSuballocation.getBlockMemorySize(); }
|
|
bool isHostVisible() const { return mSuballocation.isHostVisible(); }
|
|
bool isCoherent() const { return mSuballocation.isCoherent(); }
|
|
|
|
bool isMapped() const { return mSuballocation.isMapped(); }
|
|
|
|
// Also implicitly sets up the correct barriers.
|
|
angle::Result copyFromBuffer(ContextVk *contextVk,
|
|
BufferHelper *srcBuffer,
|
|
uint32_t regionCount,
|
|
const VkBufferCopy *copyRegions);
|
|
|
|
angle::Result map(Context *context, uint8_t **ptrOut);
|
|
angle::Result mapWithOffset(ContextVk *contextVk, uint8_t **ptrOut, size_t offset);
|
|
void unmap(RendererVk *renderer) {}
|
|
// After a sequence of writes, call flush to ensure the data is visible to the device.
|
|
angle::Result flush(RendererVk *renderer);
|
|
angle::Result flush(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size);
|
|
// After a sequence of writes, call invalidate to ensure the data is visible to the host.
|
|
angle::Result invalidate(RendererVk *renderer);
|
|
angle::Result invalidate(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size);
|
|
|
|
void changeQueue(uint32_t newQueueFamilyIndex, OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
// Performs an ownership transfer from an external instance or API.
|
|
void acquireFromExternal(ContextVk *contextVk,
|
|
uint32_t externalQueueFamilyIndex,
|
|
uint32_t rendererQueueFamilyIndex,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
// Performs an ownership transfer to an external instance or API.
|
|
void releaseToExternal(ContextVk *contextVk,
|
|
uint32_t rendererQueueFamilyIndex,
|
|
uint32_t externalQueueFamilyIndex,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
// Returns true if the image is owned by an external API or instance.
|
|
bool isReleasedToExternal() const;
|
|
|
|
bool recordReadBarrier(VkAccessFlags readAccessType,
|
|
VkPipelineStageFlags readStage,
|
|
PipelineBarrier *barrier);
|
|
|
|
bool recordWriteBarrier(VkAccessFlags writeAccessType,
|
|
VkPipelineStageFlags writeStage,
|
|
PipelineBarrier *barrier);
|
|
void fillWithColor(const angle::Color<uint8_t> &color,
|
|
const gl::InternalFormat &internalFormat);
|
|
|
|
// Special handling for VertexArray code so that we can create a dedicated VkBuffer for the
|
|
// sub-range of memory of the actual buffer data size that user requested (i.e, excluding extra
|
|
// paddings that we added for alignment, which will not get zero filled).
|
|
const Buffer &getBufferForVertexArray(ContextVk *contextVk,
|
|
VkDeviceSize actualDataSize,
|
|
VkDeviceSize *offsetOut);
|
|
|
|
void onNewDescriptorSet(const SharedDescriptorSetCacheKey &sharedCacheKey)
|
|
{
|
|
mDescriptorSetCacheManager.addKey(sharedCacheKey);
|
|
}
|
|
|
|
private:
|
|
void initializeBarrierTracker(Context *context);
|
|
angle::Result initializeNonZeroMemory(Context *context,
|
|
VkBufferUsageFlags usage,
|
|
VkDeviceSize size);
|
|
|
|
// Only called by DynamicBuffer.
|
|
friend class DynamicBuffer;
|
|
void setSuballocationOffsetAndSize(VkDeviceSize offset, VkDeviceSize size)
|
|
{
|
|
mSuballocation.setOffsetAndSize(offset, size);
|
|
}
|
|
|
|
// Suballocation object.
|
|
BufferSuballocation mSuballocation;
|
|
// This normally is invalid. We always use the BufferBlock's buffer and offset combination. But
|
|
// when robust resource init is enabled, we may want to create a dedicated VkBuffer for the
|
|
// suballocation so that vulkan driver will ensure no access beyond this sub-range. In that
|
|
// case, this VkBuffer will be created lazily as needed.
|
|
Buffer mBufferForVertexArray;
|
|
|
|
// For memory barriers.
|
|
uint32_t mCurrentQueueFamilyIndex;
|
|
VkFlags mCurrentWriteAccess;
|
|
VkFlags mCurrentReadAccess;
|
|
VkPipelineStageFlags mCurrentWriteStages;
|
|
VkPipelineStageFlags mCurrentReadStages;
|
|
|
|
BufferSerial mSerial;
|
|
// Manages the descriptorSet cache that created with this BufferHelper object.
|
|
DescriptorSetCacheManager mDescriptorSetCacheManager;
|
|
};
|
|
|
|
class BufferPool : angle::NonCopyable
|
|
{
|
|
public:
|
|
BufferPool();
|
|
BufferPool(BufferPool &&other);
|
|
~BufferPool();
|
|
|
|
// Init that gives the ability to pass in specified memory property flags for the buffer.
|
|
void initWithFlags(RendererVk *renderer,
|
|
vma::VirtualBlockCreateFlags flags,
|
|
VkBufferUsageFlags usage,
|
|
VkDeviceSize initialSize,
|
|
uint32_t memoryTypeIndex,
|
|
VkMemoryPropertyFlags memoryProperty);
|
|
|
|
angle::Result allocateBuffer(Context *context,
|
|
VkDeviceSize sizeInBytes,
|
|
VkDeviceSize alignment,
|
|
BufferSuballocation *suballocation);
|
|
|
|
// Frees resources immediately, or orphan the non-empty BufferBlocks if allowed. If orphan is
|
|
// not allowed, it will assert if BufferBlock is still not empty.
|
|
void destroy(RendererVk *renderer, bool orphanAllowed);
|
|
// Remove and destroy empty BufferBlocks
|
|
void pruneEmptyBuffers(RendererVk *renderer);
|
|
|
|
bool valid() const { return mSize != 0; }
|
|
|
|
void addStats(std::ostringstream *out) const;
|
|
size_t getBufferCount() const { return mBufferBlocks.size() + mEmptyBufferBlocks.size(); }
|
|
VkDeviceSize getMemorySize() const { return mTotalMemorySize; }
|
|
|
|
private:
|
|
angle::Result allocateNewBuffer(Context *context, VkDeviceSize sizeInBytes);
|
|
VkDeviceSize getTotalEmptyMemorySize() const;
|
|
|
|
vma::VirtualBlockCreateFlags mVirtualBlockCreateFlags;
|
|
VkBufferUsageFlags mUsage;
|
|
bool mHostVisible;
|
|
VkDeviceSize mSize;
|
|
uint32_t mMemoryTypeIndex;
|
|
VkDeviceSize mTotalMemorySize;
|
|
BufferBlockPointerVector mBufferBlocks;
|
|
BufferBlockPointerVector mEmptyBufferBlocks;
|
|
// Tracks the number of new buffers needed for suballocation since last pruneEmptyBuffers call.
|
|
// We will use this heuristic information to decide how many empty buffers to keep around.
|
|
size_t mNumberOfNewBuffersNeededSinceLastPrune;
|
|
// max size to go down the suballocation code path. Any allocation greater or equal this size
|
|
// will call into vulkan directly to allocate a dedicated VkDeviceMemory.
|
|
static constexpr size_t kMaxBufferSizeForSuballocation = 4 * 1024 * 1024;
|
|
};
|
|
using BufferPoolPointerArray = std::array<std::unique_ptr<BufferPool>, VK_MAX_MEMORY_TYPES>;
|
|
|
|
// Stores clear value In packed attachment index
|
|
class PackedClearValuesArray final
|
|
{
|
|
public:
|
|
PackedClearValuesArray();
|
|
~PackedClearValuesArray();
|
|
|
|
PackedClearValuesArray(const PackedClearValuesArray &other);
|
|
PackedClearValuesArray &operator=(const PackedClearValuesArray &rhs);
|
|
void store(PackedAttachmentIndex index,
|
|
VkImageAspectFlags aspectFlags,
|
|
const VkClearValue &clearValue);
|
|
void storeNoDepthStencil(PackedAttachmentIndex index, const VkClearValue &clearValue);
|
|
const VkClearValue &operator[](PackedAttachmentIndex index) const
|
|
{
|
|
return mValues[index.get()];
|
|
}
|
|
const VkClearValue *data() const { return mValues.data(); }
|
|
|
|
private:
|
|
gl::AttachmentArray<VkClearValue> mValues;
|
|
};
|
|
|
|
class ImageHelper;
|
|
|
|
// Reference to a render pass attachment (color or depth/stencil) alongside render-pass-related
|
|
// tracking such as when the attachment is last written to or invalidated. This is used to
|
|
// determine loadOp and storeOp of the attachment, and enables optimizations that need to know
|
|
// how the attachment has been used.
|
|
class RenderPassAttachment final
|
|
{
|
|
public:
|
|
RenderPassAttachment();
|
|
~RenderPassAttachment() = default;
|
|
|
|
void init(ImageHelper *image,
|
|
UniqueSerial imageSiblingSerial,
|
|
gl::LevelIndex levelIndex,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlagBits aspect);
|
|
void reset();
|
|
|
|
void onAccess(ResourceAccess access, uint32_t currentCmdCount);
|
|
void invalidate(const gl::Rectangle &invalidateArea,
|
|
bool isAttachmentEnabled,
|
|
uint32_t currentCmdCount);
|
|
void onRenderAreaGrowth(ContextVk *contextVk, const gl::Rectangle &newRenderArea);
|
|
void finalizeLoadStore(Context *context,
|
|
uint32_t currentCmdCount,
|
|
bool hasUnresolveAttachment,
|
|
RenderPassLoadOp *loadOp,
|
|
RenderPassStoreOp *storeOp,
|
|
bool *isInvalidatedOut);
|
|
void restoreContent();
|
|
bool hasAnyAccess() const { return mAccess != ResourceAccess::Unused; }
|
|
bool hasWriteAccess() const { return HasResourceWriteAccess(mAccess); }
|
|
|
|
ImageHelper *getImage() { return mImage; }
|
|
|
|
bool hasImage(const ImageHelper *image, UniqueSerial imageSiblingSerial) const
|
|
{
|
|
// Compare values because we do want that invalid serials compare equal.
|
|
return mImage == image && mImageSiblingSerial.getValue() == imageSiblingSerial.getValue();
|
|
}
|
|
|
|
private:
|
|
bool hasWriteAfterInvalidate(uint32_t currentCmdCount) const;
|
|
bool isInvalidated(uint32_t currentCmdCount) const;
|
|
bool onAccessImpl(ResourceAccess access, uint32_t currentCmdCount);
|
|
|
|
// The attachment image itself
|
|
ImageHelper *mImage;
|
|
// Invalid or serial of EGLImage/Surface sibling target.
|
|
UniqueSerial mImageSiblingSerial;
|
|
// The subresource used in the render pass
|
|
gl::LevelIndex mLevelIndex;
|
|
uint32_t mLayerIndex;
|
|
uint32_t mLayerCount;
|
|
VkImageAspectFlagBits mAspect;
|
|
// Tracks the highest access during the entire render pass (Write being the highest), excluding
|
|
// clear through loadOp. This allows loadOp=Clear to be optimized out when we find out that the
|
|
// attachment is not used in the render pass at all and storeOp=DontCare, or that a
|
|
// mid-render-pass clear could be hoisted to loadOp=Clear.
|
|
ResourceAccess mAccess;
|
|
// The index of the last draw command after which the attachment is invalidated
|
|
uint32_t mInvalidatedCmdCount;
|
|
// The index of the last draw command after which the attachment output is disabled
|
|
uint32_t mDisabledCmdCount;
|
|
// The area that has been invalidated
|
|
gl::Rectangle mInvalidateArea;
|
|
};
|
|
|
|
// Stores RenderPassAttachment In packed attachment index
|
|
class PackedRenderPassAttachmentArray final
|
|
{
|
|
public:
|
|
PackedRenderPassAttachmentArray() : mAttachments{} {}
|
|
~PackedRenderPassAttachmentArray() = default;
|
|
RenderPassAttachment &operator[](PackedAttachmentIndex index)
|
|
{
|
|
return mAttachments[index.get()];
|
|
}
|
|
void reset()
|
|
{
|
|
for (RenderPassAttachment &attachment : mAttachments)
|
|
{
|
|
attachment.reset();
|
|
}
|
|
}
|
|
|
|
private:
|
|
gl::AttachmentArray<RenderPassAttachment> mAttachments;
|
|
};
|
|
|
|
class SecondaryCommandBufferCollector final
|
|
{
|
|
public:
|
|
SecondaryCommandBufferCollector() = default;
|
|
SecondaryCommandBufferCollector(const SecondaryCommandBufferCollector &) = delete;
|
|
SecondaryCommandBufferCollector(SecondaryCommandBufferCollector &&) = default;
|
|
void operator=(const SecondaryCommandBufferCollector &) = delete;
|
|
SecondaryCommandBufferCollector &operator=(SecondaryCommandBufferCollector &&) = default;
|
|
~SecondaryCommandBufferCollector() { ASSERT(empty()); }
|
|
|
|
void collectCommandBuffer(priv::SecondaryCommandBuffer &&commandBuffer);
|
|
void collectCommandBuffer(VulkanSecondaryCommandBuffer &&commandBuffer);
|
|
void retireCommandBuffers();
|
|
|
|
bool empty() const { return mCollectedCommandBuffers.empty(); }
|
|
|
|
private:
|
|
std::vector<VulkanSecondaryCommandBuffer> mCollectedCommandBuffers;
|
|
};
|
|
|
|
struct CommandsState
|
|
{
|
|
std::vector<VkSemaphore> waitSemaphores;
|
|
std::vector<VkPipelineStageFlags> waitSemaphoreStageMasks;
|
|
PrimaryCommandBuffer primaryCommands;
|
|
SecondaryCommandBufferCollector secondaryCommands;
|
|
};
|
|
|
|
// How the ImageHelper object is being used by the renderpass
|
|
enum class RenderPassUsage
|
|
{
|
|
// Attached to the render taget of the current renderpass commands. It could be read/write or
|
|
// read only access.
|
|
RenderTargetAttachment,
|
|
// This is special case of RenderTargetAttachment where the render target access is read only.
|
|
// Right now it is only tracked for depth stencil attachment
|
|
DepthReadOnlyAttachment,
|
|
StencilReadOnlyAttachment,
|
|
// Attached to the texture sampler of the current renderpass commands
|
|
ColorTextureSampler,
|
|
DepthTextureSampler,
|
|
StencilTextureSampler,
|
|
|
|
InvalidEnum,
|
|
EnumCount = InvalidEnum,
|
|
};
|
|
using RenderPassUsageFlags = angle::PackedEnumBitSet<RenderPassUsage, uint16_t>;
|
|
|
|
// The following are used to help track the state of an invalidated attachment.
|
|
// This value indicates an "infinite" CmdCount that is not valid for comparing
|
|
constexpr uint32_t kInfiniteCmdCount = 0xFFFFFFFF;
|
|
|
|
// CommandBufferHelperCommon and derivatives OutsideRenderPassCommandBufferHelper and
|
|
// RenderPassCommandBufferHelper wrap the outside/inside render pass secondary command buffers,
|
|
// together with other information such as barriers to issue before the command buffer, tracking of
|
|
// resource usages, etc. When the asyncCommandQueue feature is enabled, objects of these classes
|
|
// are handed off to the worker thread to be executed on the primary command buffer.
|
|
class CommandBufferHelperCommon : angle::NonCopyable
|
|
{
|
|
public:
|
|
void bufferWrite(ContextVk *contextVk,
|
|
VkAccessFlags writeAccessType,
|
|
PipelineStage writeStage,
|
|
BufferHelper *buffer);
|
|
|
|
bool usesBuffer(const BufferHelper &buffer) const
|
|
{
|
|
return buffer.usedByCommandBuffer(mQueueSerial);
|
|
}
|
|
|
|
bool usesBufferForWrite(const BufferHelper &buffer) const
|
|
{
|
|
return buffer.writtenByCommandBuffer(mQueueSerial);
|
|
}
|
|
|
|
void executeBarriers(const angle::FeaturesVk &features, CommandsState *commandsState);
|
|
|
|
// The markOpen and markClosed functions are to aid in proper use of the *CommandBufferHelper.
|
|
// saw invalid use due to threading issues that can be easily caught by marking when it's safe
|
|
// (open) to write to the command buffer.
|
|
#if !defined(ANGLE_ENABLE_ASSERTS)
|
|
void markOpen() {}
|
|
void markClosed() {}
|
|
#endif
|
|
|
|
void setHasShaderStorageOutput() { mHasShaderStorageOutput = true; }
|
|
bool hasShaderStorageOutput() const { return mHasShaderStorageOutput; }
|
|
|
|
bool hasGLMemoryBarrierIssued() const { return mHasGLMemoryBarrierIssued; }
|
|
|
|
void retainResource(Resource *resource) { resource->setQueueSerial(mQueueSerial); }
|
|
|
|
void retainResourceForWrite(ReadWriteResource *writeResource)
|
|
{
|
|
writeResource->setWriteQueueSerial(mQueueSerial);
|
|
}
|
|
|
|
const QueueSerial &getQueueSerial() const { return mQueueSerial; }
|
|
|
|
void setAcquireNextImageSemaphore(VkSemaphore semaphore)
|
|
{
|
|
ASSERT(semaphore != VK_NULL_HANDLE);
|
|
ASSERT(!mAcquireNextImageSemaphore.valid());
|
|
mAcquireNextImageSemaphore.setHandle(semaphore);
|
|
}
|
|
|
|
// Dumping the command stream is disabled by default.
|
|
static constexpr bool kEnableCommandStreamDiagnostics = false;
|
|
|
|
protected:
|
|
CommandBufferHelperCommon();
|
|
~CommandBufferHelperCommon();
|
|
|
|
void initializeImpl();
|
|
|
|
void resetImpl();
|
|
|
|
template <class DerivedT>
|
|
angle::Result attachCommandPoolImpl(Context *context, SecondaryCommandPool *commandPool);
|
|
template <class DerivedT, bool kIsRenderPassBuffer>
|
|
angle::Result detachCommandPoolImpl(Context *context, SecondaryCommandPool **commandPoolOut);
|
|
template <class DerivedT>
|
|
void releaseCommandPoolImpl();
|
|
|
|
template <class DerivedT>
|
|
void attachAllocatorImpl(SecondaryCommandMemoryAllocator *allocator);
|
|
template <class DerivedT>
|
|
SecondaryCommandMemoryAllocator *detachAllocatorImpl();
|
|
|
|
template <class DerivedT>
|
|
void assertCanBeRecycledImpl();
|
|
|
|
void imageReadImpl(ContextVk *contextVk,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout,
|
|
ImageHelper *image);
|
|
void imageWriteImpl(ContextVk *contextVk,
|
|
gl::LevelIndex level,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout,
|
|
ImageHelper *image);
|
|
|
|
void updateImageLayoutAndBarrier(Context *context,
|
|
ImageHelper *image,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout);
|
|
|
|
void addCommandDiagnosticsCommon(std::ostringstream *out);
|
|
|
|
// Allocator used by this class.
|
|
SecondaryCommandBlockAllocator mCommandAllocator;
|
|
|
|
// Barriers to be executed before the command buffer.
|
|
PipelineBarrierArray mPipelineBarriers;
|
|
PipelineStagesMask mPipelineBarrierMask;
|
|
|
|
// The command pool *CommandBufferHelper::mCommandBuffer is allocated from. Only used with
|
|
// Vulkan secondary command buffers (as opposed to ANGLE's SecondaryCommandBuffer).
|
|
SecondaryCommandPool *mCommandPool;
|
|
|
|
// Whether the command buffers contains any draw/dispatch calls that possibly output data
|
|
// through storage buffers and images. This is used to determine whether glMemoryBarrier*
|
|
// should flush the command buffer.
|
|
bool mHasShaderStorageOutput;
|
|
// Whether glMemoryBarrier has been called while commands are recorded in this command buffer.
|
|
// This is used to know when to check and potentially flush the command buffer if storage
|
|
// buffers and images are used in it.
|
|
bool mHasGLMemoryBarrierIssued;
|
|
|
|
// Tracks resources used in the command buffer.
|
|
QueueSerial mQueueSerial;
|
|
|
|
// Only used for swapChain images
|
|
Semaphore mAcquireNextImageSemaphore;
|
|
};
|
|
|
|
class SecondaryCommandBufferCollector;
|
|
|
|
class OutsideRenderPassCommandBufferHelper final : public CommandBufferHelperCommon
|
|
{
|
|
public:
|
|
OutsideRenderPassCommandBufferHelper();
|
|
~OutsideRenderPassCommandBufferHelper();
|
|
|
|
angle::Result initialize(Context *context);
|
|
|
|
angle::Result reset(Context *context, SecondaryCommandBufferCollector *commandBufferCollector);
|
|
|
|
static constexpr bool ExecutesInline()
|
|
{
|
|
return OutsideRenderPassCommandBuffer::ExecutesInline();
|
|
}
|
|
|
|
OutsideRenderPassCommandBuffer &getCommandBuffer() { return mCommandBuffer; }
|
|
|
|
bool empty() const { return mCommandBuffer.empty(); }
|
|
|
|
angle::Result attachCommandPool(Context *context, SecondaryCommandPool *commandPool);
|
|
angle::Result detachCommandPool(Context *context, SecondaryCommandPool **commandPoolOut);
|
|
void releaseCommandPool();
|
|
|
|
void attachAllocator(SecondaryCommandMemoryAllocator *allocator);
|
|
SecondaryCommandMemoryAllocator *detachAllocator();
|
|
|
|
void assertCanBeRecycled();
|
|
|
|
#if defined(ANGLE_ENABLE_ASSERTS)
|
|
void markOpen() { mCommandBuffer.open(); }
|
|
void markClosed() { mCommandBuffer.close(); }
|
|
#endif
|
|
|
|
void bufferRead(ContextVk *contextVk,
|
|
VkAccessFlags readAccessType,
|
|
PipelineStage readStage,
|
|
BufferHelper *buffer);
|
|
|
|
void imageRead(ContextVk *contextVk,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout,
|
|
ImageHelper *image);
|
|
void imageWrite(ContextVk *contextVk,
|
|
gl::LevelIndex level,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout,
|
|
ImageHelper *image);
|
|
|
|
angle::Result flushToPrimary(Context *context, CommandsState *commandsState);
|
|
|
|
void setGLMemoryBarrierIssued()
|
|
{
|
|
if (!mCommandBuffer.empty())
|
|
{
|
|
mHasGLMemoryBarrierIssued = true;
|
|
}
|
|
}
|
|
|
|
void addCommandDiagnostics(ContextVk *contextVk);
|
|
|
|
void setQueueSerial(SerialIndex index, Serial serial)
|
|
{
|
|
mQueueSerial = QueueSerial(index, serial);
|
|
}
|
|
|
|
private:
|
|
angle::Result initializeCommandBuffer(Context *context);
|
|
angle::Result endCommandBuffer(Context *context);
|
|
|
|
OutsideRenderPassCommandBuffer mCommandBuffer;
|
|
bool mIsCommandBufferEnded = false;
|
|
|
|
friend class CommandBufferHelperCommon;
|
|
};
|
|
|
|
enum class ImagelessStatus
|
|
{
|
|
NotImageless,
|
|
Imageless,
|
|
};
|
|
|
|
class MaybeImagelessFramebuffer : angle::NonCopyable
|
|
{
|
|
public:
|
|
MaybeImagelessFramebuffer() : mImageViews({}), mImageless(ImagelessStatus::NotImageless) {}
|
|
~MaybeImagelessFramebuffer() { mFramebuffer.release(); }
|
|
|
|
MaybeImagelessFramebuffer &operator=(MaybeImagelessFramebuffer &&rhs)
|
|
{
|
|
updateFramebuffer(rhs.mFramebuffer.getHandle(), &rhs.mImageViews, rhs.mImageless);
|
|
return *this;
|
|
}
|
|
|
|
void updateFramebuffer(VkFramebuffer newFramebufferHandle,
|
|
FramebufferAttachmentsVector<VkImageView> *newImageViews,
|
|
ImagelessStatus imagelessStatus)
|
|
{
|
|
mFramebuffer.setHandle(newFramebufferHandle);
|
|
std::swap(mImageViews, *newImageViews);
|
|
mImageless = imagelessStatus;
|
|
}
|
|
|
|
Framebuffer &getFramebuffer() { return mFramebuffer; }
|
|
[[nodiscard]] VkFramebuffer getHandle() const { return mFramebuffer.getHandle(); }
|
|
void setHandle(VkFramebuffer handle) { mFramebuffer.setHandle(handle); }
|
|
|
|
FramebufferAttachmentsVector<VkImageView> &getImageViews() { return mImageViews; }
|
|
|
|
bool isImageless() { return mImageless == ImagelessStatus::Imageless; }
|
|
|
|
private:
|
|
Framebuffer mFramebuffer;
|
|
FramebufferAttachmentsVector<VkImageView> mImageViews;
|
|
ImagelessStatus mImageless;
|
|
};
|
|
|
|
class RenderPassCommandBufferHelper final : public CommandBufferHelperCommon
|
|
{
|
|
public:
|
|
RenderPassCommandBufferHelper();
|
|
~RenderPassCommandBufferHelper();
|
|
|
|
angle::Result initialize(Context *context);
|
|
|
|
angle::Result reset(Context *context, SecondaryCommandBufferCollector *commandBufferCollector);
|
|
|
|
static constexpr bool ExecutesInline() { return RenderPassCommandBuffer::ExecutesInline(); }
|
|
|
|
RenderPassCommandBuffer &getCommandBuffer()
|
|
{
|
|
return mCommandBuffers[mCurrentSubpassCommandBufferIndex];
|
|
}
|
|
|
|
bool empty() const { return !started(); }
|
|
|
|
angle::Result attachCommandPool(Context *context, SecondaryCommandPool *commandPool);
|
|
void detachCommandPool(SecondaryCommandPool **commandPoolOut);
|
|
void releaseCommandPool();
|
|
|
|
void attachAllocator(SecondaryCommandMemoryAllocator *allocator);
|
|
SecondaryCommandMemoryAllocator *detachAllocator();
|
|
|
|
void assertCanBeRecycled();
|
|
|
|
#if defined(ANGLE_ENABLE_ASSERTS)
|
|
void markOpen() { getCommandBuffer().open(); }
|
|
void markClosed() { getCommandBuffer().close(); }
|
|
#endif
|
|
|
|
void imageRead(ContextVk *contextVk,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout,
|
|
ImageHelper *image);
|
|
void imageWrite(ContextVk *contextVk,
|
|
gl::LevelIndex level,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout,
|
|
ImageHelper *image);
|
|
|
|
void bufferRead(ContextVk *contextVk,
|
|
VkAccessFlags readAccessType,
|
|
PipelineStage readStage,
|
|
BufferHelper *buffer);
|
|
|
|
void colorImagesDraw(gl::LevelIndex level,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
ImageHelper *image,
|
|
ImageHelper *resolveImage,
|
|
UniqueSerial imageSiblingSerial,
|
|
PackedAttachmentIndex packedAttachmentIndex);
|
|
void depthStencilImagesDraw(gl::LevelIndex level,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
ImageHelper *image,
|
|
ImageHelper *resolveImage,
|
|
UniqueSerial imageSiblingSerial);
|
|
|
|
bool usesImage(const ImageHelper &image) const;
|
|
bool startedAndUsesImageWithBarrier(const ImageHelper &image) const;
|
|
|
|
angle::Result flushToPrimary(Context *context,
|
|
CommandsState *commandsState,
|
|
const RenderPass *renderPass);
|
|
|
|
bool started() const { return mRenderPassStarted; }
|
|
|
|
// Finalize the layout if image has any deferred layout transition.
|
|
void finalizeImageLayout(Context *context,
|
|
const ImageHelper *image,
|
|
UniqueSerial imageSiblingSerial);
|
|
|
|
angle::Result beginRenderPass(ContextVk *contextVk,
|
|
MaybeImagelessFramebuffer &framebuffer,
|
|
const gl::Rectangle &renderArea,
|
|
const RenderPassDesc &renderPassDesc,
|
|
const AttachmentOpsArray &renderPassAttachmentOps,
|
|
const PackedAttachmentCount colorAttachmentCount,
|
|
const PackedAttachmentIndex depthStencilAttachmentIndex,
|
|
const PackedClearValuesArray &clearValues,
|
|
const QueueSerial &queueSerial,
|
|
RenderPassCommandBuffer **commandBufferOut);
|
|
|
|
angle::Result endRenderPass(ContextVk *contextVk);
|
|
|
|
angle::Result nextSubpass(ContextVk *contextVk, RenderPassCommandBuffer **commandBufferOut);
|
|
|
|
void updateStartedRenderPassWithDepthMode(bool readOnlyDepthMode);
|
|
void updateStartedRenderPassWithStencilMode(bool readOnlyStencilMode);
|
|
|
|
void beginTransformFeedback(size_t validBufferCount,
|
|
const VkBuffer *counterBuffers,
|
|
const VkDeviceSize *counterBufferOffsets,
|
|
bool rebindBuffers);
|
|
|
|
void endTransformFeedback();
|
|
|
|
void invalidateRenderPassColorAttachment(const gl::State &state,
|
|
size_t colorIndexGL,
|
|
PackedAttachmentIndex attachmentIndex,
|
|
const gl::Rectangle &invalidateArea);
|
|
void invalidateRenderPassDepthAttachment(const gl::DepthStencilState &dsState,
|
|
const gl::Rectangle &invalidateArea);
|
|
void invalidateRenderPassStencilAttachment(const gl::DepthStencilState &dsState,
|
|
const gl::Rectangle &invalidateArea);
|
|
|
|
void updateRenderPassColorClear(PackedAttachmentIndex colorIndexVk,
|
|
const VkClearValue &colorClearValue);
|
|
void updateRenderPassDepthStencilClear(VkImageAspectFlags aspectFlags,
|
|
const VkClearValue &clearValue);
|
|
|
|
const gl::Rectangle &getRenderArea() const { return mRenderArea; }
|
|
|
|
// If render pass is started with a small render area due to a small scissor, and if a new
|
|
// larger scissor is specified, grow the render area to accomodate it.
|
|
void growRenderArea(ContextVk *contextVk, const gl::Rectangle &newRenderArea);
|
|
|
|
void resumeTransformFeedback();
|
|
void pauseTransformFeedback();
|
|
bool isTransformFeedbackStarted() const { return mValidTransformFeedbackBufferCount > 0; }
|
|
bool isTransformFeedbackActiveUnpaused() const { return mIsTransformFeedbackActiveUnpaused; }
|
|
|
|
bool usesImagelessFramebuffer() { return mFramebuffer.isImageless(); }
|
|
|
|
uint32_t getAndResetCounter()
|
|
{
|
|
uint32_t count = mCounter;
|
|
mCounter = 0;
|
|
return count;
|
|
}
|
|
|
|
VkFramebuffer getFramebufferHandle() const { return mFramebuffer.getHandle(); }
|
|
|
|
void onColorAccess(PackedAttachmentIndex packedAttachmentIndex, ResourceAccess access);
|
|
void onDepthAccess(ResourceAccess access);
|
|
void onStencilAccess(ResourceAccess access);
|
|
|
|
bool hasAnyColorAccess(PackedAttachmentIndex packedAttachmentIndex)
|
|
{
|
|
ASSERT(packedAttachmentIndex < mColorAttachmentsCount);
|
|
return mColorAttachments[packedAttachmentIndex].hasAnyAccess();
|
|
}
|
|
bool hasAnyDepthAccess() { return mDepthAttachment.hasAnyAccess(); }
|
|
bool hasAnyStencilAccess() { return mStencilAttachment.hasAnyAccess(); }
|
|
|
|
void updateRenderPassForResolve(ContextVk *contextVk,
|
|
MaybeImagelessFramebuffer &newFramebuffer,
|
|
const RenderPassDesc &renderPassDesc);
|
|
|
|
bool hasDepthWriteOrClear() const
|
|
{
|
|
return mDepthAttachment.hasWriteAccess() ||
|
|
mAttachmentOps[mDepthStencilAttachmentIndex].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
}
|
|
|
|
bool hasStencilWriteOrClear() const
|
|
{
|
|
return mStencilAttachment.hasWriteAccess() ||
|
|
mAttachmentOps[mDepthStencilAttachmentIndex].stencilLoadOp ==
|
|
VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
}
|
|
|
|
const RenderPassDesc &getRenderPassDesc() const { return mRenderPassDesc; }
|
|
const AttachmentOpsArray &getAttachmentOps() const { return mAttachmentOps; }
|
|
|
|
void setImageOptimizeForPresent(ImageHelper *image) { mImageOptimizeForPresent = image; }
|
|
|
|
void setGLMemoryBarrierIssued()
|
|
{
|
|
if (mRenderPassStarted)
|
|
{
|
|
mHasGLMemoryBarrierIssued = true;
|
|
}
|
|
}
|
|
void addCommandDiagnostics(ContextVk *contextVk);
|
|
|
|
private:
|
|
uint32_t getSubpassCommandBufferCount() const { return mCurrentSubpassCommandBufferIndex + 1; }
|
|
|
|
angle::Result initializeCommandBuffer(Context *context);
|
|
angle::Result beginRenderPassCommandBuffer(ContextVk *contextVk);
|
|
angle::Result endRenderPassCommandBuffer(ContextVk *contextVk);
|
|
|
|
uint32_t getRenderPassWriteCommandCount()
|
|
{
|
|
// All subpasses are chained (no subpasses running in parallel), so the cmd count can be
|
|
// considered continuous among subpasses.
|
|
return mPreviousSubpassesCmdCount + getCommandBuffer().getRenderPassWriteCommandCount();
|
|
}
|
|
|
|
void updateStartedRenderPassWithDepthStencilMode(bool readOnlyDepthStencilMode,
|
|
RenderPassUsage readOnlyAttachmentUsage);
|
|
|
|
// We can't determine the image layout at the renderpass start time since their full usage
|
|
// aren't known until later time. We finalize the layout when either ImageHelper object is
|
|
// released or when renderpass ends.
|
|
void finalizeColorImageLayout(Context *context,
|
|
ImageHelper *image,
|
|
PackedAttachmentIndex packedAttachmentIndex,
|
|
bool isResolveImage);
|
|
void finalizeColorImageLoadStore(Context *context, PackedAttachmentIndex packedAttachmentIndex);
|
|
void finalizeDepthStencilImageLayout(Context *context);
|
|
void finalizeDepthStencilResolveImageLayout(Context *context);
|
|
void finalizeDepthStencilLoadStore(Context *context);
|
|
|
|
void finalizeColorImageLayoutAndLoadStore(Context *context,
|
|
PackedAttachmentIndex packedAttachmentIndex);
|
|
void finalizeDepthStencilImageLayoutAndLoadStore(Context *context);
|
|
|
|
// When using Vulkan secondary command buffers, each subpass must be recorded in a separate
|
|
// command buffer. Currently ANGLE produces render passes with at most 2 subpasses. Once
|
|
// framebuffer-fetch is appropriately implemented to use subpasses, this array must be made
|
|
// dynamic.
|
|
static constexpr size_t kMaxSubpassCount = 2;
|
|
std::array<RenderPassCommandBuffer, kMaxSubpassCount> mCommandBuffers;
|
|
uint32_t mCurrentSubpassCommandBufferIndex;
|
|
|
|
// RenderPass state
|
|
uint32_t mCounter;
|
|
RenderPassDesc mRenderPassDesc;
|
|
AttachmentOpsArray mAttachmentOps;
|
|
MaybeImagelessFramebuffer mFramebuffer;
|
|
gl::Rectangle mRenderArea;
|
|
PackedClearValuesArray mClearValues;
|
|
bool mRenderPassStarted;
|
|
|
|
// Transform feedback state
|
|
gl::TransformFeedbackBuffersArray<VkBuffer> mTransformFeedbackCounterBuffers;
|
|
gl::TransformFeedbackBuffersArray<VkDeviceSize> mTransformFeedbackCounterBufferOffsets;
|
|
uint32_t mValidTransformFeedbackBufferCount;
|
|
bool mRebindTransformFeedbackBuffers;
|
|
bool mIsTransformFeedbackActiveUnpaused;
|
|
|
|
// State tracking for whether to optimize the storeOp to DONT_CARE
|
|
uint32_t mPreviousSubpassesCmdCount;
|
|
|
|
// Keep track of the depth/stencil attachment index
|
|
PackedAttachmentIndex mDepthStencilAttachmentIndex;
|
|
|
|
// Array size of mColorAttachments
|
|
PackedAttachmentCount mColorAttachmentsCount;
|
|
// Attached render target images. Color and depth resolve images always come last.
|
|
PackedRenderPassAttachmentArray mColorAttachments;
|
|
PackedRenderPassAttachmentArray mColorResolveAttachments;
|
|
|
|
RenderPassAttachment mDepthAttachment;
|
|
RenderPassAttachment mDepthResolveAttachment;
|
|
|
|
RenderPassAttachment mStencilAttachment;
|
|
RenderPassAttachment mStencilResolveAttachment;
|
|
|
|
FramebufferAttachmentArray<VkImageView> mImageViews;
|
|
|
|
// This is last renderpass before present and this is the image will be presented. We can use
|
|
// final layout of the renderpass to transition it to the presentable layout
|
|
ImageHelper *mImageOptimizeForPresent;
|
|
|
|
friend class CommandBufferHelperCommon;
|
|
};
|
|
|
|
// The following class helps support both Vulkan and ANGLE secondary command buffers by
|
|
// encapsulating their differences.
|
|
template <typename CommandBufferHelperT>
|
|
class CommandBufferRecycler
|
|
{
|
|
public:
|
|
CommandBufferRecycler() = default;
|
|
~CommandBufferRecycler() = default;
|
|
|
|
void onDestroy();
|
|
|
|
angle::Result getCommandBufferHelper(Context *context,
|
|
SecondaryCommandPool *commandPool,
|
|
SecondaryCommandMemoryAllocator *commandsAllocator,
|
|
CommandBufferHelperT **commandBufferHelperOut);
|
|
|
|
void recycleCommandBufferHelper(CommandBufferHelperT **commandBuffer);
|
|
|
|
private:
|
|
std::mutex mMutex;
|
|
std::vector<CommandBufferHelperT *> mCommandBufferHelperFreeList;
|
|
};
|
|
|
|
// Imagine an image going through a few layout transitions:
|
|
//
|
|
// srcStage 1 dstStage 2 srcStage 2 dstStage 3
|
|
// Layout 1 ------Transition 1-----> Layout 2 ------Transition 2------> Layout 3
|
|
// srcAccess 1 dstAccess 2 srcAccess 2 dstAccess 3
|
|
// \_________________ ___________________/
|
|
// \/
|
|
// A transition
|
|
//
|
|
// Every transition requires 6 pieces of information: from/to layouts, src/dst stage masks and
|
|
// src/dst access masks. At the moment we decide to transition the image to Layout 2 (i.e.
|
|
// Transition 1), we need to have Layout 1, srcStage 1 and srcAccess 1 stored as history of the
|
|
// image. To perform the transition, we need to know Layout 2, dstStage 2 and dstAccess 2.
|
|
// Additionally, we need to know srcStage 2 and srcAccess 2 to retain them for the next transition.
|
|
//
|
|
// That is, with the history kept, on every new transition we need 5 pieces of new information:
|
|
// layout/dstStage/dstAccess to transition into the layout, and srcStage/srcAccess for the future
|
|
// transition out from it. Given the small number of possible combinations of these values, an
|
|
// enum is used were each value encapsulates these 5 pieces of information:
|
|
//
|
|
// +--------------------------------+
|
|
// srcStage 1 | dstStage 2 srcStage 2 | dstStage 3
|
|
// Layout 1 ------Transition 1-----> Layout 2 ------Transition 2------> Layout 3
|
|
// srcAccess 1 |dstAccess 2 srcAccess 2| dstAccess 3
|
|
// +--------------- ---------------+
|
|
// \/
|
|
// One enum value
|
|
//
|
|
// Note that, while generally dstStage for the to-transition and srcStage for the from-transition
|
|
// are the same, they may occasionally be BOTTOM_OF_PIPE and TOP_OF_PIPE respectively.
|
|
enum class ImageLayout
|
|
{
|
|
Undefined = 0,
|
|
// Framebuffer attachment layouts are placed first, so they can fit in fewer bits in
|
|
// PackedAttachmentOpsDesc.
|
|
|
|
// Color (Write):
|
|
ColorWrite,
|
|
|
|
// Depth (Write), Stencil (Write)
|
|
DepthWriteStencilWrite,
|
|
|
|
// Depth (Write), Stencil (Read)
|
|
DepthWriteStencilRead,
|
|
DepthWriteStencilReadFragmentShaderStencilRead,
|
|
DepthWriteStencilReadAllShadersStencilRead,
|
|
|
|
// Depth (Read), Stencil (Write)
|
|
DepthReadStencilWrite,
|
|
DepthReadStencilWriteFragmentShaderDepthRead,
|
|
DepthReadStencilWriteAllShadersDepthRead,
|
|
|
|
// Depth (Read), Stencil (Read)
|
|
DepthReadStencilRead,
|
|
DepthReadStencilReadFragmentShaderRead,
|
|
DepthReadStencilReadAllShadersRead,
|
|
|
|
// The GENERAL layout is used when there's a feedback loop. For depth/stencil it does't matter
|
|
// which aspect is participating in feedback and whether the other aspect is read-only.
|
|
ColorWriteFragmentShaderFeedback,
|
|
ColorWriteAllShadersFeedback,
|
|
DepthStencilFragmentShaderFeedback,
|
|
DepthStencilAllShadersFeedback,
|
|
|
|
// Depth/stencil resolve is special because it uses the _color_ output stage and mask
|
|
DepthStencilResolve,
|
|
|
|
Present,
|
|
SharedPresent,
|
|
// The rest of the layouts.
|
|
ExternalPreInitialized,
|
|
ExternalShadersReadOnly,
|
|
ExternalShadersWrite,
|
|
TransferSrc,
|
|
TransferDst,
|
|
VertexShaderReadOnly,
|
|
VertexShaderWrite,
|
|
// PreFragment == Vertex, Tessellation and Geometry stages
|
|
PreFragmentShadersReadOnly,
|
|
PreFragmentShadersWrite,
|
|
FragmentShaderReadOnly,
|
|
FragmentShaderWrite,
|
|
ComputeShaderReadOnly,
|
|
ComputeShaderWrite,
|
|
AllGraphicsShadersReadOnly,
|
|
AllGraphicsShadersWrite,
|
|
|
|
InvalidEnum,
|
|
EnumCount = InvalidEnum,
|
|
};
|
|
|
|
VkImageCreateFlags GetImageCreateFlags(gl::TextureType textureType);
|
|
|
|
ImageLayout GetImageLayoutFromGLImageLayout(Context *context, GLenum layout);
|
|
|
|
GLenum ConvertImageLayoutToGLImageLayout(ImageLayout imageLayout);
|
|
|
|
VkImageLayout ConvertImageLayoutToVkImageLayout(Context *context, ImageLayout imageLayout);
|
|
|
|
// The source of update to an ImageHelper
|
|
enum class UpdateSource
|
|
{
|
|
// Clear an image subresource.
|
|
Clear,
|
|
// Clear only the emulated channels of the subresource. This operation is more expensive than
|
|
// Clear, and so is only used for emulated color formats and only for external images. Color
|
|
// only because depth or stencil clear is already per channel, so Clear works for them.
|
|
// External only because they may contain data that needs to be preserved. Additionally, this
|
|
// is a one-time only clear. Once the emulated channels are cleared, ANGLE ensures that they
|
|
// remain untouched.
|
|
ClearEmulatedChannelsOnly,
|
|
// When an image with emulated channels is invalidated, a clear may be restaged to keep the
|
|
// contents of the emulated channels defined. This is given a dedicated enum value, so it can
|
|
// be removed if the invalidate is undone at the end of the render pass.
|
|
ClearAfterInvalidate,
|
|
// The source of the copy is a buffer.
|
|
Buffer,
|
|
// The source of the copy is an image.
|
|
Image,
|
|
};
|
|
|
|
constexpr VkImageAspectFlagBits IMAGE_ASPECT_DEPTH_STENCIL =
|
|
static_cast<VkImageAspectFlagBits>(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
|
|
|
|
bool FormatHasNecessaryFeature(RendererVk *renderer,
|
|
angle::FormatID formatID,
|
|
VkImageTiling tilingMode,
|
|
VkFormatFeatureFlags featureBits);
|
|
|
|
bool CanCopyWithTransfer(RendererVk *renderer,
|
|
angle::FormatID srcFormatID,
|
|
VkImageTiling srcTilingMode,
|
|
angle::FormatID dstFormatID,
|
|
VkImageTiling dstTilingMode);
|
|
|
|
class ImageViewHelper;
|
|
class ImageHelper final : public Resource, public angle::Subject
|
|
{
|
|
public:
|
|
ImageHelper();
|
|
~ImageHelper() override;
|
|
|
|
angle::Result init(Context *context,
|
|
gl::TextureType textureType,
|
|
const VkExtent3D &extents,
|
|
const Format &format,
|
|
GLint samples,
|
|
VkImageUsageFlags usage,
|
|
gl::LevelIndex firstLevel,
|
|
uint32_t mipLevels,
|
|
uint32_t layerCount,
|
|
bool isRobustResourceInitEnabled,
|
|
bool hasProtectedContent);
|
|
angle::Result initMSAASwapchain(Context *context,
|
|
gl::TextureType textureType,
|
|
const VkExtent3D &extents,
|
|
bool rotatedAspectRatio,
|
|
const Format &format,
|
|
GLint samples,
|
|
VkImageUsageFlags usage,
|
|
gl::LevelIndex firstLevel,
|
|
uint32_t mipLevels,
|
|
uint32_t layerCount,
|
|
bool isRobustResourceInitEnabled,
|
|
bool hasProtectedContent);
|
|
angle::Result initExternal(Context *context,
|
|
gl::TextureType textureType,
|
|
const VkExtent3D &extents,
|
|
angle::FormatID intendedFormatID,
|
|
angle::FormatID actualFormatID,
|
|
GLint samples,
|
|
VkImageUsageFlags usage,
|
|
VkImageCreateFlags additionalCreateFlags,
|
|
ImageLayout initialLayout,
|
|
const void *externalImageCreateInfo,
|
|
gl::LevelIndex firstLevel,
|
|
uint32_t mipLevels,
|
|
uint32_t layerCount,
|
|
bool isRobustResourceInitEnabled,
|
|
bool hasProtectedContent);
|
|
angle::Result initMemory(Context *context,
|
|
bool hasProtectedContent,
|
|
const MemoryProperties &memoryProperties,
|
|
VkMemoryPropertyFlags flags,
|
|
vk::MemoryAllocationType);
|
|
angle::Result initExternalMemory(Context *context,
|
|
const MemoryProperties &memoryProperties,
|
|
const VkMemoryRequirements &memoryRequirements,
|
|
uint32_t extraAllocationInfoCount,
|
|
const void **extraAllocationInfo,
|
|
uint32_t currentQueueFamilyIndex,
|
|
VkMemoryPropertyFlags flags);
|
|
|
|
static constexpr VkImageUsageFlags kDefaultImageViewUsageFlags = 0;
|
|
angle::Result initLayerImageView(Context *context,
|
|
gl::TextureType textureType,
|
|
VkImageAspectFlags aspectMask,
|
|
const gl::SwizzleState &swizzleMap,
|
|
ImageView *imageViewOut,
|
|
LevelIndex baseMipLevelVk,
|
|
uint32_t levelCount,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount,
|
|
gl::SrgbWriteControlMode srgbWriteControlMode,
|
|
gl::YuvSamplingMode yuvSamplingMode,
|
|
VkImageUsageFlags imageUsageFlags) const;
|
|
angle::Result initReinterpretedLayerImageView(Context *context,
|
|
gl::TextureType textureType,
|
|
VkImageAspectFlags aspectMask,
|
|
const gl::SwizzleState &swizzleMap,
|
|
ImageView *imageViewOut,
|
|
LevelIndex baseMipLevelVk,
|
|
uint32_t levelCount,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount,
|
|
VkImageUsageFlags imageUsageFlags,
|
|
angle::FormatID imageViewFormat) const;
|
|
angle::Result initImageView(Context *context,
|
|
gl::TextureType textureType,
|
|
VkImageAspectFlags aspectMask,
|
|
const gl::SwizzleState &swizzleMap,
|
|
ImageView *imageViewOut,
|
|
LevelIndex baseMipLevelVk,
|
|
uint32_t levelCount,
|
|
VkImageUsageFlags imageUsageFlags);
|
|
// Create a 2D[Array] for staging purposes. Used by:
|
|
//
|
|
// - TextureVk::copySubImageImplWithDraw
|
|
// - FramebufferVk::readPixelsImpl
|
|
//
|
|
angle::Result init2DStaging(Context *context,
|
|
bool hasProtectedContent,
|
|
const MemoryProperties &memoryProperties,
|
|
const gl::Extents &glExtents,
|
|
angle::FormatID intendedFormatID,
|
|
angle::FormatID actualFormatID,
|
|
VkImageUsageFlags usage,
|
|
uint32_t layerCount);
|
|
// Create an image for staging purposes. Used by:
|
|
//
|
|
// - TextureVk::copyAndStageImageData
|
|
//
|
|
angle::Result initStaging(Context *context,
|
|
bool hasProtectedContent,
|
|
const MemoryProperties &memoryProperties,
|
|
VkImageType imageType,
|
|
const VkExtent3D &extents,
|
|
angle::FormatID intendedFormatID,
|
|
angle::FormatID actualFormatID,
|
|
GLint samples,
|
|
VkImageUsageFlags usage,
|
|
uint32_t mipLevels,
|
|
uint32_t layerCount);
|
|
// Create a multisampled image for use as the implicit image in multisampled render to texture
|
|
// rendering. If LAZILY_ALLOCATED memory is available, it will prefer that.
|
|
angle::Result initImplicitMultisampledRenderToTexture(Context *context,
|
|
bool hasProtectedContent,
|
|
const MemoryProperties &memoryProperties,
|
|
gl::TextureType textureType,
|
|
GLint samples,
|
|
const ImageHelper &resolveImage,
|
|
bool isRobustResourceInitEnabled);
|
|
|
|
// Helper for initExternal and users to automatically derive the appropriate VkImageCreateInfo
|
|
// pNext chain based on the given parameters, and adjust create flags. In some cases, these
|
|
// shouldn't be automatically derived, for example when importing images through
|
|
// EXT_external_objects and ANGLE_external_objects_flags.
|
|
static constexpr uint32_t kImageListFormatCount = 2;
|
|
using ImageListFormats = std::array<VkFormat, kImageListFormatCount>;
|
|
static const void *DeriveCreateInfoPNext(
|
|
Context *context,
|
|
angle::FormatID actualFormatID,
|
|
const void *pNext,
|
|
VkImageFormatListCreateInfoKHR *imageFormatListInfoStorage,
|
|
ImageListFormats *imageListFormatsStorage,
|
|
VkImageCreateFlags *createFlagsOut);
|
|
|
|
// Image formats used for the creation of imageless framebuffers.
|
|
using ImageFormats = angle::FixedVector<VkFormat, kImageListFormatCount>;
|
|
ImageFormats &getViewFormats() { return mViewFormats; }
|
|
|
|
// Helper for initExternal and users to extract the view formats of the image from the pNext
|
|
// chain in VkImageCreateInfo.
|
|
void deriveImageViewFormatFromCreateInfoPNext(VkImageCreateInfo &imageInfo,
|
|
ImageFormats &formatOut);
|
|
|
|
// Release the underlining VkImage object for garbage collection.
|
|
void releaseImage(RendererVk *renderer);
|
|
// Similar to releaseImage, but also notify all contexts in the same share group to stop
|
|
// accessing to it.
|
|
void releaseImageFromShareContexts(RendererVk *renderer,
|
|
ContextVk *contextVk,
|
|
UniqueSerial imageSiblingSerial);
|
|
void finalizeImageLayoutInShareContexts(RendererVk *renderer,
|
|
ContextVk *contextVk,
|
|
UniqueSerial imageSiblingSerial);
|
|
void releaseStagedUpdates(RendererVk *renderer);
|
|
|
|
bool valid() const { return mImage.valid(); }
|
|
|
|
VkImageAspectFlags getAspectFlags() const;
|
|
// True if image contains both depth & stencil aspects
|
|
bool isCombinedDepthStencilFormat() const;
|
|
void destroy(RendererVk *renderer);
|
|
void release(RendererVk *renderer) { destroy(renderer); }
|
|
|
|
void init2DWeakReference(Context *context,
|
|
VkImage handle,
|
|
const gl::Extents &glExtents,
|
|
bool rotatedAspectRatio,
|
|
angle::FormatID intendedFormatID,
|
|
angle::FormatID actualFormatID,
|
|
VkImageUsageFlags usage,
|
|
GLint samples,
|
|
bool isRobustResourceInitEnabled);
|
|
void resetImageWeakReference();
|
|
|
|
const Image &getImage() const { return mImage; }
|
|
const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
|
|
|
|
const VkImageCreateInfo &getVkImageCreateInfo() const { return mVkImageCreateInfo; }
|
|
void setTilingMode(VkImageTiling tilingMode) { mTilingMode = tilingMode; }
|
|
VkImageTiling getTilingMode() const { return mTilingMode; }
|
|
VkImageCreateFlags getCreateFlags() const { return mCreateFlags; }
|
|
VkImageUsageFlags getUsage() const { return mUsage; }
|
|
VkImageType getType() const { return mImageType; }
|
|
const VkExtent3D &getExtents() const { return mExtents; }
|
|
const VkExtent3D getRotatedExtents() const;
|
|
uint32_t getLayerCount() const
|
|
{
|
|
ASSERT(valid());
|
|
return mLayerCount;
|
|
}
|
|
uint32_t getLevelCount() const
|
|
{
|
|
ASSERT(valid());
|
|
return mLevelCount;
|
|
}
|
|
angle::FormatID getIntendedFormatID() const
|
|
{
|
|
ASSERT(valid());
|
|
return mIntendedFormatID;
|
|
}
|
|
const angle::Format &getIntendedFormat() const
|
|
{
|
|
ASSERT(valid());
|
|
return angle::Format::Get(mIntendedFormatID);
|
|
}
|
|
angle::FormatID getActualFormatID() const
|
|
{
|
|
ASSERT(valid());
|
|
return mActualFormatID;
|
|
}
|
|
VkFormat getActualVkFormat() const
|
|
{
|
|
ASSERT(valid());
|
|
return GetVkFormatFromFormatID(mActualFormatID);
|
|
}
|
|
const angle::Format &getActualFormat() const
|
|
{
|
|
ASSERT(valid());
|
|
return angle::Format::Get(mActualFormatID);
|
|
}
|
|
bool hasEmulatedImageChannels() const;
|
|
bool hasEmulatedDepthChannel() const;
|
|
bool hasEmulatedStencilChannel() const;
|
|
bool hasEmulatedImageFormat() const { return mActualFormatID != mIntendedFormatID; }
|
|
GLint getSamples() const { return mSamples; }
|
|
|
|
ImageSerial getImageSerial() const
|
|
{
|
|
ASSERT(valid() && mImageSerial.valid());
|
|
return mImageSerial;
|
|
}
|
|
|
|
void setCurrentImageLayout(ImageLayout newLayout)
|
|
{
|
|
// Once you transition to ImageLayout::SharedPresent, you never transition out of it.
|
|
if (mCurrentLayout == ImageLayout::SharedPresent)
|
|
{
|
|
return;
|
|
}
|
|
mCurrentLayout = newLayout;
|
|
}
|
|
ImageLayout getCurrentImageLayout() const { return mCurrentLayout; }
|
|
VkImageLayout getCurrentLayout(Context *context) const;
|
|
const QueueSerial &getBarrierQueueSerial() const { return mBarrierQueueSerial; }
|
|
|
|
gl::Extents getLevelExtents(LevelIndex levelVk) const;
|
|
// Helper function to calculate the extents of a render target created for a certain mip of the
|
|
// image.
|
|
gl::Extents getLevelExtents2D(LevelIndex levelVk) const;
|
|
gl::Extents getRotatedLevelExtents2D(LevelIndex levelVk) const;
|
|
|
|
bool isDepthOrStencil() const;
|
|
|
|
void setRenderPassUsageFlag(RenderPassUsage flag);
|
|
void clearRenderPassUsageFlag(RenderPassUsage flag);
|
|
void resetRenderPassUsageFlags();
|
|
bool hasRenderPassUsageFlag(RenderPassUsage flag) const;
|
|
bool usedByCurrentRenderPassAsAttachmentAndSampler(RenderPassUsage textureSamplerUsage) const;
|
|
|
|
static void Copy(Context *context,
|
|
ImageHelper *srcImage,
|
|
ImageHelper *dstImage,
|
|
const gl::Offset &srcOffset,
|
|
const gl::Offset &dstOffset,
|
|
const gl::Extents ©Size,
|
|
const VkImageSubresourceLayers &srcSubresources,
|
|
const VkImageSubresourceLayers &dstSubresources,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
static angle::Result CopyImageSubData(const gl::Context *context,
|
|
ImageHelper *srcImage,
|
|
GLint srcLevel,
|
|
GLint srcX,
|
|
GLint srcY,
|
|
GLint srcZ,
|
|
ImageHelper *dstImage,
|
|
GLint dstLevel,
|
|
GLint dstX,
|
|
GLint dstY,
|
|
GLint dstZ,
|
|
GLsizei srcWidth,
|
|
GLsizei srcHeight,
|
|
GLsizei srcDepth);
|
|
|
|
// Generate mipmap from level 0 into the rest of the levels with blit.
|
|
angle::Result generateMipmapsWithBlit(ContextVk *contextVk,
|
|
LevelIndex baseLevel,
|
|
LevelIndex maxLevel);
|
|
|
|
// Resolve this image into a destination image. This image should be in the TransferSrc layout.
|
|
// The destination image is automatically transitioned into TransferDst.
|
|
void resolve(ImageHelper *dst,
|
|
const VkImageResolve ®ion,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
// Data staging
|
|
void removeSingleSubresourceStagedUpdates(ContextVk *contextVk,
|
|
gl::LevelIndex levelIndexGL,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount);
|
|
void removeSingleStagedClearAfterInvalidate(gl::LevelIndex levelIndexGL,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount);
|
|
void removeStagedUpdates(Context *context,
|
|
gl::LevelIndex levelGLStart,
|
|
gl::LevelIndex levelGLEnd);
|
|
|
|
angle::Result stageSubresourceUpdateImpl(ContextVk *contextVk,
|
|
const gl::ImageIndex &index,
|
|
const gl::Extents &glExtents,
|
|
const gl::Offset &offset,
|
|
const gl::InternalFormat &formatInfo,
|
|
const gl::PixelUnpackState &unpack,
|
|
GLenum type,
|
|
const uint8_t *pixels,
|
|
const Format &vkFormat,
|
|
ImageAccess access,
|
|
const GLuint inputRowPitch,
|
|
const GLuint inputDepthPitch,
|
|
const GLuint inputSkipBytes);
|
|
|
|
angle::Result stageSubresourceUpdate(ContextVk *contextVk,
|
|
const gl::ImageIndex &index,
|
|
const gl::Extents &glExtents,
|
|
const gl::Offset &offset,
|
|
const gl::InternalFormat &formatInfo,
|
|
const gl::PixelUnpackState &unpack,
|
|
GLenum type,
|
|
const uint8_t *pixels,
|
|
const Format &vkFormat,
|
|
ImageAccess access);
|
|
|
|
angle::Result stageSubresourceUpdateAndGetData(ContextVk *contextVk,
|
|
size_t allocationSize,
|
|
const gl::ImageIndex &imageIndex,
|
|
const gl::Extents &glExtents,
|
|
const gl::Offset &offset,
|
|
uint8_t **destData,
|
|
angle::FormatID formatID);
|
|
|
|
angle::Result stageSubresourceUpdateFromFramebuffer(const gl::Context *context,
|
|
const gl::ImageIndex &index,
|
|
const gl::Rectangle &sourceArea,
|
|
const gl::Offset &dstOffset,
|
|
const gl::Extents &dstExtent,
|
|
const gl::InternalFormat &formatInfo,
|
|
ImageAccess access,
|
|
FramebufferVk *framebufferVk);
|
|
|
|
void stageSubresourceUpdateFromImage(RefCounted<ImageHelper> *image,
|
|
const gl::ImageIndex &index,
|
|
LevelIndex srcMipLevel,
|
|
const gl::Offset &destOffset,
|
|
const gl::Extents &glExtents,
|
|
const VkImageType imageType);
|
|
|
|
// Takes an image and stages a subresource update for each level of it, including its full
|
|
// extent and all its layers, at the specified GL level.
|
|
void stageSubresourceUpdatesFromAllImageLevels(RefCounted<ImageHelper> *image,
|
|
gl::LevelIndex baseLevel);
|
|
|
|
// Stage a clear to an arbitrary value.
|
|
void stageClear(const gl::ImageIndex &index,
|
|
VkImageAspectFlags aspectFlags,
|
|
const VkClearValue &clearValue);
|
|
|
|
// Stage a clear based on robust resource init.
|
|
angle::Result stageRobustResourceClearWithFormat(ContextVk *contextVk,
|
|
const gl::ImageIndex &index,
|
|
const gl::Extents &glExtents,
|
|
const angle::Format &intendedFormat,
|
|
const angle::Format &imageFormat);
|
|
void stageRobustResourceClear(const gl::ImageIndex &index);
|
|
|
|
angle::Result stageResourceClearWithFormat(ContextVk *contextVk,
|
|
const gl::ImageIndex &index,
|
|
const gl::Extents &glExtents,
|
|
const angle::Format &intendedFormat,
|
|
const angle::Format &imageFormat,
|
|
const VkClearValue &clearValue);
|
|
|
|
// Stage the currently allocated image as updates to base level and on, making this !valid().
|
|
// This is used for:
|
|
//
|
|
// - Mipmap generation, where levelCount is 1 so only the base level is retained
|
|
// - Image respecification, where every level (other than those explicitly skipped) is staged
|
|
void stageSelfAsSubresourceUpdates(ContextVk *contextVk,
|
|
uint32_t levelCount,
|
|
gl::TexLevelMask skipLevelsMask);
|
|
|
|
// Flush staged updates for a single subresource. Can optionally take a parameter to defer
|
|
// clears to a subsequent RenderPass load op.
|
|
angle::Result flushSingleSubresourceStagedUpdates(ContextVk *contextVk,
|
|
gl::LevelIndex levelGL,
|
|
uint32_t layer,
|
|
uint32_t layerCount,
|
|
ClearValuesArray *deferredClears,
|
|
uint32_t deferredClearIndex);
|
|
|
|
// Flushes staged updates to a range of levels and layers from start to (but not including) end.
|
|
// Due to the nature of updates (done wholly to a VkImageSubresourceLayers), some unsolicited
|
|
// layers may also be updated.
|
|
angle::Result flushStagedUpdates(ContextVk *contextVk,
|
|
gl::LevelIndex levelGLStart,
|
|
gl::LevelIndex levelGLEnd,
|
|
uint32_t layerStart,
|
|
uint32_t layerEnd,
|
|
gl::TexLevelMask skipLevelsMask);
|
|
|
|
// Creates a command buffer and flushes all staged updates. This is used for one-time
|
|
// initialization of resources that we don't expect to accumulate further staged updates, such
|
|
// as with renderbuffers or surface images.
|
|
angle::Result flushAllStagedUpdates(ContextVk *contextVk);
|
|
|
|
bool hasStagedUpdatesForSubresource(gl::LevelIndex levelGL,
|
|
uint32_t layer,
|
|
uint32_t layerCount) const;
|
|
bool hasStagedUpdatesInAllocatedLevels() const;
|
|
|
|
bool removeStagedClearUpdatesAndReturnColor(gl::LevelIndex levelGL,
|
|
const VkClearColorValue **color);
|
|
|
|
void recordWriteBarrier(Context *context,
|
|
VkImageAspectFlags aspectMask,
|
|
ImageLayout newLayout,
|
|
OutsideRenderPassCommandBufferHelper *commands);
|
|
|
|
void recordWriteBarrierOneOff(Context *context,
|
|
ImageLayout newLayout,
|
|
PrimaryCommandBuffer *commandBuffer,
|
|
VkSemaphore *acquireNextImageSemaphoreOut)
|
|
{
|
|
barrierImpl(context, getAspectFlags(), newLayout, mCurrentQueueFamilyIndex, commandBuffer,
|
|
acquireNextImageSemaphoreOut);
|
|
}
|
|
|
|
// This function can be used to prevent issuing redundant layout transition commands.
|
|
bool isReadBarrierNecessary(ImageLayout newLayout) const;
|
|
|
|
void recordReadBarrier(Context *context,
|
|
VkImageAspectFlags aspectMask,
|
|
ImageLayout newLayout,
|
|
OutsideRenderPassCommandBufferHelper *commands);
|
|
|
|
bool isQueueChangeNeccesary(uint32_t newQueueFamilyIndex) const
|
|
{
|
|
return mCurrentQueueFamilyIndex != newQueueFamilyIndex;
|
|
}
|
|
|
|
void changeLayoutAndQueue(Context *context,
|
|
VkImageAspectFlags aspectMask,
|
|
ImageLayout newLayout,
|
|
uint32_t newQueueFamilyIndex,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
// Returns true if barrier has been generated
|
|
bool updateLayoutAndBarrier(Context *context,
|
|
VkImageAspectFlags aspectMask,
|
|
ImageLayout newLayout,
|
|
const QueueSerial &queueSerial,
|
|
PipelineBarrier *barrier,
|
|
VkSemaphore *semaphoreOut);
|
|
|
|
// Performs an ownership transfer from an external instance or API.
|
|
void acquireFromExternal(ContextVk *contextVk,
|
|
uint32_t externalQueueFamilyIndex,
|
|
uint32_t rendererQueueFamilyIndex,
|
|
ImageLayout currentLayout,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
// Performs an ownership transfer to an external instance or API.
|
|
void releaseToExternal(ContextVk *contextVk,
|
|
uint32_t rendererQueueFamilyIndex,
|
|
uint32_t externalQueueFamilyIndex,
|
|
ImageLayout desiredLayout,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
// Returns true if the image is owned by an external API or instance.
|
|
bool isReleasedToExternal() const;
|
|
|
|
gl::LevelIndex getFirstAllocatedLevel() const
|
|
{
|
|
ASSERT(valid());
|
|
return mFirstAllocatedLevel;
|
|
}
|
|
gl::LevelIndex getLastAllocatedLevel() const;
|
|
LevelIndex toVkLevel(gl::LevelIndex levelIndexGL) const;
|
|
gl::LevelIndex toGLLevel(LevelIndex levelIndexVk) const;
|
|
|
|
angle::Result copyImageDataToBuffer(ContextVk *contextVk,
|
|
gl::LevelIndex sourceLevelGL,
|
|
uint32_t layerCount,
|
|
uint32_t baseLayer,
|
|
const gl::Box &sourceArea,
|
|
BufferHelper *dstBuffer,
|
|
uint8_t **outDataPtr);
|
|
|
|
angle::Result copySurfaceImageToBuffer(DisplayVk *displayVk,
|
|
gl::LevelIndex sourceLevelGL,
|
|
uint32_t layerCount,
|
|
uint32_t baseLayer,
|
|
const gl::Box &sourceArea,
|
|
vk::BufferHelper *bufferHelperOut);
|
|
|
|
angle::Result copyBufferToSurfaceImage(DisplayVk *displayVk,
|
|
gl::LevelIndex destLevelGL,
|
|
uint32_t layerCount,
|
|
uint32_t baseLayer,
|
|
const gl::Box &destArea,
|
|
vk::BufferHelper *bufferHelper);
|
|
|
|
static angle::Result GetReadPixelsParams(ContextVk *contextVk,
|
|
const gl::PixelPackState &packState,
|
|
gl::Buffer *packBuffer,
|
|
GLenum format,
|
|
GLenum type,
|
|
const gl::Rectangle &area,
|
|
const gl::Rectangle &clippedArea,
|
|
PackPixelsParams *paramsOut,
|
|
GLuint *skipBytesOut);
|
|
|
|
angle::Result readPixelsForGetImage(ContextVk *contextVk,
|
|
const gl::PixelPackState &packState,
|
|
gl::Buffer *packBuffer,
|
|
gl::LevelIndex levelGL,
|
|
uint32_t layer,
|
|
uint32_t layerCount,
|
|
GLenum format,
|
|
GLenum type,
|
|
void *pixels);
|
|
|
|
angle::Result readPixelsForCompressedGetImage(ContextVk *contextVk,
|
|
const gl::PixelPackState &packState,
|
|
gl::Buffer *packBuffer,
|
|
gl::LevelIndex levelGL,
|
|
uint32_t layer,
|
|
uint32_t layerCount,
|
|
void *pixels);
|
|
|
|
angle::Result readPixels(ContextVk *contextVk,
|
|
const gl::Rectangle &area,
|
|
const PackPixelsParams &packPixelsParams,
|
|
VkImageAspectFlagBits copyAspectFlags,
|
|
gl::LevelIndex levelGL,
|
|
uint32_t layer,
|
|
void *pixels);
|
|
|
|
angle::Result CalculateBufferInfo(ContextVk *contextVk,
|
|
const gl::Extents &glExtents,
|
|
const gl::InternalFormat &formatInfo,
|
|
const gl::PixelUnpackState &unpack,
|
|
GLenum type,
|
|
bool is3D,
|
|
GLuint *inputRowPitch,
|
|
GLuint *inputDepthPitch,
|
|
GLuint *inputSkipBytes);
|
|
|
|
// Mark a given subresource as written to. The subresource is identified by [levelStart,
|
|
// levelStart + levelCount) and [layerStart, layerStart + layerCount).
|
|
void onWrite(gl::LevelIndex levelStart,
|
|
uint32_t levelCount,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags);
|
|
bool hasImmutableSampler() const { return mYcbcrConversionDesc.valid(); }
|
|
uint64_t getExternalFormat() const { return mYcbcrConversionDesc.getExternalFormat(); }
|
|
bool updateChromaFilter(RendererVk *rendererVk, VkFilter filter)
|
|
{
|
|
return mYcbcrConversionDesc.updateChromaFilter(rendererVk, filter);
|
|
}
|
|
const YcbcrConversionDesc &getYcbcrConversionDesc() const { return mYcbcrConversionDesc; }
|
|
const YcbcrConversionDesc getY2YConversionDesc() const
|
|
{
|
|
YcbcrConversionDesc y2yDesc = mYcbcrConversionDesc;
|
|
y2yDesc.updateConversionModel(VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY);
|
|
return y2yDesc;
|
|
}
|
|
void updateYcbcrConversionDesc(RendererVk *rendererVk,
|
|
uint64_t externalFormat,
|
|
VkSamplerYcbcrModelConversion conversionModel,
|
|
VkSamplerYcbcrRange colorRange,
|
|
VkChromaLocation xChromaOffset,
|
|
VkChromaLocation yChromaOffset,
|
|
VkFilter chromaFilter,
|
|
VkComponentMapping components,
|
|
angle::FormatID intendedFormatID)
|
|
{
|
|
mYcbcrConversionDesc.update(rendererVk, externalFormat, conversionModel, colorRange,
|
|
xChromaOffset, yChromaOffset, chromaFilter, components,
|
|
intendedFormatID);
|
|
}
|
|
|
|
// Used by framebuffer and render pass functions to decide loadOps and invalidate/un-invalidate
|
|
// render target contents.
|
|
bool hasSubresourceDefinedContent(gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount) const;
|
|
bool hasSubresourceDefinedStencilContent(gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount) const;
|
|
void invalidateSubresourceContent(ContextVk *contextVk,
|
|
gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount,
|
|
bool *preferToKeepContentsDefinedOut);
|
|
void invalidateSubresourceStencilContent(ContextVk *contextVk,
|
|
gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount,
|
|
bool *preferToKeepContentsDefinedOut);
|
|
void restoreSubresourceContent(gl::LevelIndex level, uint32_t layerIndex, uint32_t layerCount);
|
|
void restoreSubresourceStencilContent(gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount);
|
|
angle::Result reformatStagedBufferUpdates(ContextVk *contextVk,
|
|
angle::FormatID srcFormatID,
|
|
angle::FormatID dstFormatID);
|
|
bool hasStagedImageUpdatesWithMismatchedFormat(gl::LevelIndex levelStart,
|
|
gl::LevelIndex levelEnd,
|
|
angle::FormatID formatID) const;
|
|
|
|
void setAcquireNextImageSemaphore(VkSemaphore semaphore)
|
|
{
|
|
ASSERT(semaphore != VK_NULL_HANDLE);
|
|
ASSERT(!mAcquireNextImageSemaphore.valid());
|
|
mAcquireNextImageSemaphore.setHandle(semaphore);
|
|
}
|
|
const Semaphore &getAcquireNextImageSemaphore() const { return mAcquireNextImageSemaphore; }
|
|
void resetAcquireNextImageSemaphore() { mAcquireNextImageSemaphore.release(); }
|
|
|
|
private:
|
|
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
|
|
struct ClearUpdate
|
|
{
|
|
bool operator==(const ClearUpdate &rhs) const
|
|
{
|
|
return memcmp(this, &rhs, sizeof(ClearUpdate)) == 0;
|
|
}
|
|
VkImageAspectFlags aspectFlags;
|
|
VkClearValue value;
|
|
uint32_t levelIndex;
|
|
uint32_t layerIndex;
|
|
uint32_t layerCount;
|
|
// For ClearEmulatedChannelsOnly, mask of which channels to clear.
|
|
VkColorComponentFlags colorMaskFlags;
|
|
};
|
|
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
|
|
struct BufferUpdate
|
|
{
|
|
BufferHelper *bufferHelper;
|
|
VkBufferImageCopy copyRegion;
|
|
angle::FormatID formatID;
|
|
};
|
|
struct ImageUpdate
|
|
{
|
|
VkImageCopy copyRegion;
|
|
angle::FormatID formatID;
|
|
};
|
|
|
|
struct SubresourceUpdate : angle::NonCopyable
|
|
{
|
|
SubresourceUpdate();
|
|
~SubresourceUpdate();
|
|
SubresourceUpdate(RefCounted<BufferHelper> *bufferIn,
|
|
BufferHelper *bufferHelperIn,
|
|
const VkBufferImageCopy ©Region,
|
|
angle::FormatID formatID);
|
|
SubresourceUpdate(RefCounted<ImageHelper> *imageIn,
|
|
const VkImageCopy ©Region,
|
|
angle::FormatID formatID);
|
|
SubresourceUpdate(VkImageAspectFlags aspectFlags,
|
|
const VkClearValue &clearValue,
|
|
const gl::ImageIndex &imageIndex);
|
|
SubresourceUpdate(VkImageAspectFlags aspectFlags,
|
|
const VkClearValue &clearValue,
|
|
gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount);
|
|
SubresourceUpdate(VkColorComponentFlags colorMaskFlags,
|
|
const VkClearColorValue &clearValue,
|
|
const gl::ImageIndex &imageIndex);
|
|
SubresourceUpdate(SubresourceUpdate &&other);
|
|
|
|
SubresourceUpdate &operator=(SubresourceUpdate &&other);
|
|
|
|
void release(RendererVk *renderer);
|
|
|
|
bool isUpdateToLayers(uint32_t layerIndex, uint32_t layerCount) const;
|
|
void getDestSubresource(uint32_t imageLayerCount,
|
|
uint32_t *baseLayerOut,
|
|
uint32_t *layerCountOut) const;
|
|
VkImageAspectFlags getDestAspectFlags() const;
|
|
|
|
UpdateSource updateSource;
|
|
union
|
|
{
|
|
ClearUpdate clear;
|
|
BufferUpdate buffer;
|
|
ImageUpdate image;
|
|
} data;
|
|
union
|
|
{
|
|
RefCounted<ImageHelper> *image;
|
|
RefCounted<BufferHelper> *buffer;
|
|
} refCounted;
|
|
};
|
|
|
|
// Up to 8 layers are tracked per level for whether contents are defined, above which the
|
|
// contents are considered unconditionally defined. This handles the more likely scenarios of:
|
|
//
|
|
// - Single layer framebuffer attachments,
|
|
// - Cube map framebuffer attachments,
|
|
// - Multi-view rendering.
|
|
//
|
|
// If there arises a need to optimize an application that invalidates layer >= 8, this can
|
|
// easily be raised to 32 to 64 bits. Beyond that, an additional hash map can be used to track
|
|
// such subresources.
|
|
static constexpr uint32_t kMaxContentDefinedLayerCount = 8;
|
|
using LevelContentDefinedMask = angle::BitSet8<kMaxContentDefinedLayerCount>;
|
|
|
|
void deriveExternalImageTiling(const void *createInfoChain);
|
|
|
|
// Used to initialize ImageFormats from actual format, with no pNext from a VkImageCreateInfo
|
|
// object.
|
|
void setImageFormatsFromActualFormat(VkFormat actualFormat, ImageFormats &imageFormatsOut);
|
|
|
|
// Called from flushStagedUpdates, removes updates that are later superseded by another. This
|
|
// cannot be done at the time the updates were staged, as the image is not created (and thus the
|
|
// extents are not known).
|
|
void removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask skipLevelsMask);
|
|
|
|
void initImageMemoryBarrierStruct(Context *context,
|
|
VkImageAspectFlags aspectMask,
|
|
ImageLayout newLayout,
|
|
uint32_t newQueueFamilyIndex,
|
|
VkImageMemoryBarrier *imageMemoryBarrier) const;
|
|
|
|
// Generalized to accept both "primary" and "secondary" command buffers.
|
|
template <typename CommandBufferT>
|
|
void barrierImpl(Context *context,
|
|
VkImageAspectFlags aspectMask,
|
|
ImageLayout newLayout,
|
|
uint32_t newQueueFamilyIndex,
|
|
CommandBufferT *commandBuffer,
|
|
VkSemaphore *acquireNextImageSemaphoreOut);
|
|
|
|
// If the image has emulated channels, we clear them once so as not to leave garbage on those
|
|
// channels.
|
|
VkColorComponentFlags getEmulatedChannelsMask() const;
|
|
void stageClearIfEmulatedFormat(bool isRobustResourceInitEnabled, bool isExternalImage);
|
|
bool verifyEmulatedClearsAreBeforeOtherUpdates(const std::vector<SubresourceUpdate> &updates);
|
|
|
|
// Clear either color or depth/stencil based on image format.
|
|
void clear(Context *context,
|
|
VkImageAspectFlags aspectFlags,
|
|
const VkClearValue &value,
|
|
LevelIndex mipLevel,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
void clearColor(Context *context,
|
|
const VkClearColorValue &color,
|
|
LevelIndex baseMipLevelVk,
|
|
uint32_t levelCount,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
void clearDepthStencil(Context *context,
|
|
VkImageAspectFlags clearAspectFlags,
|
|
const VkClearDepthStencilValue &depthStencil,
|
|
LevelIndex baseMipLevelVk,
|
|
uint32_t levelCount,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount,
|
|
OutsideRenderPassCommandBuffer *commandBuffer);
|
|
|
|
angle::Result clearEmulatedChannels(ContextVk *contextVk,
|
|
VkColorComponentFlags colorMaskFlags,
|
|
const VkClearValue &value,
|
|
LevelIndex mipLevel,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount);
|
|
|
|
angle::Result initializeNonZeroMemory(Context *context,
|
|
bool hasProtectedContent,
|
|
VkMemoryPropertyFlags flags,
|
|
VkDeviceSize size);
|
|
|
|
std::vector<SubresourceUpdate> *getLevelUpdates(gl::LevelIndex level);
|
|
const std::vector<SubresourceUpdate> *getLevelUpdates(gl::LevelIndex level) const;
|
|
|
|
void appendSubresourceUpdate(gl::LevelIndex level, SubresourceUpdate &&update);
|
|
void prependSubresourceUpdate(gl::LevelIndex level, SubresourceUpdate &&update);
|
|
|
|
enum class PruneReason
|
|
{
|
|
MemoryOptimization,
|
|
MinimizeWorkBeforeFlush
|
|
};
|
|
void pruneSupersededUpdatesForLevel(ContextVk *contextVk,
|
|
const gl::LevelIndex level,
|
|
const PruneReason reason);
|
|
|
|
// Whether there are any updates in [start, end).
|
|
bool hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelIndex levelEnd) const;
|
|
|
|
// Used only for assertions, these functions verify that
|
|
// SubresourceUpdate::refcountedObject::image or buffer references have the correct ref count.
|
|
// This is to prevent accidental leaks.
|
|
bool validateSubresourceUpdateImageRefConsistent(RefCounted<ImageHelper> *image) const;
|
|
bool validateSubresourceUpdateBufferRefConsistent(RefCounted<BufferHelper> *buffer) const;
|
|
bool validateSubresourceUpdateRefCountsConsistent() const;
|
|
|
|
void resetCachedProperties();
|
|
void setEntireContentDefined();
|
|
void setEntireContentUndefined();
|
|
void setContentDefined(LevelIndex levelStart,
|
|
uint32_t levelCount,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags);
|
|
void invalidateSubresourceContentImpl(ContextVk *contextVk,
|
|
gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlagBits aspect,
|
|
LevelContentDefinedMask *contentDefinedMask,
|
|
bool *preferToKeepContentsDefinedOut);
|
|
void restoreSubresourceContentImpl(gl::LevelIndex level,
|
|
uint32_t layerIndex,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlagBits aspect,
|
|
LevelContentDefinedMask *contentDefinedMask);
|
|
|
|
// Use the following functions to access m*ContentDefined to make sure the correct level index
|
|
// is used (i.e. vk::LevelIndex and not gl::LevelIndex).
|
|
LevelContentDefinedMask &getLevelContentDefined(LevelIndex level);
|
|
LevelContentDefinedMask &getLevelStencilContentDefined(LevelIndex level);
|
|
const LevelContentDefinedMask &getLevelContentDefined(LevelIndex level) const;
|
|
const LevelContentDefinedMask &getLevelStencilContentDefined(LevelIndex level) const;
|
|
|
|
angle::Result initLayerImageViewImpl(Context *context,
|
|
gl::TextureType textureType,
|
|
VkImageAspectFlags aspectMask,
|
|
const gl::SwizzleState &swizzleMap,
|
|
ImageView *imageViewOut,
|
|
LevelIndex baseMipLevelVk,
|
|
uint32_t levelCount,
|
|
uint32_t baseArrayLayer,
|
|
uint32_t layerCount,
|
|
VkFormat imageFormat,
|
|
VkImageUsageFlags usageFlags,
|
|
gl::YuvSamplingMode yuvSamplingMode) const;
|
|
|
|
angle::Result readPixelsImpl(ContextVk *contextVk,
|
|
const gl::Rectangle &area,
|
|
const PackPixelsParams &packPixelsParams,
|
|
VkImageAspectFlagBits copyAspectFlags,
|
|
gl::LevelIndex levelGL,
|
|
uint32_t layer,
|
|
void *pixels);
|
|
|
|
angle::Result packReadPixelBuffer(ContextVk *contextVk,
|
|
const gl::Rectangle &area,
|
|
const PackPixelsParams &packPixelsParams,
|
|
const angle::Format &readFormat,
|
|
const angle::Format &aspectFormat,
|
|
const uint8_t *readPixelBuffer,
|
|
gl::LevelIndex levelGL,
|
|
void *pixels);
|
|
|
|
bool canCopyWithTransformForReadPixels(const PackPixelsParams &packPixelsParams,
|
|
const angle::Format *readFormat);
|
|
|
|
// Returns true if source data and actual image format matches except color space differences.
|
|
bool isDataFormatMatchForCopy(angle::FormatID srcDataFormatID) const
|
|
{
|
|
if (mActualFormatID == srcDataFormatID)
|
|
{
|
|
return true;
|
|
}
|
|
angle::FormatID actualFormatLinear =
|
|
getActualFormat().isSRGB ? ConvertToLinear(mActualFormatID) : mActualFormatID;
|
|
angle::FormatID srcDataFormatIDLinear = angle::Format::Get(srcDataFormatID).isSRGB
|
|
? ConvertToLinear(srcDataFormatID)
|
|
: srcDataFormatID;
|
|
return actualFormatLinear == srcDataFormatIDLinear;
|
|
}
|
|
|
|
// Vulkan objects.
|
|
Image mImage;
|
|
DeviceMemory mDeviceMemory;
|
|
Allocation mVmaAllocation;
|
|
|
|
// Image properties.
|
|
VkImageCreateInfo mVkImageCreateInfo;
|
|
VkImageType mImageType;
|
|
VkImageTiling mTilingMode;
|
|
VkImageCreateFlags mCreateFlags;
|
|
VkImageUsageFlags mUsage;
|
|
// For Android swapchain images, the Vulkan VkImage must be "rotated". However, most of ANGLE
|
|
// uses non-rotated extents (i.e. the way the application views the extents--see "Introduction
|
|
// to Android rotation and pre-rotation" in "SurfaceVk.cpp"). Thus, mExtents are non-rotated.
|
|
// The rotated extents are also stored along with a bool that indicates if the aspect ratio is
|
|
// different between the rotated and non-rotated extents.
|
|
VkExtent3D mExtents;
|
|
bool mRotatedAspectRatio;
|
|
angle::FormatID mIntendedFormatID;
|
|
angle::FormatID mActualFormatID;
|
|
GLint mSamples;
|
|
ImageSerial mImageSerial;
|
|
|
|
// Current state.
|
|
ImageLayout mCurrentLayout;
|
|
uint32_t mCurrentQueueFamilyIndex;
|
|
// For optimizing transition between different shader readonly layouts
|
|
ImageLayout mLastNonShaderReadOnlyLayout;
|
|
VkPipelineStageFlags mCurrentShaderReadStageMask;
|
|
// Track how it is being used by current open renderpass.
|
|
RenderPassUsageFlags mRenderPassUsageFlags;
|
|
// The QueueSerial that associated with the last barrier.
|
|
QueueSerial mBarrierQueueSerial;
|
|
|
|
// For imported images
|
|
YcbcrConversionDesc mYcbcrConversionDesc;
|
|
|
|
// The first level that has been allocated. For mutable textures, this should be same as
|
|
// mBaseLevel since we always reallocate VkImage based on mBaseLevel change. But for immutable
|
|
// textures, we always allocate from level 0 regardless of mBaseLevel change.
|
|
gl::LevelIndex mFirstAllocatedLevel;
|
|
|
|
// Cached properties.
|
|
uint32_t mLayerCount;
|
|
uint32_t mLevelCount;
|
|
|
|
// Image formats used for imageless framebuffers.
|
|
ImageFormats mViewFormats;
|
|
|
|
std::vector<std::vector<SubresourceUpdate>> mSubresourceUpdates;
|
|
VkDeviceSize mTotalStagedBufferUpdateSize;
|
|
|
|
// Optimization for repeated clear with the same value. If this pointer is not null, the entire
|
|
// image it has been cleared to the specified clear value. If another clear call is made with
|
|
// the exact same clear value, we will detect and skip the clear call.
|
|
Optional<ClearUpdate> mCurrentSingleClearValue;
|
|
|
|
// Track whether each subresource has defined contents. Up to 8 layers are tracked per level,
|
|
// above which the contents are considered unconditionally defined.
|
|
gl::TexLevelArray<LevelContentDefinedMask> mContentDefined;
|
|
gl::TexLevelArray<LevelContentDefinedMask> mStencilContentDefined;
|
|
|
|
// Used for memory allocation tracking.
|
|
// Memory size allocated for the image in the memory during the initialization.
|
|
VkDeviceSize mAllocationSize;
|
|
// Type of the memory allocation for the image (Image or ImageExternal).
|
|
MemoryAllocationType mMemoryAllocationType;
|
|
// Memory type index used for the allocation. It can be used to determine the heap index.
|
|
uint32_t mMemoryTypeIndex;
|
|
|
|
// Only used for swapChain images. This is set when an image is acquired and is waited on
|
|
// by the next submission (which uses this image), at which point it is released.
|
|
Semaphore mAcquireNextImageSemaphore;
|
|
};
|
|
|
|
ANGLE_INLINE bool RenderPassCommandBufferHelper::usesImage(const ImageHelper &image) const
|
|
{
|
|
return image.usedByCommandBuffer(mQueueSerial);
|
|
}
|
|
|
|
ANGLE_INLINE bool RenderPassCommandBufferHelper::startedAndUsesImageWithBarrier(
|
|
const ImageHelper &image) const
|
|
{
|
|
return mRenderPassStarted && image.getBarrierQueueSerial() == mQueueSerial;
|
|
}
|
|
|
|
// A vector of image views, such as one per level or one per layer.
|
|
using ImageViewVector = std::vector<ImageView>;
|
|
|
|
// A vector of vector of image views. Primary index is layer, secondary index is level.
|
|
using LayerLevelImageViewVector = std::vector<ImageViewVector>;
|
|
|
|
// Address mode for layers: only possible to access either all layers, or up to
|
|
// IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS layers. This enum uses 0 for all layers and the rest of
|
|
// the values conveniently alias the number of layers.
|
|
enum LayerMode
|
|
{
|
|
All,
|
|
_1,
|
|
_2,
|
|
_3,
|
|
_4,
|
|
};
|
|
static_assert(gl::IMPLEMENTATION_ANGLE_MULTIVIEW_MAX_VIEWS == 4, "Update LayerMode");
|
|
|
|
LayerMode GetLayerMode(const vk::ImageHelper &image, uint32_t layerCount);
|
|
|
|
// Sampler decode mode indicating if an attachment needs to be decoded in linear colorspace or sRGB
|
|
enum class SrgbDecodeMode
|
|
{
|
|
SkipDecode,
|
|
SrgbDecode
|
|
};
|
|
|
|
class ImageViewHelper final : angle::NonCopyable
|
|
{
|
|
public:
|
|
ImageViewHelper();
|
|
ImageViewHelper(ImageViewHelper &&other);
|
|
~ImageViewHelper();
|
|
|
|
void init(RendererVk *renderer);
|
|
void destroy(VkDevice device);
|
|
|
|
const ImageView &getLinearReadImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeLinearReadImageViews);
|
|
}
|
|
const ImageView &getSRGBReadImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeSRGBReadImageViews);
|
|
}
|
|
const ImageView &getLinearFetchImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeLinearFetchImageViews);
|
|
}
|
|
const ImageView &getSRGBFetchImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeSRGBFetchImageViews);
|
|
}
|
|
const ImageView &getLinearCopyImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeLinearCopyImageViews);
|
|
}
|
|
const ImageView &getSRGBCopyImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeSRGBCopyImageViews);
|
|
}
|
|
const ImageView &getStencilReadImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeStencilReadImageViews);
|
|
}
|
|
|
|
const ImageView &getReadImageView() const
|
|
{
|
|
return mLinearColorspace ? getReadViewImpl(mPerLevelRangeLinearReadImageViews)
|
|
: getReadViewImpl(mPerLevelRangeSRGBReadImageViews);
|
|
}
|
|
|
|
const ImageView &getFetchImageView() const
|
|
{
|
|
return mLinearColorspace ? getReadViewImpl(mPerLevelRangeLinearFetchImageViews)
|
|
: getReadViewImpl(mPerLevelRangeSRGBFetchImageViews);
|
|
}
|
|
|
|
const ImageView &getCopyImageView() const
|
|
{
|
|
return mLinearColorspace ? getReadViewImpl(mPerLevelRangeLinearCopyImageViews)
|
|
: getReadViewImpl(mPerLevelRangeSRGBCopyImageViews);
|
|
}
|
|
|
|
ImageView &getSamplerExternal2DY2YEXTImageView()
|
|
{
|
|
return getReadViewImpl(mPerLevelRangeSamplerExternal2DY2YEXTImageViews);
|
|
}
|
|
|
|
const ImageView &getSamplerExternal2DY2YEXTImageView() const
|
|
{
|
|
return getValidReadViewImpl(mPerLevelRangeSamplerExternal2DY2YEXTImageViews);
|
|
}
|
|
|
|
// Used when initialized RenderTargets.
|
|
bool hasStencilReadImageView() const
|
|
{
|
|
return mCurrentBaseMaxLevelHash < mPerLevelRangeStencilReadImageViews.size()
|
|
? mPerLevelRangeStencilReadImageViews[mCurrentBaseMaxLevelHash].valid()
|
|
: false;
|
|
}
|
|
|
|
bool hasFetchImageView() const
|
|
{
|
|
if ((mLinearColorspace &&
|
|
mCurrentBaseMaxLevelHash < mPerLevelRangeLinearFetchImageViews.size()) ||
|
|
(!mLinearColorspace &&
|
|
mCurrentBaseMaxLevelHash < mPerLevelRangeSRGBFetchImageViews.size()))
|
|
{
|
|
return getFetchImageView().valid();
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool hasCopyImageView() const
|
|
{
|
|
if ((mLinearColorspace &&
|
|
mCurrentBaseMaxLevelHash < mPerLevelRangeLinearCopyImageViews.size()) ||
|
|
(!mLinearColorspace &&
|
|
mCurrentBaseMaxLevelHash < mPerLevelRangeSRGBCopyImageViews.size()))
|
|
{
|
|
return getCopyImageView().valid();
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// For applications that frequently switch a texture's max level, and make no other changes to
|
|
// the texture, change the currently-used max level, and potentially create new "read views"
|
|
// for the new max-level
|
|
angle::Result initReadViews(ContextVk *contextVk,
|
|
gl::TextureType viewType,
|
|
const ImageHelper &image,
|
|
const gl::SwizzleState &formatSwizzle,
|
|
const gl::SwizzleState &readSwizzle,
|
|
LevelIndex baseLevel,
|
|
uint32_t levelCount,
|
|
uint32_t baseLayer,
|
|
uint32_t layerCount,
|
|
bool requiresSRGBViews,
|
|
VkImageUsageFlags imageUsageFlags);
|
|
|
|
// Creates a storage view with all layers of the level.
|
|
angle::Result getLevelStorageImageView(Context *context,
|
|
gl::TextureType viewType,
|
|
const ImageHelper &image,
|
|
LevelIndex levelVk,
|
|
uint32_t layer,
|
|
VkImageUsageFlags imageUsageFlags,
|
|
angle::FormatID formatID,
|
|
const ImageView **imageViewOut);
|
|
|
|
// Creates a storage view with a single layer of the level.
|
|
angle::Result getLevelLayerStorageImageView(Context *context,
|
|
const ImageHelper &image,
|
|
LevelIndex levelVk,
|
|
uint32_t layer,
|
|
VkImageUsageFlags imageUsageFlags,
|
|
angle::FormatID formatID,
|
|
const ImageView **imageViewOut);
|
|
|
|
// Creates a draw view with a range of layers of the level.
|
|
angle::Result getLevelDrawImageView(Context *context,
|
|
const ImageHelper &image,
|
|
LevelIndex levelVk,
|
|
uint32_t layer,
|
|
uint32_t layerCount,
|
|
gl::SrgbWriteControlMode mode,
|
|
const ImageView **imageViewOut);
|
|
|
|
// Creates a draw view with a single layer of the level.
|
|
angle::Result getLevelLayerDrawImageView(Context *context,
|
|
const ImageHelper &image,
|
|
LevelIndex levelVk,
|
|
uint32_t layer,
|
|
gl::SrgbWriteControlMode mode,
|
|
const ImageView **imageViewOut);
|
|
|
|
// Return unique Serial for an imageView.
|
|
ImageOrBufferViewSubresourceSerial getSubresourceSerial(
|
|
gl::LevelIndex levelGL,
|
|
uint32_t levelCount,
|
|
uint32_t layer,
|
|
LayerMode layerMode,
|
|
SrgbDecodeMode srgbDecodeMode,
|
|
gl::SrgbOverride srgbOverrideMode) const;
|
|
|
|
bool isImageViewGarbageEmpty() const;
|
|
|
|
void release(RendererVk *renderer, const ResourceUse &use);
|
|
|
|
private:
|
|
ImageView &getReadImageView()
|
|
{
|
|
return mLinearColorspace ? getReadViewImpl(mPerLevelRangeLinearReadImageViews)
|
|
: getReadViewImpl(mPerLevelRangeSRGBReadImageViews);
|
|
}
|
|
ImageView &getFetchImageView()
|
|
{
|
|
return mLinearColorspace ? getReadViewImpl(mPerLevelRangeLinearFetchImageViews)
|
|
: getReadViewImpl(mPerLevelRangeSRGBFetchImageViews);
|
|
}
|
|
ImageView &getCopyImageView()
|
|
{
|
|
return mLinearColorspace ? getReadViewImpl(mPerLevelRangeLinearCopyImageViews)
|
|
: getReadViewImpl(mPerLevelRangeSRGBCopyImageViews);
|
|
}
|
|
|
|
// Used by public get*ImageView() methods to do proper assert based on vector size and validity
|
|
inline const ImageView &getValidReadViewImpl(const ImageViewVector &imageViewVector) const
|
|
{
|
|
ASSERT(mCurrentBaseMaxLevelHash < imageViewVector.size() &&
|
|
imageViewVector[mCurrentBaseMaxLevelHash].valid());
|
|
return imageViewVector[mCurrentBaseMaxLevelHash];
|
|
}
|
|
|
|
// Used by public get*ImageView() methods to do proper assert based on vector size
|
|
inline const ImageView &getReadViewImpl(const ImageViewVector &imageViewVector) const
|
|
{
|
|
ASSERT(mCurrentBaseMaxLevelHash < imageViewVector.size());
|
|
return imageViewVector[mCurrentBaseMaxLevelHash];
|
|
}
|
|
|
|
// Used by private get*ImageView() methods to do proper assert based on vector size
|
|
inline ImageView &getReadViewImpl(ImageViewVector &imageViewVector)
|
|
{
|
|
ASSERT(mCurrentBaseMaxLevelHash < imageViewVector.size());
|
|
return imageViewVector[mCurrentBaseMaxLevelHash];
|
|
}
|
|
|
|
// Creates views with multiple layers and levels.
|
|
angle::Result initReadViewsImpl(ContextVk *contextVk,
|
|
gl::TextureType viewType,
|
|
const ImageHelper &image,
|
|
const gl::SwizzleState &formatSwizzle,
|
|
const gl::SwizzleState &readSwizzle,
|
|
LevelIndex baseLevel,
|
|
uint32_t levelCount,
|
|
uint32_t baseLayer,
|
|
uint32_t layerCount,
|
|
VkImageUsageFlags imageUsageFlags);
|
|
|
|
// Create SRGB-reinterpreted read views
|
|
angle::Result initSRGBReadViewsImpl(ContextVk *contextVk,
|
|
gl::TextureType viewType,
|
|
const ImageHelper &image,
|
|
const gl::SwizzleState &formatSwizzle,
|
|
const gl::SwizzleState &readSwizzle,
|
|
LevelIndex baseLevel,
|
|
uint32_t levelCount,
|
|
uint32_t baseLayer,
|
|
uint32_t layerCount,
|
|
VkImageUsageFlags imageUsageFlags);
|
|
|
|
// For applications that frequently switch a texture's base/max level, and make no other changes
|
|
// to the texture, keep track of the currently-used base and max levels, and keep one "read
|
|
// view" per each combination. The value stored here is base<<4|max, used to look up the view
|
|
// in a vector.
|
|
static_assert(gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS <= 16,
|
|
"Not enough bits in mCurrentBaseMaxLevelHash");
|
|
uint8_t mCurrentBaseMaxLevelHash;
|
|
|
|
bool mLinearColorspace;
|
|
|
|
// Read views (one per [base, max] level range)
|
|
ImageViewVector mPerLevelRangeLinearReadImageViews;
|
|
ImageViewVector mPerLevelRangeSRGBReadImageViews;
|
|
ImageViewVector mPerLevelRangeLinearFetchImageViews;
|
|
ImageViewVector mPerLevelRangeSRGBFetchImageViews;
|
|
ImageViewVector mPerLevelRangeLinearCopyImageViews;
|
|
ImageViewVector mPerLevelRangeSRGBCopyImageViews;
|
|
ImageViewVector mPerLevelRangeStencilReadImageViews;
|
|
ImageViewVector mPerLevelRangeSamplerExternal2DY2YEXTImageViews;
|
|
|
|
// Draw views
|
|
LayerLevelImageViewVector mLayerLevelDrawImageViews;
|
|
LayerLevelImageViewVector mLayerLevelDrawImageViewsLinear;
|
|
angle::HashMap<ImageSubresourceRange, std::unique_ptr<ImageView>> mSubresourceDrawImageViews;
|
|
|
|
// Storage views
|
|
ImageViewVector mLevelStorageImageViews;
|
|
LayerLevelImageViewVector mLayerLevelStorageImageViews;
|
|
|
|
// Serial for the image view set. getSubresourceSerial combines it with subresource info.
|
|
ImageOrBufferViewSerial mImageViewSerial;
|
|
};
|
|
|
|
ImageSubresourceRange MakeImageSubresourceReadRange(gl::LevelIndex level,
|
|
uint32_t levelCount,
|
|
uint32_t layer,
|
|
LayerMode layerMode,
|
|
SrgbDecodeMode srgbDecodeMode,
|
|
gl::SrgbOverride srgbOverrideMode);
|
|
ImageSubresourceRange MakeImageSubresourceDrawRange(gl::LevelIndex level,
|
|
uint32_t layer,
|
|
LayerMode layerMode,
|
|
gl::SrgbWriteControlMode srgbWriteControlMode);
|
|
|
|
class BufferViewHelper final : public Resource
|
|
{
|
|
public:
|
|
BufferViewHelper();
|
|
BufferViewHelper(BufferViewHelper &&other);
|
|
~BufferViewHelper() override;
|
|
|
|
void init(RendererVk *renderer, VkDeviceSize offset, VkDeviceSize size);
|
|
void release(ContextVk *contextVk);
|
|
void destroy(VkDevice device);
|
|
|
|
angle::Result getView(Context *context,
|
|
const BufferHelper &buffer,
|
|
VkDeviceSize bufferOffset,
|
|
const Format &format,
|
|
const BufferView **viewOut);
|
|
|
|
// Return unique Serial for a bufferView.
|
|
ImageOrBufferViewSubresourceSerial getSerial() const;
|
|
|
|
private:
|
|
// To support format reinterpretation, additional views for formats other than the one specified
|
|
// to glTexBuffer may need to be created. On draw/dispatch, the format layout qualifier of the
|
|
// imageBuffer is used (if provided) to create a potentially different view of the buffer.
|
|
angle::HashMap<VkFormat, BufferView> mViews;
|
|
|
|
// View properties:
|
|
//
|
|
// Offset and size specified to glTexBufferRange
|
|
VkDeviceSize mOffset;
|
|
VkDeviceSize mSize;
|
|
|
|
// Serial for the buffer view. An ImageOrBufferViewSerial is used for texture buffers so that
|
|
// they fit together with the other texture types.
|
|
ImageOrBufferViewSerial mViewSerial;
|
|
};
|
|
|
|
// Context state that can affect a compute pipeline
|
|
enum class ComputePipelineFlag : uint8_t
|
|
{
|
|
// Whether VK_EXT_pipeline_robustness should be used to make the pipeline robust. Note that
|
|
// programs are allowed to be shared between robust and non-robust contexts, so different
|
|
// pipelines can be created for the same compute program.
|
|
Robust,
|
|
// Whether VK_EXT_pipeline_protected_access should be used to make the pipeline protected-only.
|
|
// Similar to robustness, EGL allows protected and unprotected to be in the same share group.
|
|
Protected,
|
|
|
|
InvalidEnum,
|
|
EnumCount = InvalidEnum,
|
|
};
|
|
|
|
using ComputePipelineFlags = angle::PackedEnumBitSet<ComputePipelineFlag, uint8_t>;
|
|
using ComputePipelineCache = std::array<PipelineHelper, 1u << ComputePipelineFlags::size()>;
|
|
|
|
class ShaderProgramHelper : angle::NonCopyable
|
|
{
|
|
public:
|
|
ShaderProgramHelper();
|
|
~ShaderProgramHelper();
|
|
|
|
bool valid(const gl::ShaderType shaderType) const;
|
|
void destroy(RendererVk *rendererVk);
|
|
void release(ContextVk *contextVk);
|
|
|
|
void setShader(gl::ShaderType shaderType, RefCounted<ShaderModule> *shader);
|
|
|
|
// Create a graphics pipeline and place it in the cache. Must not be called if the pipeline
|
|
// exists in cache.
|
|
template <typename PipelineHash>
|
|
ANGLE_INLINE angle::Result createGraphicsPipeline(
|
|
ContextVk *contextVk,
|
|
GraphicsPipelineCache<PipelineHash> *graphicsPipelines,
|
|
PipelineCacheAccess *pipelineCache,
|
|
const RenderPass &compatibleRenderPass,
|
|
const PipelineLayout &pipelineLayout,
|
|
PipelineSource source,
|
|
const GraphicsPipelineDesc &pipelineDesc,
|
|
const SpecializationConstants &specConsts,
|
|
const GraphicsPipelineDesc **descPtrOut,
|
|
PipelineHelper **pipelineOut) const
|
|
{
|
|
return graphicsPipelines->createPipeline(contextVk, pipelineCache, compatibleRenderPass,
|
|
pipelineLayout, mShaders, specConsts, source,
|
|
pipelineDesc, descPtrOut, pipelineOut);
|
|
}
|
|
|
|
void createMonolithicPipelineCreationTask(ContextVk *contextVk,
|
|
PipelineCacheAccess *pipelineCache,
|
|
const GraphicsPipelineDesc &desc,
|
|
const PipelineLayout &pipelineLayout,
|
|
const SpecializationConstants &specConsts,
|
|
PipelineHelper *pipeline) const;
|
|
|
|
angle::Result getOrCreateComputePipeline(ContextVk *contextVk,
|
|
ComputePipelineCache *computePipelines,
|
|
PipelineCacheAccess *pipelineCache,
|
|
const PipelineLayout &pipelineLayout,
|
|
ComputePipelineFlags pipelineFlags,
|
|
PipelineSource source,
|
|
PipelineHelper **pipelineOut) const;
|
|
|
|
private:
|
|
ShaderModuleMap mShaders;
|
|
};
|
|
|
|
// Tracks current handle allocation counts in the back-end. Useful for debugging and profiling.
|
|
// Note: not all handle types are currently implemented.
|
|
class ActiveHandleCounter final : angle::NonCopyable
|
|
{
|
|
public:
|
|
ActiveHandleCounter();
|
|
~ActiveHandleCounter();
|
|
|
|
void onAllocate(HandleType handleType)
|
|
{
|
|
mActiveCounts[handleType]++;
|
|
mAllocatedCounts[handleType]++;
|
|
}
|
|
|
|
void onDeallocate(HandleType handleType) { mActiveCounts[handleType]--; }
|
|
|
|
uint32_t getActive(HandleType handleType) const { return mActiveCounts[handleType]; }
|
|
uint32_t getAllocated(HandleType handleType) const { return mAllocatedCounts[handleType]; }
|
|
|
|
private:
|
|
angle::PackedEnumMap<HandleType, uint32_t> mActiveCounts;
|
|
angle::PackedEnumMap<HandleType, uint32_t> mAllocatedCounts;
|
|
};
|
|
|
|
// Sometimes ANGLE issues a command internally, such as copies, draws and dispatches that do not
|
|
// directly correspond to the application draw/dispatch call. Before the command is recorded in the
|
|
// command buffer, the render pass may need to be broken and/or appropriate barriers may need to be
|
|
// inserted. The following struct aggregates all resources that such internal commands need.
|
|
struct CommandBufferBufferAccess
|
|
{
|
|
BufferHelper *buffer;
|
|
VkAccessFlags accessType;
|
|
PipelineStage stage;
|
|
};
|
|
struct CommandBufferImageAccess
|
|
{
|
|
ImageHelper *image;
|
|
VkImageAspectFlags aspectFlags;
|
|
ImageLayout imageLayout;
|
|
};
|
|
struct CommandBufferImageWrite
|
|
{
|
|
CommandBufferImageAccess access;
|
|
gl::LevelIndex levelStart;
|
|
uint32_t levelCount;
|
|
uint32_t layerStart;
|
|
uint32_t layerCount;
|
|
};
|
|
struct CommandBufferBufferExternalAcquireRelease
|
|
{
|
|
BufferHelper *buffer;
|
|
};
|
|
struct CommandBufferResourceAccess
|
|
{
|
|
Resource *resource;
|
|
};
|
|
class CommandBufferAccess : angle::NonCopyable
|
|
{
|
|
public:
|
|
CommandBufferAccess();
|
|
~CommandBufferAccess();
|
|
|
|
void onBufferTransferRead(BufferHelper *buffer)
|
|
{
|
|
onBufferRead(VK_ACCESS_TRANSFER_READ_BIT, PipelineStage::Transfer, buffer);
|
|
}
|
|
void onBufferTransferWrite(BufferHelper *buffer)
|
|
{
|
|
onBufferWrite(VK_ACCESS_TRANSFER_WRITE_BIT, PipelineStage::Transfer, buffer);
|
|
}
|
|
void onBufferSelfCopy(BufferHelper *buffer)
|
|
{
|
|
onBufferWrite(VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
PipelineStage::Transfer, buffer);
|
|
}
|
|
void onBufferComputeShaderRead(BufferHelper *buffer)
|
|
{
|
|
onBufferRead(VK_ACCESS_SHADER_READ_BIT, PipelineStage::ComputeShader, buffer);
|
|
}
|
|
void onBufferComputeShaderWrite(BufferHelper *buffer)
|
|
{
|
|
onBufferWrite(VK_ACCESS_SHADER_WRITE_BIT, PipelineStage::ComputeShader, buffer);
|
|
}
|
|
|
|
void onImageTransferRead(VkImageAspectFlags aspectFlags, ImageHelper *image)
|
|
{
|
|
onImageRead(aspectFlags, ImageLayout::TransferSrc, image);
|
|
}
|
|
void onImageTransferWrite(gl::LevelIndex levelStart,
|
|
uint32_t levelCount,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageHelper *image)
|
|
{
|
|
onImageWrite(levelStart, levelCount, layerStart, layerCount, aspectFlags,
|
|
ImageLayout::TransferDst, image);
|
|
}
|
|
void onImageComputeShaderRead(VkImageAspectFlags aspectFlags, ImageHelper *image)
|
|
{
|
|
onImageRead(aspectFlags, ImageLayout::ComputeShaderReadOnly, image);
|
|
}
|
|
void onImageComputeShaderWrite(gl::LevelIndex levelStart,
|
|
uint32_t levelCount,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageHelper *image)
|
|
{
|
|
onImageWrite(levelStart, levelCount, layerStart, layerCount, aspectFlags,
|
|
ImageLayout::ComputeShaderWrite, image);
|
|
}
|
|
void onExternalAcquireRelease(ImageHelper *image) { onResourceAccess(image); }
|
|
void onQueryAccess(QueryHelper *query) { onResourceAccess(query); }
|
|
void onBufferExternalAcquireRelease(BufferHelper *buffer);
|
|
|
|
// The limits reflect the current maximum concurrent usage of each resource type. ASSERTs will
|
|
// fire if this limit is exceeded in the future.
|
|
using ReadBuffers = angle::FixedVector<CommandBufferBufferAccess, 2>;
|
|
using WriteBuffers = angle::FixedVector<CommandBufferBufferAccess, 2>;
|
|
using ReadImages = angle::FixedVector<CommandBufferImageAccess, 2>;
|
|
using WriteImages = angle::FixedVector<CommandBufferImageWrite, 1>;
|
|
using ExternalAcquireReleaseBuffers =
|
|
angle::FixedVector<CommandBufferBufferExternalAcquireRelease, 1>;
|
|
using AccessResources = angle::FixedVector<CommandBufferResourceAccess, 1>;
|
|
|
|
const ReadBuffers &getReadBuffers() const { return mReadBuffers; }
|
|
const WriteBuffers &getWriteBuffers() const { return mWriteBuffers; }
|
|
const ReadImages &getReadImages() const { return mReadImages; }
|
|
const WriteImages &getWriteImages() const { return mWriteImages; }
|
|
const ExternalAcquireReleaseBuffers &getExternalAcquireReleaseBuffers() const
|
|
{
|
|
return mExternalAcquireReleaseBuffers;
|
|
}
|
|
const AccessResources &getAccessResources() const { return mAccessResources; }
|
|
|
|
private:
|
|
void onBufferRead(VkAccessFlags readAccessType, PipelineStage readStage, BufferHelper *buffer);
|
|
void onBufferWrite(VkAccessFlags writeAccessType,
|
|
PipelineStage writeStage,
|
|
BufferHelper *buffer);
|
|
|
|
void onImageRead(VkImageAspectFlags aspectFlags, ImageLayout imageLayout, ImageHelper *image);
|
|
void onImageWrite(gl::LevelIndex levelStart,
|
|
uint32_t levelCount,
|
|
uint32_t layerStart,
|
|
uint32_t layerCount,
|
|
VkImageAspectFlags aspectFlags,
|
|
ImageLayout imageLayout,
|
|
ImageHelper *image);
|
|
void onResourceAccess(Resource *resource);
|
|
|
|
ReadBuffers mReadBuffers;
|
|
WriteBuffers mWriteBuffers;
|
|
ReadImages mReadImages;
|
|
WriteImages mWriteImages;
|
|
ExternalAcquireReleaseBuffers mExternalAcquireReleaseBuffers;
|
|
AccessResources mAccessResources;
|
|
};
|
|
|
|
// This class' responsibility is to create index buffers needed to support line loops in Vulkan.
|
|
// In the setup phase of drawing, the createIndexBuffer method should be called with the
|
|
// current draw call parameters. If an element array buffer is bound for an indexed draw, use
|
|
// createIndexBufferFromElementArrayBuffer.
|
|
//
|
|
// If the user wants to draw a loop between [v1, v2, v3], we will create an indexed buffer with
|
|
// these indexes: [0, 1, 2, 3, 0] to emulate the loop.
|
|
class LineLoopHelper final : angle::NonCopyable
|
|
{
|
|
public:
|
|
LineLoopHelper(RendererVk *renderer);
|
|
~LineLoopHelper();
|
|
|
|
angle::Result getIndexBufferForDrawArrays(ContextVk *contextVk,
|
|
uint32_t clampedVertexCount,
|
|
GLint firstVertex,
|
|
BufferHelper **bufferOut);
|
|
|
|
angle::Result getIndexBufferForElementArrayBuffer(ContextVk *contextVk,
|
|
BufferVk *elementArrayBufferVk,
|
|
gl::DrawElementsType glIndexType,
|
|
int indexCount,
|
|
intptr_t elementArrayOffset,
|
|
BufferHelper **bufferOut,
|
|
uint32_t *indexCountOut);
|
|
|
|
angle::Result streamIndices(ContextVk *contextVk,
|
|
gl::DrawElementsType glIndexType,
|
|
GLsizei indexCount,
|
|
const uint8_t *srcPtr,
|
|
BufferHelper **bufferOut,
|
|
uint32_t *indexCountOut);
|
|
|
|
angle::Result streamIndicesIndirect(ContextVk *contextVk,
|
|
gl::DrawElementsType glIndexType,
|
|
BufferHelper *indexBuffer,
|
|
BufferHelper *indirectBuffer,
|
|
VkDeviceSize indirectBufferOffset,
|
|
BufferHelper **indexBufferOut,
|
|
BufferHelper **indirectBufferOut);
|
|
|
|
angle::Result streamArrayIndirect(ContextVk *contextVk,
|
|
size_t vertexCount,
|
|
BufferHelper *arrayIndirectBuffer,
|
|
VkDeviceSize arrayIndirectBufferOffset,
|
|
BufferHelper **indexBufferOut,
|
|
BufferHelper **indexIndirectBufferOut);
|
|
|
|
void release(ContextVk *contextVk);
|
|
void destroy(RendererVk *renderer);
|
|
|
|
static void Draw(uint32_t count, uint32_t baseVertex, RenderPassCommandBuffer *commandBuffer);
|
|
|
|
private:
|
|
BufferHelper mDynamicIndexBuffer;
|
|
BufferHelper mDynamicIndirectBuffer;
|
|
};
|
|
|
|
enum class PresentMode
|
|
{
|
|
ImmediateKHR = VK_PRESENT_MODE_IMMEDIATE_KHR,
|
|
MailboxKHR = VK_PRESENT_MODE_MAILBOX_KHR,
|
|
FifoKHR = VK_PRESENT_MODE_FIFO_KHR,
|
|
FifoRelaxedKHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR,
|
|
SharedDemandRefreshKHR = VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR,
|
|
SharedContinuousRefreshKHR = VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR,
|
|
|
|
InvalidEnum,
|
|
EnumCount = InvalidEnum,
|
|
};
|
|
|
|
VkPresentModeKHR ConvertPresentModeToVkPresentMode(PresentMode presentMode);
|
|
PresentMode ConvertVkPresentModeToPresentMode(VkPresentModeKHR vkPresentMode);
|
|
|
|
} // namespace vk
|
|
} // namespace rx
|
|
|
|
#endif // LIBANGLE_RENDERER_VULKAN_VK_HELPERS_H_
|