vk: VulkanBuffer cannot skip staging buffer after a read (#8946)
The previous introduction of VulkanBufferProxy uses the refcount of the backing VulkanBuffer as an indication whether there's a write in progress or not. But we also want to make sure there is no read in progress (i.e. not referenced in a descriptor set). This commit fixes the bookkeeping while trying to clean up the relevant structs. We add logic to track whether a UBO is actively been read (i.e. bound to a descriptor set that is itself bound to a command buffer).
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -33,22 +34,16 @@ VulkanBufferProxy::VulkanBufferProxy(VulkanContext const& context, VmaAllocator
|
||||
mStagePool(stagePool),
|
||||
mBufferCache(bufferCache),
|
||||
mBuffer(mBufferCache.acquire(usage, numBytes)),
|
||||
mUpdatedOffset(0),
|
||||
mUpdatedBytes(0) {}
|
||||
mLastReadAge(0) {}
|
||||
|
||||
void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData,
|
||||
uint32_t byteOffset, uint32_t numBytes) {
|
||||
// The VulkanBuffer is available if the only object reference is hold by the
|
||||
// `VulkanBufferProxy`. This means that the buffer is not currently referenced by a
|
||||
// `VulkanCommandBuffer`.
|
||||
bool const isAvailable = mBuffer->getCount() == 1;
|
||||
|
||||
if (isAvailable) {
|
||||
// We are up to date with all the update operations, so no need to synchronize with previous
|
||||
// updates.
|
||||
mUpdatedBytes = 0;
|
||||
mUpdatedOffset = 0;
|
||||
}
|
||||
// This means that we're recording a write into a command buffer without a previous read, so it
|
||||
// should be safe to
|
||||
// 1) Do a direct memcpy in UMA mode
|
||||
// 2) Skip adding a barrier (to protect the write from writing over a read).
|
||||
bool const isAvailable = commands.age() != mLastReadAge;
|
||||
|
||||
// Keep track of the VulkanBuffer usage
|
||||
commands.acquire(mBuffer);
|
||||
@@ -67,6 +62,10 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
vmaFlushAllocation(mAllocator, mBuffer->getGpuBuffer()->vmaAllocation, byteOffset,
|
||||
numBytes);
|
||||
return;
|
||||
|
||||
// TODO: to properly bypass staging buffer, we'd need to be able to swap out a VulkanBuffer,
|
||||
// which represents a VkBuffer. This means that the corresponding descriptor sets also have
|
||||
// to be updated.
|
||||
}
|
||||
|
||||
// Note: this should be stored within the command buffer before going out of
|
||||
@@ -77,10 +76,9 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
memcpy(stage->mapping(), cpuData, numBytes);
|
||||
vmaFlushAllocation(mAllocator, stage->memory(), stage->offset(), numBytes);
|
||||
|
||||
// If there was a previous update, then we need to make sure the following write is properly
|
||||
// If there was a previous read, then we need to make sure the following write is properly
|
||||
// synced with the previous read.
|
||||
if (mUpdatedBytes > 0 &&
|
||||
(byteOffset >= mUpdatedOffset && byteOffset <= (mUpdatedOffset + mUpdatedBytes))) {
|
||||
if (!isAvailable) {
|
||||
VkAccessFlags srcAccess = 0;
|
||||
VkPipelineStageFlags srcStage = 0;
|
||||
if (getUsage() == VulkanBufferUsage::UNIFORM) {
|
||||
@@ -115,9 +113,6 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
};
|
||||
vkCmdCopyBuffer(commands.buffer(), stage->buffer(), getVkBuffer(), 1, ®ion);
|
||||
|
||||
mUpdatedOffset = byteOffset;
|
||||
mUpdatedBytes = numBytes;
|
||||
|
||||
// Firstly, ensure that the copy finishes before the next draw call.
|
||||
// Secondly, in case the user decides to upload another chunk (without ever using the first one)
|
||||
// we need to ensure that this upload completes first (hence
|
||||
@@ -161,4 +156,9 @@ VulkanBufferUsage VulkanBufferProxy::getUsage() const noexcept {
|
||||
return mBuffer->getGpuBuffer()->usage;
|
||||
}
|
||||
|
||||
void VulkanBufferProxy::referencedBy(VulkanCommandBuffer& commands) {
|
||||
commands.acquire(mBuffer);
|
||||
mLastReadAge = commands.age();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanDescriptorSet;
|
||||
struct VulkanCommandBuffer;
|
||||
|
||||
// This class acts as a dynamic wrapper for a `VulkanBuffer`. It allows you to modify the
|
||||
// `VulkanBuffer` it references at runtime, wihtout affecting any external objects.
|
||||
class VulkanBufferProxy {
|
||||
@@ -38,17 +41,19 @@ public:
|
||||
|
||||
VkBuffer getVkBuffer() const noexcept;
|
||||
|
||||
VulkanBufferUsage getUsage() const noexcept;
|
||||
void referencedBy(VulkanCommandBuffer& commands);
|
||||
|
||||
private:
|
||||
VulkanBufferUsage getUsage() const noexcept;
|
||||
|
||||
bool const mStagingBufferBypassEnabled;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanStagePool& mStagePool;
|
||||
VulkanBufferCache& mBufferCache;
|
||||
|
||||
fvkmemory::resource_ptr<VulkanBuffer> mBuffer;
|
||||
uint32_t mUpdatedOffset = 0;
|
||||
uint32_t mUpdatedBytes = 0;
|
||||
|
||||
uint32_t mLastReadAge;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -86,6 +86,8 @@ bool VulkanGroupMarkers::empty() const noexcept {
|
||||
}
|
||||
#endif // FVK_DEBUG_GROUP_MARKERS
|
||||
|
||||
uint32_t VulkanCommandBuffer::sAgeCounter = 0;
|
||||
|
||||
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice device,
|
||||
VkQueue queue, VkCommandPool pool, bool isProtected)
|
||||
: mContext(context),
|
||||
@@ -94,7 +96,8 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice
|
||||
mDevice(device),
|
||||
mQueue(queue),
|
||||
mBuffer(createCommandBuffer(device, pool)),
|
||||
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
|
||||
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)),
|
||||
mAge(++sAgeCounter) {
|
||||
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
||||
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
|
||||
|
||||
@@ -111,6 +114,7 @@ void VulkanCommandBuffer::reset() noexcept {
|
||||
mMarkerCount = 0;
|
||||
mResources.clear();
|
||||
mWaitSemaphores.clear();
|
||||
mAge = ++sAgeCounter;
|
||||
|
||||
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
|
||||
// gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
|
||||
|
||||
@@ -108,7 +108,13 @@ struct VulkanCommandBuffer {
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
uint32_t age() const {
|
||||
return mAge;
|
||||
}
|
||||
|
||||
private:
|
||||
static uint32_t sAgeCounter;
|
||||
|
||||
VulkanContext const& mContext;
|
||||
uint8_t mMarkerCount;
|
||||
bool const isProtected;
|
||||
@@ -120,6 +126,7 @@ private:
|
||||
VkFence mFence;
|
||||
std::shared_ptr<VulkanCmdFence> mFenceStatus;
|
||||
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
|
||||
uint32_t mAge;
|
||||
};
|
||||
|
||||
struct CommandBufferPool {
|
||||
|
||||
@@ -290,7 +290,9 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
||||
if (mLastBoundInfo.pipelineLayout == pipelineLayout) {
|
||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||
curMask.forEachSetBit([&](size_t index) {
|
||||
if (updateSets[index] == lastBoundSets[index] && !useExternalSamplers[index]) {
|
||||
auto& set = updateSets[index];
|
||||
if (set == lastBoundSets[index] && !useExternalSamplers[index] &&
|
||||
set->uniqueDynamicUboCount == 0) {
|
||||
curMask.unset(index);
|
||||
}
|
||||
});
|
||||
@@ -302,25 +304,24 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
VkDescriptorSet vkset = useExternalSamplers[index] ? set->getExternalSamplerVkSet() :
|
||||
set->getVkSet();
|
||||
commands->acquire(set);
|
||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||
commands->acquire(set);
|
||||
set->referencedBy(*commands);
|
||||
});
|
||||
|
||||
mStashedSets = {};
|
||||
|
||||
mLastBoundInfo = {
|
||||
pipelineLayout,
|
||||
setMask,
|
||||
updateSets,
|
||||
};
|
||||
mStashedSets = {};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
VkDescriptorBufferInfo const info = {
|
||||
.buffer = bufferObject->buffer.getVkBuffer(),
|
||||
.buffer = bufferObject->getVkBuffer(),
|
||||
.offset = offset,
|
||||
.range = size,
|
||||
};
|
||||
|
||||
@@ -1239,7 +1239,7 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
|
||||
commands.acquire(ib);
|
||||
ib->buffer.loadFromCpu(commands, p.buffer, byteOffset, p.size);
|
||||
ib->loadFromCpu(commands, p.buffer, byteOffset, p.size);
|
||||
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
@@ -1255,7 +1255,7 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
|
||||
|
||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
commands.acquire(bo);
|
||||
bo->buffer.loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
|
||||
scheduleDestroy(std::move(bd));
|
||||
}
|
||||
@@ -1266,7 +1266,7 @@ void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
|
||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
commands.acquire(bo);
|
||||
// TODO: implement unsynchronized version
|
||||
bo->buffer.loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
scheduleDestroy(std::move(bd));
|
||||
}
|
||||
|
||||
@@ -1915,7 +1915,7 @@ void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
|
||||
// it would be rare for a client to make consecutive draw calls with the same render primitive.
|
||||
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
|
||||
vkCmdBindIndexBuffer(cmdbuffer, prim->indexBuffer->buffer.getVkBuffer(), 0,
|
||||
vkCmdBindIndexBuffer(cmdbuffer, prim->indexBuffer->getVkBuffer(), 0,
|
||||
prim->indexBuffer->indexType);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,20 @@ namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
inline VulkanBufferUsage getBufferObjectUsage(BufferObjectBinding bindingType) noexcept {
|
||||
switch (bindingType) {
|
||||
case BufferObjectBinding::VERTEX:
|
||||
return VulkanBufferUsage::VERTEX;
|
||||
case BufferObjectBinding::UNIFORM:
|
||||
return VulkanBufferUsage::UNIFORM;
|
||||
case BufferObjectBinding::SHADER_STORAGE:
|
||||
return VulkanBufferUsage::SHADER_STORAGE;
|
||||
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
|
||||
// if necessary.
|
||||
}
|
||||
return VulkanBufferUsage::UNKNOWN;
|
||||
}
|
||||
|
||||
void flipVertically(VkViewport* rect, uint32_t framebufferHeight) {
|
||||
rect->y = framebufferHeight - rect->y - rect->height;
|
||||
}
|
||||
@@ -166,14 +180,6 @@ VulkanAttachment createSwapchainAttachment(const fvkmemory::resource_ptr<VulkanT
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanTexture> texture) {
|
||||
mResources.push_back(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> obj) {
|
||||
mResources.push_back(obj);
|
||||
}
|
||||
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
|
||||
VkDescriptorSetLayout vkLayout)
|
||||
: bitmask(fromBackendLayout(layout)),
|
||||
@@ -185,6 +191,17 @@ VulkanDescriptorSetLayout::Bitmask VulkanDescriptorSetLayout::Bitmask::fromLayou
|
||||
return fromBackendLayout(layout);
|
||||
}
|
||||
|
||||
// This method will store an age associated with this command buffer into the VulkanBuffer, which
|
||||
// will allow us to determine whether a barrier is necessary or not.
|
||||
void VulkanDescriptorSet::referencedBy(VulkanCommandBuffer& commands) {
|
||||
mUboMask.forEachSetBit([this, &commands](size_t index) {
|
||||
auto& res = mResources[index];
|
||||
fvkmemory::resource_ptr<VulkanBufferObject> bo =
|
||||
fvkmemory::resource_ptr<VulkanBufferObject>::cast((VulkanBufferObject*) res.get());
|
||||
bo->referencedBy(commands);
|
||||
});
|
||||
}
|
||||
|
||||
PushConstantDescription::PushConstantDescription(backend::Program const& program) {
|
||||
mRangeCount = 0;
|
||||
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
|
||||
@@ -588,7 +605,7 @@ void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> b
|
||||
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
|
||||
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
|
||||
if (attribToBuffer[attribIndex] == static_cast<int8_t>(index)) {
|
||||
vkbuffers[attribIndex] = bufferObject->buffer.getVkBuffer();
|
||||
vkbuffers[attribIndex] = bufferObject->getVkBuffer();
|
||||
}
|
||||
}
|
||||
mResources.push_back(bufferObject);
|
||||
@@ -598,9 +615,8 @@ VulkanBufferObject::VulkanBufferObject(VulkanContext const& context, VmaAllocato
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType)
|
||||
: HwBufferObject(byteCount),
|
||||
buffer(context, allocator, stagePool, bufferCache, getBufferObjectUsage(bindingType),
|
||||
byteCount),
|
||||
bindingType(bindingType) {}
|
||||
bindingType(bindingType),
|
||||
mBuffer(context, allocator, stagePool, bufferCache, getBufferObjectUsage(bindingType), byteCount) {}
|
||||
|
||||
VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
|
||||
fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "VulkanFboCache.h"
|
||||
#include "VulkanSwapChain.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/VulkanCommands.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
#include "vulkan/utils/StaticVector.h"
|
||||
@@ -200,8 +201,15 @@ public:
|
||||
return &mOffsets;
|
||||
}
|
||||
|
||||
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
|
||||
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
|
||||
template<typename Resource>
|
||||
void acquire(fvkmemory::resource_ptr<Resource> res) {
|
||||
if (res->template isType<VulkanBufferObject>()) {
|
||||
mUboMask.set(mResources.size());
|
||||
}
|
||||
mResources.push_back(res);
|
||||
}
|
||||
|
||||
void referencedBy(VulkanCommandBuffer& commands);
|
||||
|
||||
fvkutils::UniformBufferBitmask const dynamicUboMask;
|
||||
uint8_t const uniqueDynamicUboCount;
|
||||
@@ -214,6 +222,7 @@ private:
|
||||
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
||||
OnRecycle mOnRecycleFn;
|
||||
OnRecycle mOnRecycleExternalSamplerFn;
|
||||
fvkutils::UniformBufferBitmask mUboMask;
|
||||
};
|
||||
|
||||
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
|
||||
@@ -419,6 +428,9 @@ struct VulkanVertexBuffer : public HwVertexBuffer, fvkmemory::Resource {
|
||||
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi);
|
||||
void setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, uint32_t index);
|
||||
|
||||
// TODO: because VulkanBufferObject is backed by VulkanBufferProxy, which could switch out the
|
||||
// backing VkBuffer, we cannot store the VkBuffers for optimization here. We could store a dirty
|
||||
// bit to indicate if a VkBuffer has changed maybe.
|
||||
inline VkBuffer const* getVkBuffers() const { return mBuffers.data(); }
|
||||
inline VkBuffer* getVkBuffers() { return mBuffers.data(); }
|
||||
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi;
|
||||
@@ -433,12 +445,21 @@ struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint8_t elementSize,
|
||||
uint32_t indexCount)
|
||||
: HwIndexBuffer(elementSize, indexCount),
|
||||
buffer(context, allocator, stagePool, bufferCache, VulkanBufferUsage::INDEX,
|
||||
elementSize * indexCount),
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {}
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32),
|
||||
mBuffer(context, allocator, stagePool, bufferCache, VulkanBufferUsage::INDEX,
|
||||
elementSize * indexCount) {}
|
||||
|
||||
VulkanBufferProxy buffer;
|
||||
const VkIndexType indexType;
|
||||
inline void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes) {
|
||||
mBuffer.loadFromCpu(commands, cpuData, byteOffset, numBytes);
|
||||
}
|
||||
|
||||
inline VkBuffer getVkBuffer() const noexcept { return mBuffer.getVkBuffer(); }
|
||||
|
||||
VkIndexType const indexType;
|
||||
|
||||
private:
|
||||
VulkanBufferProxy mBuffer;
|
||||
};
|
||||
|
||||
struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
|
||||
@@ -446,8 +467,21 @@ struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType);
|
||||
|
||||
VulkanBufferProxy buffer;
|
||||
const BufferObjectBinding bindingType;
|
||||
inline void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
|
||||
uint32_t numBytes) {
|
||||
mBuffer.loadFromCpu(commands, cpuData, byteOffset, numBytes);
|
||||
}
|
||||
|
||||
inline VkBuffer getVkBuffer() const noexcept { return mBuffer.getVkBuffer(); }
|
||||
|
||||
inline void referencedBy(VulkanCommandBuffer& commands) {
|
||||
mBuffer.referencedBy(commands);
|
||||
}
|
||||
|
||||
BufferObjectBinding const bindingType;
|
||||
|
||||
private:
|
||||
VulkanBufferProxy mBuffer;
|
||||
};
|
||||
|
||||
struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
|
||||
@@ -459,21 +493,6 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
|
||||
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
|
||||
};
|
||||
|
||||
inline constexpr VulkanBufferUsage getBufferObjectUsage(BufferObjectBinding bindingType) noexcept {
|
||||
switch (bindingType) {
|
||||
case BufferObjectBinding::VERTEX:
|
||||
return VulkanBufferUsage::VERTEX;
|
||||
case BufferObjectBinding::UNIFORM:
|
||||
return VulkanBufferUsage::UNIFORM;
|
||||
case BufferObjectBinding::SHADER_STORAGE:
|
||||
return VulkanBufferUsage::SHADER_STORAGE;
|
||||
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
|
||||
// if necessary.
|
||||
}
|
||||
|
||||
return VulkanBufferUsage::UNKNOWN;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H
|
||||
|
||||
@@ -72,7 +72,10 @@ struct Resource {
|
||||
restype(ResourceType::UNDEFINED_TYPE),
|
||||
mHandleConsideredDestroyed(false) {}
|
||||
|
||||
uint32_t getCount() const { return mCount; }
|
||||
template<typename D>
|
||||
bool isType() const {
|
||||
return getTypeEnum<D>() == restype;
|
||||
}
|
||||
|
||||
private:
|
||||
inline void inc() noexcept {
|
||||
|
||||
Reference in New Issue
Block a user