mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-20 08:52:22 +01:00
[dxvk] Remove old barrier tracking code
This commit is contained in:
parent
708d23ca4b
commit
6301b9d23d
@ -499,303 +499,4 @@ namespace dxvk {
|
||||
flush(list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DxvkBarrierSet:: DxvkBarrierSet(DxvkCmdBuffer cmdBuffer)
|
||||
: m_cmdBuffer(cmdBuffer) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
DxvkBarrierSet::~DxvkBarrierSet() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DxvkBarrierSet::accessMemory(
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess) {
|
||||
DxvkAccessFlags access = this->getAccessTypes(srcAccess);
|
||||
|
||||
m_allBarrierSrcStages |= srcStages;
|
||||
m_memBarrier.srcStageMask |= srcStages & vk::StageDeviceMask;
|
||||
m_memBarrier.srcAccessMask |= srcAccess & vk::AccessWriteMask;
|
||||
m_memBarrier.dstStageMask |= dstStages & vk::StageDeviceMask;
|
||||
|
||||
if (access.test(DxvkAccess::Write)) {
|
||||
m_memBarrier.dstAccessMask |= dstAccess & vk::AccessDeviceMask;
|
||||
|
||||
if (dstAccess & vk::AccessHostMask) {
|
||||
m_hostBarrierSrcStages |= srcStages & vk::StageDeviceMask;
|
||||
m_hostBarrierDstAccess |= dstAccess & vk::AccessHostMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DxvkBarrierSet::accessBuffer(
|
||||
const DxvkBufferSliceHandle& bufSlice,
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess) {
|
||||
DxvkAccessFlags access = this->getAccessTypes(srcAccess);
|
||||
|
||||
m_allBarrierSrcStages |= srcStages;
|
||||
m_memBarrier.srcStageMask |= srcStages & vk::StageDeviceMask;
|
||||
m_memBarrier.srcAccessMask |= srcAccess & vk::AccessWriteMask;
|
||||
m_memBarrier.dstStageMask |= dstStages & vk::StageDeviceMask;
|
||||
|
||||
if (access.test(DxvkAccess::Write)) {
|
||||
m_memBarrier.dstAccessMask |= dstAccess & vk::AccessDeviceMask;
|
||||
|
||||
if (dstAccess & vk::AccessHostMask) {
|
||||
m_hostBarrierSrcStages |= srcStages & vk::StageDeviceMask;
|
||||
m_hostBarrierDstAccess |= dstAccess & vk::AccessHostMask;
|
||||
}
|
||||
}
|
||||
|
||||
m_bufSlices.insert(bufSlice.handle,
|
||||
DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, access));
|
||||
}
|
||||
|
||||
|
||||
void DxvkBarrierSet::accessImage(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& subresources,
|
||||
VkImageLayout srcLayout,
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
VkImageLayout dstLayout,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess) {
|
||||
DxvkAccessFlags access = this->getAccessTypes(srcAccess);
|
||||
|
||||
m_allBarrierSrcStages |= srcStages & vk::StageDeviceMask;
|
||||
|
||||
if (srcLayout == dstLayout) {
|
||||
m_memBarrier.srcStageMask |= srcStages & vk::StageDeviceMask;
|
||||
m_memBarrier.srcAccessMask |= srcAccess & vk::AccessWriteMask;
|
||||
m_memBarrier.dstStageMask |= dstStages & vk::StageDeviceMask;
|
||||
|
||||
if (access.test(DxvkAccess::Write)) {
|
||||
m_memBarrier.dstAccessMask |= dstAccess;
|
||||
|
||||
if (dstAccess & vk::AccessHostMask) {
|
||||
m_hostBarrierSrcStages |= srcStages & vk::StageDeviceMask;
|
||||
m_hostBarrierDstAccess |= dstAccess & vk::AccessHostMask;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
barrier.srcStageMask = srcStages & vk::StageDeviceMask;
|
||||
barrier.srcAccessMask = srcAccess & vk::AccessWriteMask;
|
||||
barrier.dstStageMask = dstStages & vk::StageDeviceMask;
|
||||
barrier.dstAccessMask = dstAccess & vk::AccessDeviceMask;
|
||||
barrier.oldLayout = srcLayout;
|
||||
barrier.newLayout = dstLayout;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = image->handle();
|
||||
barrier.subresourceRange = subresources;
|
||||
barrier.subresourceRange.aspectMask = image->formatInfo()->aspectMask;
|
||||
m_imgBarriers.push_back(barrier);
|
||||
|
||||
if (dstAccess & vk::AccessHostMask) {
|
||||
m_hostBarrierSrcStages |= srcStages;
|
||||
m_hostBarrierDstAccess |= dstAccess & vk::AccessHostMask;
|
||||
}
|
||||
|
||||
access.set(DxvkAccess::Write);
|
||||
}
|
||||
|
||||
m_imgSlices.insert(image->handle(),
|
||||
DxvkBarrierImageSlice(subresources, access));
|
||||
}
|
||||
|
||||
|
||||
void DxvkBarrierSet::releaseImage(
|
||||
DxvkBarrierSet& acquire,
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& subresources,
|
||||
uint32_t srcQueue,
|
||||
VkImageLayout srcLayout,
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
uint32_t dstQueue,
|
||||
VkImageLayout dstLayout,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess) {
|
||||
auto& release = *this;
|
||||
|
||||
m_allBarrierSrcStages |= srcStages;
|
||||
|
||||
VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
barrier.srcStageMask = srcStages & vk::StageDeviceMask;
|
||||
barrier.srcAccessMask = srcAccess & vk::AccessWriteMask;
|
||||
barrier.dstStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT;
|
||||
barrier.dstAccessMask = 0;
|
||||
barrier.oldLayout = srcLayout;
|
||||
barrier.newLayout = dstLayout;
|
||||
barrier.srcQueueFamilyIndex = srcQueue;
|
||||
barrier.dstQueueFamilyIndex = dstQueue;
|
||||
barrier.image = image->handle();
|
||||
barrier.subresourceRange = subresources;
|
||||
barrier.subresourceRange.aspectMask = image->formatInfo()->aspectMask;
|
||||
release.m_imgBarriers.push_back(barrier);
|
||||
|
||||
if (srcQueue == dstQueue)
|
||||
barrier.oldLayout = dstLayout;
|
||||
|
||||
barrier.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT;
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstStageMask = dstStages;
|
||||
barrier.dstAccessMask = dstAccess;
|
||||
acquire.m_imgBarriers.push_back(barrier);
|
||||
|
||||
if (dstAccess & vk::AccessHostMask) {
|
||||
acquire.m_hostBarrierSrcStages |= srcStages & vk::StageDeviceMask;
|
||||
acquire.m_hostBarrierDstAccess |= dstAccess & vk::AccessHostMask;
|
||||
}
|
||||
|
||||
DxvkAccessFlags access(DxvkAccess::Read, DxvkAccess::Write);
|
||||
release.m_imgSlices.insert(image->handle(),
|
||||
DxvkBarrierImageSlice(subresources, access));
|
||||
acquire.m_imgSlices.insert(image->handle(),
|
||||
DxvkBarrierImageSlice(subresources, access));
|
||||
}
|
||||
|
||||
|
||||
bool DxvkBarrierSet::isBufferDirty(
|
||||
const DxvkBufferSliceHandle& bufSlice,
|
||||
DxvkAccessFlags bufAccess) {
|
||||
return m_bufSlices.isDirty(bufSlice.handle,
|
||||
DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, bufAccess));
|
||||
}
|
||||
|
||||
|
||||
bool DxvkBarrierSet::isImageDirty(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& imgSubres,
|
||||
DxvkAccessFlags imgAccess) {
|
||||
return m_imgSlices.isDirty(image->handle(),
|
||||
DxvkBarrierImageSlice(imgSubres, imgAccess));
|
||||
}
|
||||
|
||||
|
||||
DxvkAccessFlags DxvkBarrierSet::getBufferAccess(
|
||||
const DxvkBufferSliceHandle& bufSlice) {
|
||||
return m_bufSlices.getAccess(bufSlice.handle,
|
||||
DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, 0));
|
||||
}
|
||||
|
||||
|
||||
DxvkAccessFlags DxvkBarrierSet::getImageAccess(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& imgSubres) {
|
||||
return m_imgSlices.getAccess(image->handle(),
|
||||
DxvkBarrierImageSlice(imgSubres, 0));
|
||||
}
|
||||
|
||||
|
||||
void DxvkBarrierSet::finalize(const Rc<DxvkCommandList>& commandList) {
|
||||
// Emit host barrier if necessary
|
||||
if (m_hostBarrierSrcStages) {
|
||||
m_memBarrier.srcStageMask |= m_hostBarrierSrcStages;
|
||||
m_memBarrier.srcAccessMask |= VK_ACCESS_MEMORY_WRITE_BIT;
|
||||
m_memBarrier.dstStageMask |= VK_PIPELINE_STAGE_HOST_BIT;
|
||||
m_memBarrier.dstAccessMask |= m_hostBarrierDstAccess;
|
||||
|
||||
m_hostBarrierSrcStages = 0;
|
||||
m_hostBarrierDstAccess = 0;
|
||||
}
|
||||
|
||||
this->recordCommands(commandList);
|
||||
}
|
||||
|
||||
|
||||
void DxvkBarrierSet::recordCommands(const Rc<DxvkCommandList>& commandList) {
|
||||
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
|
||||
if (m_memBarrier.srcStageMask | m_memBarrier.dstStageMask) {
|
||||
depInfo.memoryBarrierCount = 1;
|
||||
depInfo.pMemoryBarriers = &m_memBarrier;
|
||||
}
|
||||
|
||||
if (!m_imgBarriers.empty()) {
|
||||
depInfo.imageMemoryBarrierCount = m_imgBarriers.size();
|
||||
depInfo.pImageMemoryBarriers = m_imgBarriers.data();
|
||||
}
|
||||
|
||||
uint32_t totalBarrierCount = depInfo.memoryBarrierCount
|
||||
+ depInfo.bufferMemoryBarrierCount
|
||||
+ depInfo.imageMemoryBarrierCount;
|
||||
|
||||
if (!totalBarrierCount)
|
||||
return;
|
||||
|
||||
// AMDVLK (and -PRO) will just crash if they encounter a very large structure
|
||||
// in one vkCmdPipelineBarrier2 call, so we need to split the barrier into parts.
|
||||
constexpr uint32_t MaxBarriersPerCall = 512;
|
||||
|
||||
if (unlikely(totalBarrierCount > MaxBarriersPerCall)) {
|
||||
VkDependencyInfo splitDepInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
|
||||
for (uint32_t i = 0; i < depInfo.memoryBarrierCount; i += MaxBarriersPerCall) {
|
||||
splitDepInfo.memoryBarrierCount = std::min(depInfo.memoryBarrierCount - i, MaxBarriersPerCall);
|
||||
splitDepInfo.pMemoryBarriers = depInfo.pMemoryBarriers + i;
|
||||
commandList->cmdPipelineBarrier(m_cmdBuffer, &splitDepInfo);
|
||||
}
|
||||
|
||||
splitDepInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
|
||||
for (uint32_t i = 0; i < depInfo.bufferMemoryBarrierCount; i += MaxBarriersPerCall) {
|
||||
splitDepInfo.bufferMemoryBarrierCount = std::min(depInfo.bufferMemoryBarrierCount - i, MaxBarriersPerCall);
|
||||
splitDepInfo.pBufferMemoryBarriers = depInfo.pBufferMemoryBarriers + i;
|
||||
commandList->cmdPipelineBarrier(m_cmdBuffer, &splitDepInfo);
|
||||
}
|
||||
|
||||
splitDepInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||
|
||||
for (uint32_t i = 0; i < depInfo.imageMemoryBarrierCount; i += MaxBarriersPerCall) {
|
||||
splitDepInfo.imageMemoryBarrierCount = std::min(depInfo.imageMemoryBarrierCount - i, MaxBarriersPerCall);
|
||||
splitDepInfo.pImageMemoryBarriers = depInfo.pImageMemoryBarriers + i;
|
||||
commandList->cmdPipelineBarrier(m_cmdBuffer, &splitDepInfo);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, issue the barrier as-is
|
||||
commandList->cmdPipelineBarrier(m_cmdBuffer, &depInfo);
|
||||
}
|
||||
|
||||
commandList->addStatCtr(DxvkStatCounter::CmdBarrierCount, 1);
|
||||
|
||||
this->reset();
|
||||
}
|
||||
|
||||
|
||||
void DxvkBarrierSet::reset() {
|
||||
m_allBarrierSrcStages = 0;
|
||||
|
||||
m_memBarrier.srcStageMask = 0;
|
||||
m_memBarrier.srcAccessMask = 0;
|
||||
m_memBarrier.dstStageMask = 0;
|
||||
m_memBarrier.dstAccessMask = 0;
|
||||
|
||||
m_imgBarriers.clear();
|
||||
|
||||
m_bufSlices.clear();
|
||||
m_imgSlices.clear();
|
||||
}
|
||||
|
||||
|
||||
DxvkAccessFlags DxvkBarrierSet::getAccessTypes(VkAccessFlags flags) {
|
||||
DxvkAccessFlags result;
|
||||
if (flags & vk::AccessReadMask) result.set(DxvkAccess::Read);
|
||||
if (flags & vk::AccessWriteMask) result.set(DxvkAccess::Write);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -285,605 +285,4 @@ namespace dxvk {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Buffer slice for barrier tracking
|
||||
*
|
||||
* Stores the offset and length of a buffer slice,
|
||||
* as well as access flags for the given range.
|
||||
*/
|
||||
class DxvkBarrierBufferSlice {
|
||||
|
||||
public:
|
||||
|
||||
DxvkBarrierBufferSlice()
|
||||
: m_loAddr(0), m_hiAddr(0), m_access(0) { }
|
||||
|
||||
DxvkBarrierBufferSlice(VkDeviceSize offset, VkDeviceSize length, DxvkAccessFlags access)
|
||||
: m_loAddr(offset),
|
||||
m_hiAddr(offset + length),
|
||||
m_access(access) { }
|
||||
|
||||
/**
|
||||
* \brief Checks whether two slices overlap
|
||||
*
|
||||
* \param [in] slice The other buffer slice to check
|
||||
* \returns \c true if the two slices overlap
|
||||
*/
|
||||
bool overlaps(const DxvkBarrierBufferSlice& slice) const {
|
||||
return m_hiAddr > slice.m_loAddr
|
||||
&& m_loAddr < slice.m_hiAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether a given slice is dirty
|
||||
*
|
||||
* \param [in] slice The buffer slice to check
|
||||
* \returns \c true if the two slices overlap, and if
|
||||
* at least one of the two slices have write access.
|
||||
*/
|
||||
bool isDirty(const DxvkBarrierBufferSlice& slice) const {
|
||||
return (slice.m_access | m_access).test(DxvkAccess::Write) && overlaps(slice);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether two slices can be merged
|
||||
*
|
||||
* Two buffer slices can be merged if they overlap or are adjacent
|
||||
* and if the access flags are the same, or alternatively, if the
|
||||
* offset and size are the same and only the access flags differ.
|
||||
* \param [in] slice The other buffer slice to check
|
||||
* \returns \c true if the slices can be merged.
|
||||
*/
|
||||
bool canMerge(const DxvkBarrierBufferSlice& slice) const {
|
||||
if (m_access == slice.m_access) {
|
||||
return m_hiAddr >= slice.m_loAddr
|
||||
&& m_loAddr <= slice.m_hiAddr;
|
||||
} else {
|
||||
return m_loAddr == slice.m_loAddr
|
||||
&& m_hiAddr == slice.m_hiAddr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Merges two buffer slices
|
||||
*
|
||||
* The resulting slice is guaranteed to fully contain both slices,
|
||||
* including their access flags. If called when \c canMerge would
|
||||
* return \c false, this will be a strict superset of both slices.
|
||||
* \param [in] slice The slice to merge
|
||||
*/
|
||||
void merge(const DxvkBarrierBufferSlice& slice) {
|
||||
m_loAddr = std::min(m_loAddr, slice.m_loAddr);
|
||||
m_hiAddr = std::max(m_hiAddr, slice.m_hiAddr);
|
||||
m_access.set(slice.m_access);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Queries access flags
|
||||
* \returns Access flags
|
||||
*/
|
||||
DxvkAccessFlags getAccess() const {
|
||||
return DxvkAccessFlags(m_access);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
VkDeviceSize m_loAddr;
|
||||
VkDeviceSize m_hiAddr;
|
||||
DxvkAccessFlags m_access;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Image slice for barrier tracking
|
||||
*
|
||||
* Stores an image subresource range, as well as
|
||||
* access flags for the given image subresources.
|
||||
*/
|
||||
class DxvkBarrierImageSlice {
|
||||
|
||||
public:
|
||||
|
||||
DxvkBarrierImageSlice()
|
||||
: m_aspects(0),
|
||||
m_minLayer(0),
|
||||
m_maxLayer(0),
|
||||
m_minLevel(0),
|
||||
m_maxLevel(0),
|
||||
m_access(0) { }
|
||||
|
||||
DxvkBarrierImageSlice(VkImageSubresourceRange range, DxvkAccessFlags access)
|
||||
: m_aspects(range.aspectMask),
|
||||
m_minLayer(range.baseArrayLayer),
|
||||
m_maxLayer(range.baseArrayLayer + range.layerCount),
|
||||
m_minLevel(range.baseMipLevel),
|
||||
m_maxLevel(range.baseMipLevel + range.levelCount),
|
||||
m_access(access) { }
|
||||
|
||||
/**
|
||||
* \brief Checks whether two slices overlap
|
||||
*
|
||||
* \param [in] slice The other image slice to check
|
||||
* \returns \c true if the two slices overlap
|
||||
*/
|
||||
bool overlaps(const DxvkBarrierImageSlice& slice) const {
|
||||
return (m_aspects & slice.m_aspects)
|
||||
&& (m_minLayer < slice.m_maxLayer)
|
||||
&& (m_maxLayer > slice.m_minLayer)
|
||||
&& (m_minLevel < slice.m_maxLevel)
|
||||
&& (m_maxLevel > slice.m_minLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether a given slice is dirty
|
||||
*
|
||||
* \param [in] slice The image slice to check
|
||||
* \returns \c true if the two slices overlap, and if
|
||||
* at least one of the two slices have write access.
|
||||
*/
|
||||
bool isDirty(const DxvkBarrierImageSlice& slice) const {
|
||||
return (slice.m_access | m_access).test(DxvkAccess::Write) && overlaps(slice);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether two slices can be merged
|
||||
*
|
||||
* This is a simplified implementation that does only
|
||||
* checks for adjacent subresources in one dimension.
|
||||
* \param [in] slice The other image slice to check
|
||||
* \returns \c true if the slices can be merged.
|
||||
*/
|
||||
bool canMerge(const DxvkBarrierImageSlice& slice) const {
|
||||
bool sameLayers = m_minLayer == slice.m_minLayer
|
||||
&& m_maxLayer == slice.m_maxLayer;
|
||||
bool sameLevels = m_minLevel == slice.m_minLevel
|
||||
&& m_maxLevel == slice.m_maxLevel;
|
||||
|
||||
if (sameLayers == sameLevels)
|
||||
return sameLayers;
|
||||
|
||||
if (m_access != slice.m_access)
|
||||
return false;
|
||||
|
||||
if (sameLayers) {
|
||||
return m_maxLevel >= slice.m_minLevel
|
||||
&& m_minLevel <= slice.m_maxLevel;
|
||||
} else /* if (sameLevels) */ {
|
||||
return m_maxLayer >= slice.m_minLayer
|
||||
&& m_minLayer <= slice.m_maxLayer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Merges two image slices
|
||||
*
|
||||
* The resulting slice is guaranteed to fully contain both slices,
|
||||
* including their access flags. If called when \c canMerge would
|
||||
* return \c false, this will be a strict superset of both slices.
|
||||
* \param [in] slice The slice to merge
|
||||
*/
|
||||
void merge(const DxvkBarrierImageSlice& slice) {
|
||||
m_aspects |= slice.m_aspects;
|
||||
m_minLayer = std::min(m_minLayer, slice.m_minLayer);
|
||||
m_maxLayer = std::max(m_maxLayer, slice.m_maxLayer);
|
||||
m_minLevel = std::min(m_minLevel, slice.m_minLevel);
|
||||
m_maxLevel = std::max(m_maxLevel, slice.m_maxLevel);
|
||||
m_access.set(slice.m_access);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Queries access flags
|
||||
* \returns Access flags
|
||||
*/
|
||||
DxvkAccessFlags getAccess() const {
|
||||
return m_access;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
VkImageAspectFlags m_aspects;
|
||||
uint32_t m_minLayer;
|
||||
uint32_t m_maxLayer;
|
||||
uint32_t m_minLevel;
|
||||
uint32_t m_maxLevel;
|
||||
DxvkAccessFlags m_access;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Resource slice set for barrier tracking
|
||||
*
|
||||
* Implements a versioned hash table for fast resource
|
||||
* lookup, with a single-linked list accurately storing
|
||||
* each accessed slice if necessary.
|
||||
* \tparam K Resource handle type
|
||||
* \tparam T Resource slice type
|
||||
*/
|
||||
template<typename K, typename T>
|
||||
class DxvkBarrierSubresourceSet {
|
||||
constexpr static uint32_t NoEntry = ~0u;
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Queries access flags of a given resource slice
|
||||
*
|
||||
* \param [in] resource Resource handle
|
||||
* \param [in] slice Resource slice
|
||||
* \returns Or'd access flags of all known slices
|
||||
* that overlap with the given slice.
|
||||
*/
|
||||
DxvkAccessFlags getAccess(K resource, const T& slice) {
|
||||
HashEntry* entry = findHashEntry(resource);
|
||||
|
||||
if (!entry)
|
||||
return DxvkAccessFlags();
|
||||
|
||||
// Exit early if we know that there are no overlapping
|
||||
// slices, or if there is only one slice to check anyway.
|
||||
if (!entry->data.overlaps(slice))
|
||||
return DxvkAccessFlags();
|
||||
|
||||
ListEntry* list = getListEntry(entry->next);
|
||||
|
||||
if (!list)
|
||||
return entry->data.getAccess();
|
||||
|
||||
// The early out condition just checks whether there are
|
||||
// any access flags left that may potentially get added
|
||||
DxvkAccessFlags access;
|
||||
|
||||
while (list && access != entry->data.getAccess()) {
|
||||
if (list->data.overlaps(slice))
|
||||
access.set(list->data.getAccess());
|
||||
|
||||
list = getListEntry(list->next);
|
||||
}
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether a given resource slice is dirty
|
||||
*
|
||||
* \param [in] resource Resourece handle
|
||||
* \param [in] slice Resource slice
|
||||
* \returns \c true if there is at least one slice that
|
||||
* overlaps with the given slice, and either slice has
|
||||
* the \c DxvkAccess::Write flag set.
|
||||
*/
|
||||
bool isDirty(K resource, const T& slice) {
|
||||
HashEntry* entry = findHashEntry(resource);
|
||||
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
// Exit early if there are no overlapping slices, or
|
||||
// if none of the slices have the write flag set.
|
||||
if (!entry->data.isDirty(slice))
|
||||
return false;
|
||||
|
||||
// We know that some subresources are dirty, so if
|
||||
// there is no list, the given slice must be dirty.
|
||||
ListEntry* list = getListEntry(entry->next);
|
||||
|
||||
if (!list)
|
||||
return true;
|
||||
|
||||
// Exit earlier if we find one dirty slice
|
||||
bool dirty = false;
|
||||
|
||||
while (list && !dirty) {
|
||||
dirty = list->data.isDirty(slice);
|
||||
list = getListEntry(list->next);
|
||||
}
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Inserts a given resource slice
|
||||
*
|
||||
* This will attempt to deduplicate and merge entries if
|
||||
* possible, so that lookup and further insertions remain
|
||||
* reasonably fast.
|
||||
* \param [in] resource Resource handle
|
||||
* \param [in] slice Resource slice
|
||||
*/
|
||||
void insert(K resource, const T& slice) {
|
||||
HashEntry* hashEntry = insertHashEntry(resource, slice);
|
||||
|
||||
if (hashEntry) {
|
||||
ListEntry* listEntry = getListEntry(hashEntry->next);
|
||||
|
||||
if (listEntry) {
|
||||
if (std::is_same_v<T, DxvkBarrierImageSlice>) {
|
||||
// For images, try to merge the slice with existing
|
||||
// entries if possible to keep the list small
|
||||
do {
|
||||
if (listEntry->data.canMerge(slice)) {
|
||||
listEntry->data.merge(slice);
|
||||
break;
|
||||
}
|
||||
} while ((listEntry = getListEntry(listEntry->next)));
|
||||
|
||||
if (!listEntry)
|
||||
insertListEntry(slice, hashEntry);
|
||||
} else {
|
||||
// For buffers it's not even worth trying. Most of the
|
||||
// time we won't be able to merge, and traversing the
|
||||
// entire list every time is slow.
|
||||
insertListEntry(slice, hashEntry);
|
||||
}
|
||||
} else if (!hashEntry->data.canMerge(slice)) {
|
||||
// Only create the linear list if absolutely necessary
|
||||
insertListEntry(hashEntry->data, hashEntry);
|
||||
insertListEntry(slice, hashEntry);
|
||||
}
|
||||
|
||||
// Merge hash entry data so that it stores
|
||||
// a superset of all slices in the list.
|
||||
hashEntry->data.merge(slice);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Removes all resources from the set
|
||||
*/
|
||||
void clear() {
|
||||
m_used = 0;
|
||||
m_version += 1;
|
||||
m_list.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether set is empty
|
||||
* \returns \c true if there are no entries
|
||||
*/
|
||||
bool empty() const {
|
||||
return m_used == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct ListEntry {
|
||||
T data;
|
||||
uint32_t next;
|
||||
};
|
||||
|
||||
struct HashEntry {
|
||||
uint64_t version;
|
||||
K key;
|
||||
T data;
|
||||
uint32_t next;
|
||||
};
|
||||
|
||||
uint64_t m_version = 1ull;
|
||||
uint64_t m_used = 0ull;
|
||||
size_t m_indexMask = 0;
|
||||
|
||||
std::vector<ListEntry> m_list;
|
||||
std::vector<HashEntry> m_hashMap;
|
||||
|
||||
static size_t computeHash(K key) {
|
||||
size_t hash = size_t(key) * 93887;
|
||||
return hash ^ (hash >> 16);
|
||||
}
|
||||
|
||||
size_t computeSize() const {
|
||||
return m_indexMask
|
||||
? m_indexMask + 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
size_t computeIndex(K key) const {
|
||||
return computeHash(key) & (m_indexMask);
|
||||
}
|
||||
|
||||
size_t advanceIndex(size_t index) const {
|
||||
return (index + 1) & m_indexMask;
|
||||
}
|
||||
|
||||
HashEntry* findHashEntry(K key) {
|
||||
if (!m_used)
|
||||
return nullptr;
|
||||
|
||||
size_t index = computeIndex(key);
|
||||
|
||||
while (m_hashMap[index].version == m_version) {
|
||||
if (m_hashMap[index].key == key)
|
||||
return &m_hashMap[index];
|
||||
|
||||
index = advanceIndex(index);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HashEntry* insertHashEntry(K key, const T& data) {
|
||||
growHashMapBeforeInsert();
|
||||
|
||||
// If we already have an entry for the given key, return
|
||||
// the old one and let the caller deal with it
|
||||
size_t index = computeIndex(key);
|
||||
|
||||
while (m_hashMap[index].version == m_version) {
|
||||
if (m_hashMap[index].key == key)
|
||||
return &m_hashMap[index];
|
||||
|
||||
index = advanceIndex(index);
|
||||
}
|
||||
|
||||
HashEntry* entry = &m_hashMap[index];
|
||||
entry->version = m_version;
|
||||
entry->key = key;
|
||||
entry->data = data;
|
||||
entry->next = NoEntry;
|
||||
|
||||
m_used += 1;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void growHashMap(size_t newSize) {
|
||||
size_t oldSize = computeSize();
|
||||
m_hashMap.resize(newSize);
|
||||
|
||||
// Relocate hash entries in place
|
||||
for (size_t i = 0; i < oldSize; i++) {
|
||||
HashEntry entry = m_hashMap[i];
|
||||
m_hashMap[i].version = 0;
|
||||
|
||||
while (entry.version == m_version) {
|
||||
size_t index = computeIndex(entry.key);
|
||||
entry.version = m_version + 1;
|
||||
|
||||
while (m_hashMap[index].version > m_version)
|
||||
index = advanceIndex(index);
|
||||
|
||||
std::swap(entry, m_hashMap[index]);
|
||||
}
|
||||
}
|
||||
|
||||
m_version += 1;
|
||||
m_indexMask = newSize - 1;
|
||||
}
|
||||
|
||||
void growHashMapBeforeInsert() {
|
||||
// Allow a load factor of 0.7 for performance reasons
|
||||
size_t oldSize = computeSize();
|
||||
|
||||
if (10 * m_used >= 7 * oldSize) {
|
||||
size_t newSize = oldSize ? oldSize * 2 : 64;
|
||||
growHashMap(newSize);
|
||||
}
|
||||
}
|
||||
|
||||
ListEntry* getListEntry(uint32_t index) {
|
||||
return index < NoEntry ? &m_list[index] : nullptr;
|
||||
}
|
||||
|
||||
ListEntry* insertListEntry(const T& subresource, HashEntry* head) {
|
||||
uint32_t newIndex = uint32_t(m_list.size());
|
||||
m_list.push_back({ subresource, head->next });
|
||||
head->next = newIndex;
|
||||
return &m_list[newIndex];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Barrier set
|
||||
*
|
||||
* Accumulates memory barriers and provides a
|
||||
* method to record all those barriers into a
|
||||
* command buffer at once.
|
||||
*/
|
||||
class DxvkBarrierSet {
|
||||
|
||||
public:
|
||||
|
||||
DxvkBarrierSet(DxvkCmdBuffer cmdBuffer);
|
||||
~DxvkBarrierSet();
|
||||
|
||||
void accessMemory(
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess);
|
||||
|
||||
void accessBuffer(
|
||||
const DxvkBufferSliceHandle& bufSlice,
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess);
|
||||
|
||||
void accessImage(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& subresources,
|
||||
VkImageLayout srcLayout,
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
VkImageLayout dstLayout,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess);
|
||||
|
||||
void releaseImage(
|
||||
DxvkBarrierSet& acquire,
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& subresources,
|
||||
uint32_t srcQueue,
|
||||
VkImageLayout srcLayout,
|
||||
VkPipelineStageFlags srcStages,
|
||||
VkAccessFlags srcAccess,
|
||||
uint32_t dstQueue,
|
||||
VkImageLayout dstLayout,
|
||||
VkPipelineStageFlags dstStages,
|
||||
VkAccessFlags dstAccess);
|
||||
|
||||
bool isBufferDirty(
|
||||
const DxvkBufferSliceHandle& bufSlice,
|
||||
DxvkAccessFlags bufAccess);
|
||||
|
||||
bool isImageDirty(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& imgSubres,
|
||||
DxvkAccessFlags imgAccess);
|
||||
|
||||
DxvkAccessFlags getBufferAccess(
|
||||
const DxvkBufferSliceHandle& bufSlice);
|
||||
|
||||
DxvkAccessFlags getImageAccess(
|
||||
const Rc<DxvkImage>& image,
|
||||
const VkImageSubresourceRange& imgSubres);
|
||||
|
||||
VkPipelineStageFlags getSrcStages() {
|
||||
return m_allBarrierSrcStages;
|
||||
}
|
||||
|
||||
void finalize(
|
||||
const Rc<DxvkCommandList>& commandList);
|
||||
|
||||
void recordCommands(
|
||||
const Rc<DxvkCommandList>& commandList);
|
||||
|
||||
void reset();
|
||||
|
||||
bool hasResourceBarriers() const {
|
||||
return !m_bufSlices.empty()
|
||||
|| !m_imgSlices.empty();
|
||||
}
|
||||
|
||||
static DxvkAccessFlags getAccessTypes(VkAccessFlags flags);
|
||||
|
||||
private:
|
||||
|
||||
struct BufSlice {
|
||||
DxvkBufferSliceHandle slice;
|
||||
DxvkAccessFlags access;
|
||||
};
|
||||
|
||||
struct ImgSlice {
|
||||
VkImage image;
|
||||
VkImageSubresourceRange subres;
|
||||
DxvkAccessFlags access;
|
||||
};
|
||||
|
||||
DxvkCmdBuffer m_cmdBuffer;
|
||||
|
||||
VkPipelineStageFlags2 m_hostBarrierSrcStages = 0;
|
||||
VkAccessFlags2 m_hostBarrierDstAccess = 0;
|
||||
|
||||
VkPipelineStageFlags2 m_allBarrierSrcStages = 0;
|
||||
|
||||
VkMemoryBarrier2 m_memBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
|
||||
std::vector<VkImageMemoryBarrier2> m_imgBarriers;
|
||||
|
||||
DxvkBarrierSubresourceSet<VkBuffer, DxvkBarrierBufferSlice> m_bufSlices;
|
||||
DxvkBarrierSubresourceSet<VkImage, DxvkBarrierImageSlice> m_imgSlices;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -392,7 +392,7 @@ namespace dxvk {
|
||||
// When overwriting small buffers, we can allocate a new slice in order to
|
||||
// avoid suspending the current render pass or inserting barriers. The source
|
||||
// buffer must be read-only since otherwise we cannot schedule the copy early.
|
||||
bool srcIsReadOnly = DxvkBarrierSet::getAccessTypes(srcBuffer->info().access) == DxvkAccess::Read;
|
||||
bool srcIsReadOnly = !(srcBuffer->info().access & vk::AccessWriteMask);
|
||||
bool replaceBuffer = srcIsReadOnly && this->tryInvalidateDeviceLocalBuffer(dstBuffer, numBytes);
|
||||
|
||||
auto srcSlice = srcBuffer->getSliceHandle(srcOffset, numBytes);
|
||||
@ -4979,8 +4979,7 @@ namespace dxvk {
|
||||
srcBarrier.access |= pipelineBarrier.access;
|
||||
|
||||
if (srcBarrier.stages) {
|
||||
DxvkAccessFlags access = DxvkBarrierSet::getAccessTypes(srcBarrier.access);
|
||||
DxvkGlobalPipelineBarrier dstBarrier = access.test(DxvkAccess::Write)
|
||||
DxvkGlobalPipelineBarrier dstBarrier = (srcBarrier.access & vk::AccessWriteMask)
|
||||
? m_globalRwGraphicsBarrier
|
||||
: m_globalRoGraphicsBarrier;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user