mirror of https://github.com/doitsujin/dxvk
727 lines
24 KiB
C++
727 lines
24 KiB
C++
#include <sstream>
|
|
|
|
#include "dxvk_buffer.h"
|
|
#include "dxvk_device.h"
|
|
#include "dxvk_image.h"
|
|
#include "dxvk_sparse.h"
|
|
|
|
namespace dxvk {
|
|
|
|
DxvkSparseMapping::DxvkSparseMapping()
|
|
: m_pool(nullptr),
|
|
m_page(nullptr) {
|
|
|
|
}
|
|
|
|
|
|
DxvkSparseMapping::DxvkSparseMapping(
|
|
Rc<DxvkSparsePageAllocator> allocator,
|
|
Rc<DxvkSparsePage> page)
|
|
: m_pool(std::move(allocator)),
|
|
m_page(std::move(page)) {
|
|
|
|
}
|
|
|
|
|
|
DxvkSparseMapping::DxvkSparseMapping(
|
|
DxvkSparseMapping&& other)
|
|
: m_pool(std::move(other.m_pool)),
|
|
m_page(std::move(other.m_page)) {
|
|
// No need to acquire here. The only place from which
|
|
// this constructor can be called does this atomically.
|
|
}
|
|
|
|
|
|
DxvkSparseMapping::DxvkSparseMapping(
|
|
const DxvkSparseMapping& other)
|
|
: m_pool(other.m_pool),
|
|
m_page(other.m_page) {
|
|
this->acquire();
|
|
}
|
|
|
|
|
|
DxvkSparseMapping& DxvkSparseMapping::operator = (
|
|
DxvkSparseMapping&& other) {
|
|
this->release();
|
|
|
|
m_pool = std::move(other.m_pool);
|
|
m_page = std::move(other.m_page);
|
|
return *this;
|
|
}
|
|
|
|
|
|
DxvkSparseMapping& DxvkSparseMapping::operator = (
|
|
const DxvkSparseMapping& other) {
|
|
other.acquire();
|
|
this->release();
|
|
|
|
m_pool = other.m_pool;
|
|
m_page = other.m_page;
|
|
return *this;
|
|
}
|
|
|
|
|
|
DxvkSparseMapping::~DxvkSparseMapping() {
|
|
this->release();
|
|
}
|
|
|
|
|
|
void DxvkSparseMapping::acquire() const {
|
|
if (m_page != nullptr)
|
|
m_pool->acquirePage(m_page);
|
|
}
|
|
|
|
|
|
void DxvkSparseMapping::release() const {
|
|
if (m_page != nullptr)
|
|
m_pool->releasePage(m_page);
|
|
}
|
|
|
|
|
|
DxvkSparsePageAllocator::DxvkSparsePageAllocator(
|
|
DxvkMemoryAllocator& memoryAllocator)
|
|
: m_memory(&memoryAllocator) {
|
|
|
|
}
|
|
|
|
|
|
DxvkSparsePageAllocator::~DxvkSparsePageAllocator() {
|
|
|
|
}
|
|
|
|
|
|
DxvkSparseMapping DxvkSparsePageAllocator::acquirePage(
|
|
uint32_t page) {
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
if (unlikely(page >= m_pageCount))
|
|
return DxvkSparseMapping();
|
|
|
|
m_useCount += 1;
|
|
return DxvkSparseMapping(this, m_pages[page]);
|
|
}
|
|
|
|
|
|
void DxvkSparsePageAllocator::setCapacity(
|
|
uint32_t pageCount) {
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
if (pageCount < m_pageCount) {
|
|
if (!m_useCount)
|
|
m_pages.resize(pageCount);
|
|
} else if (pageCount > m_pageCount) {
|
|
while (m_pages.size() < pageCount)
|
|
m_pages.push_back(allocPage());
|
|
}
|
|
|
|
m_pageCount = pageCount;
|
|
}
|
|
|
|
|
|
Rc<DxvkSparsePage> DxvkSparsePageAllocator::allocPage() {
|
|
DxvkMemoryRequirements memoryRequirements = { };
|
|
memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 };
|
|
|
|
// We don't know what kind of resource the memory6
|
|
// might be bound to, so just guess the memory types
|
|
auto& core = memoryRequirements.core.memoryRequirements;
|
|
core.size = SparseMemoryPageSize;
|
|
core.alignment = SparseMemoryPageSize;
|
|
core.memoryTypeBits = m_memory->getSparseMemoryTypes();
|
|
|
|
DxvkMemoryProperties memoryProperties = { };
|
|
memoryProperties.flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
|
|
DxvkMemory memory = m_memory->alloc(memoryRequirements,
|
|
memoryProperties, DxvkMemoryFlag::GpuReadable);
|
|
|
|
return new DxvkSparsePage(std::move(memory));
|
|
}
|
|
|
|
|
|
void DxvkSparsePageAllocator::acquirePage(
|
|
const Rc<DxvkSparsePage>& page) {
|
|
std::lock_guard lock(m_mutex);
|
|
m_useCount += 1;
|
|
}
|
|
|
|
|
|
void DxvkSparsePageAllocator::releasePage(
|
|
const Rc<DxvkSparsePage>& page) {
|
|
std::lock_guard lock(m_mutex);
|
|
m_useCount -= 1;
|
|
|
|
if (!m_useCount)
|
|
m_pages.resize(m_pageCount);
|
|
}
|
|
|
|
|
|
DxvkSparsePageTable::DxvkSparsePageTable() {
|
|
|
|
}
|
|
|
|
|
|
DxvkSparsePageTable::DxvkSparsePageTable(
|
|
DxvkDevice* device,
|
|
const DxvkBuffer* buffer)
|
|
: m_buffer(buffer) {
|
|
VkDeviceSize bufferSize = buffer->info().size;
|
|
|
|
// For linear buffers, the mapping is very simple
|
|
// and consists of consecutive 64k pages
|
|
size_t pageCount = align(bufferSize, SparseMemoryPageSize) / SparseMemoryPageSize;
|
|
m_metadata.resize(pageCount);
|
|
m_mappings.resize(pageCount);
|
|
|
|
for (size_t i = 0; i < pageCount; i++) {
|
|
VkDeviceSize pageOffset = SparseMemoryPageSize * i;
|
|
m_metadata[i].type = DxvkSparsePageType::Buffer;
|
|
m_metadata[i].buffer.offset = pageOffset;
|
|
m_metadata[i].buffer.length = std::min(SparseMemoryPageSize, bufferSize - pageOffset);
|
|
}
|
|
|
|
// Initialize properties and subresource info so that we can
|
|
// easily query this without having to know the resource type
|
|
m_subresources.resize(1);
|
|
m_subresources[0].pageCount = { uint32_t(pageCount), 1u, 1u };
|
|
m_subresources[0].pageIndex = 0;
|
|
|
|
m_properties.pageRegionExtent = { uint32_t(SparseMemoryPageSize), 1u, 1u };
|
|
}
|
|
|
|
|
|
DxvkSparsePageTable::DxvkSparsePageTable(
|
|
DxvkDevice* device,
|
|
const DxvkImage* image)
|
|
: m_image(image) {
|
|
auto vk = device->vkd();
|
|
|
|
// Query sparse memory requirements
|
|
uint32_t reqCount = 0;
|
|
vk->vkGetImageSparseMemoryRequirements(vk->device(), image->handle(), &reqCount, nullptr);
|
|
|
|
std::vector<VkSparseImageMemoryRequirements> req(reqCount);
|
|
vk->vkGetImageSparseMemoryRequirements(vk->device(), image->handle(), &reqCount, req.data());
|
|
|
|
// Find first non-metadata struct and use it to fill in the image properties
|
|
bool foundMainAspect = false;
|
|
|
|
for (const auto& r : req) {
|
|
if (r.formatProperties.aspectMask & VK_IMAGE_ASPECT_METADATA_BIT) {
|
|
VkDeviceSize metadataSize = r.imageMipTailSize;
|
|
|
|
if (!(r.formatProperties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))
|
|
metadataSize *= m_image->info().numLayers;
|
|
|
|
m_properties.metadataPageCount += uint32_t(metadataSize / SparseMemoryPageSize);
|
|
} else if (!foundMainAspect) {
|
|
m_properties.flags = r.formatProperties.flags;
|
|
m_properties.pageRegionExtent = r.formatProperties.imageGranularity;
|
|
|
|
if (r.imageMipTailFirstLod < image->info().mipLevels && r.imageMipTailSize) {
|
|
m_properties.pagedMipCount = r.imageMipTailFirstLod;
|
|
m_properties.mipTailOffset = r.imageMipTailOffset;
|
|
m_properties.mipTailSize = r.imageMipTailSize;
|
|
m_properties.mipTailStride = r.imageMipTailStride;
|
|
} else {
|
|
m_properties.pagedMipCount = image->info().mipLevels;
|
|
}
|
|
|
|
foundMainAspect = true;
|
|
} else {
|
|
Logger::err(str::format("Found multiple aspects for sparse image:"
|
|
"\n Type: ", image->info().type,
|
|
"\n Format: ", image->info().format,
|
|
"\n Flags: ", image->info().flags,
|
|
"\n Extent: ", "(", image->info().extent.width,
|
|
",", image->info().extent.height,
|
|
",", image->info().extent.depth, ")",
|
|
"\n Mip levels: ", image->info().mipLevels,
|
|
"\n Array layers: ", image->info().numLayers,
|
|
"\n Samples: ", image->info().sampleCount,
|
|
"\n Usage: ", image->info().usage,
|
|
"\n Tiling: ", image->info().tiling));
|
|
}
|
|
}
|
|
|
|
// Fill in subresource metadata and compute page count
|
|
uint32_t totalPageCount = 0;
|
|
uint32_t subresourceCount = image->info().numLayers * image->info().mipLevels;
|
|
m_subresources.reserve(subresourceCount);
|
|
|
|
for (uint32_t l = 0; l < image->info().numLayers; l++) {
|
|
for (uint32_t m = 0; m < image->info().mipLevels; m++) {
|
|
if (m < m_properties.pagedMipCount) {
|
|
// Compute block count for current mip based on image properties
|
|
DxvkSparseImageSubresourceProperties subresourceInfo;
|
|
subresourceInfo.isMipTail = VK_FALSE;
|
|
subresourceInfo.pageCount = util::computeBlockCount(
|
|
image->mipLevelExtent(m), m_properties.pageRegionExtent);
|
|
|
|
// Advance total page count by number of pages in the subresource
|
|
subresourceInfo.pageIndex = totalPageCount;
|
|
totalPageCount += util::flattenImageExtent(subresourceInfo.pageCount);
|
|
|
|
m_subresources.push_back(subresourceInfo);
|
|
} else {
|
|
DxvkSparseImageSubresourceProperties subresourceInfo = { };
|
|
subresourceInfo.isMipTail = VK_TRUE;
|
|
subresourceInfo.pageCount = { 0u, 0u, 0u };
|
|
subresourceInfo.pageIndex = 0u;
|
|
m_subresources.push_back(subresourceInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_properties.mipTailSize) {
|
|
m_properties.mipTailPageIndex = totalPageCount;
|
|
|
|
// We may need multiple mip tails for the image
|
|
uint32_t mipTailPageCount = m_properties.mipTailSize / SparseMemoryPageSize;
|
|
|
|
if (!(m_properties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))
|
|
mipTailPageCount *= m_image->info().numLayers;
|
|
|
|
totalPageCount += mipTailPageCount;
|
|
}
|
|
|
|
// Fill in page metadata
|
|
m_metadata.reserve(totalPageCount);
|
|
m_mappings.resize(totalPageCount);
|
|
|
|
for (uint32_t l = 0; l < image->info().numLayers; l++) {
|
|
for (uint32_t m = 0; m < m_properties.pagedMipCount; m++) {
|
|
VkExtent3D mipExtent = image->mipLevelExtent(m);
|
|
VkExtent3D pageCount = util::computeBlockCount(mipExtent, m_properties.pageRegionExtent);
|
|
|
|
for (uint32_t z = 0; z < pageCount.depth; z++) {
|
|
for (uint32_t y = 0; y < pageCount.height; y++) {
|
|
for (uint32_t x = 0; x < pageCount.width; x++) {
|
|
DxvkSparsePageInfo pageInfo;
|
|
pageInfo.type = DxvkSparsePageType::Image;
|
|
pageInfo.image.subresource.aspectMask = image->formatInfo()->aspectMask;
|
|
pageInfo.image.subresource.mipLevel = m;
|
|
pageInfo.image.subresource.arrayLayer = l;
|
|
pageInfo.image.offset.x = x * m_properties.pageRegionExtent.width;
|
|
pageInfo.image.offset.y = y * m_properties.pageRegionExtent.height;
|
|
pageInfo.image.offset.z = z * m_properties.pageRegionExtent.depth;
|
|
pageInfo.image.extent.width = std::min(m_properties.pageRegionExtent.width, mipExtent.width - pageInfo.image.offset.x);
|
|
pageInfo.image.extent.height = std::min(m_properties.pageRegionExtent.height, mipExtent.height - pageInfo.image.offset.y);
|
|
pageInfo.image.extent.depth = std::min(m_properties.pageRegionExtent.depth, mipExtent.depth - pageInfo.image.offset.z);
|
|
m_metadata.push_back(pageInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_properties.mipTailSize) {
|
|
uint32_t pageCount = m_properties.mipTailSize / SparseMemoryPageSize;
|
|
uint32_t layerCount = 1;
|
|
|
|
if (!(m_properties.flags & VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT))
|
|
layerCount = image->info().numLayers;
|
|
|
|
for (uint32_t i = 0; i < layerCount; i++) {
|
|
for (uint32_t j = 0; j < pageCount; j++) {
|
|
DxvkSparsePageInfo pageInfo;
|
|
pageInfo.type = DxvkSparsePageType::ImageMipTail;
|
|
pageInfo.mipTail.resourceOffset = m_properties.mipTailOffset
|
|
+ i * m_properties.mipTailStride
|
|
+ j * SparseMemoryPageSize;
|
|
pageInfo.mipTail.resourceLength = SparseMemoryPageSize;
|
|
m_metadata.push_back(pageInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VkBuffer DxvkSparsePageTable::getBufferHandle() const {
|
|
return m_buffer ? m_buffer->getSliceHandle().handle : VK_NULL_HANDLE;
|
|
}
|
|
|
|
|
|
VkImage DxvkSparsePageTable::getImageHandle() const {
|
|
return m_image ? m_image->handle() : VK_NULL_HANDLE;
|
|
}
|
|
|
|
|
|
uint32_t DxvkSparsePageTable::computePageIndex(
|
|
uint32_t subresource,
|
|
VkOffset3D regionOffset,
|
|
VkExtent3D regionExtent,
|
|
VkBool32 regionIsLinear,
|
|
uint32_t pageIndex) const {
|
|
auto subresourceInfo = getSubresourceProperties(subresource);
|
|
|
|
// The mip tail is always linear
|
|
if (subresourceInfo.isMipTail)
|
|
return m_properties.mipTailPageIndex + pageIndex;
|
|
|
|
// Compute offset into the given subresource
|
|
VkOffset3D pageOffset = regionOffset;
|
|
|
|
if (!regionIsLinear) {
|
|
pageOffset.x += (pageIndex % regionExtent.width);
|
|
pageOffset.y += (pageIndex / regionExtent.width) % regionExtent.height;
|
|
pageOffset.z += (pageIndex / regionExtent.width) / regionExtent.height;
|
|
pageIndex = 0;
|
|
}
|
|
|
|
uint32_t result = subresourceInfo.pageIndex + pageOffset.x
|
|
+ subresourceInfo.pageCount.width * (pageOffset.y
|
|
+ subresourceInfo.pageCount.height * pageOffset.z);
|
|
|
|
return result + pageIndex;
|
|
}
|
|
|
|
|
|
DxvkSparseMapping DxvkSparsePageTable::getMapping(
|
|
uint32_t page) {
|
|
return page < m_mappings.size()
|
|
? m_mappings[page]
|
|
: DxvkSparseMapping();
|
|
}
|
|
|
|
|
|
void DxvkSparsePageTable::updateMapping(
|
|
DxvkCommandList* cmd,
|
|
uint32_t page,
|
|
DxvkSparseMapping&& mapping) {
|
|
if (m_mappings[page] != page) {
|
|
if (m_mappings[page])
|
|
cmd->trackResource<DxvkAccess::None>(m_mappings[page].m_page);
|
|
|
|
m_mappings[page] = std::move(mapping);
|
|
}
|
|
}
|
|
|
|
|
|
DxvkSparseBindSubmission::DxvkSparseBindSubmission() {
|
|
|
|
}
|
|
|
|
|
|
DxvkSparseBindSubmission::~DxvkSparseBindSubmission() {
|
|
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::waitSemaphore(
|
|
VkSemaphore semaphore,
|
|
uint64_t value) {
|
|
m_waitSemaphores.push_back(semaphore);
|
|
m_waitSemaphoreValues.push_back(value);
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::signalSemaphore(
|
|
VkSemaphore semaphore,
|
|
uint64_t value) {
|
|
m_signalSemaphores.push_back(semaphore);
|
|
m_signalSemaphoreValues.push_back(value);
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::bindBufferMemory(
|
|
const DxvkSparseBufferBindKey& key,
|
|
const DxvkSparsePageHandle& memory) {
|
|
m_bufferBinds.insert_or_assign(key, memory);
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::bindImageMemory(
|
|
const DxvkSparseImageBindKey& key,
|
|
const DxvkSparsePageHandle& memory) {
|
|
m_imageBinds.insert_or_assign(key, memory);
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::bindImageOpaqueMemory(
|
|
const DxvkSparseImageOpaqueBindKey& key,
|
|
const DxvkSparsePageHandle& memory) {
|
|
m_imageOpaqueBinds.insert_or_assign(key, memory);
|
|
}
|
|
|
|
|
|
VkResult DxvkSparseBindSubmission::submit(
|
|
DxvkDevice* device,
|
|
VkQueue queue) {
|
|
auto vk = device->vkd();
|
|
|
|
DxvkSparseBufferBindArrays buffer;
|
|
this->processBufferBinds(buffer);
|
|
|
|
DxvkSparseImageBindArrays image;
|
|
this->processImageBinds(image);
|
|
|
|
DxvkSparseImageOpaqueBindArrays opaque;
|
|
this->processOpaqueBinds(opaque);
|
|
|
|
// The sparse binding API has never been updated to take the new
|
|
// semaphore submission info structs, so we have to do this instead
|
|
VkTimelineSemaphoreSubmitInfo timelineInfo = { VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO };
|
|
timelineInfo.waitSemaphoreValueCount = m_waitSemaphoreValues.size();
|
|
timelineInfo.pWaitSemaphoreValues = m_waitSemaphoreValues.data();
|
|
timelineInfo.signalSemaphoreValueCount = m_signalSemaphoreValues.size();
|
|
timelineInfo.pSignalSemaphoreValues = m_signalSemaphoreValues.data();
|
|
|
|
VkBindSparseInfo bindInfo = { VK_STRUCTURE_TYPE_BIND_SPARSE_INFO };
|
|
|
|
if (!m_waitSemaphores.empty()) {
|
|
bindInfo.pNext = &timelineInfo;
|
|
bindInfo.waitSemaphoreCount = m_waitSemaphores.size();
|
|
bindInfo.pWaitSemaphores = m_waitSemaphores.data();
|
|
}
|
|
|
|
if (!buffer.infos.empty()) {
|
|
bindInfo.bufferBindCount = buffer.infos.size();
|
|
bindInfo.pBufferBinds = buffer.infos.data();
|
|
}
|
|
|
|
if (!opaque.infos.empty()) {
|
|
bindInfo.imageOpaqueBindCount = opaque.infos.size();
|
|
bindInfo.pImageOpaqueBinds = opaque.infos.data();
|
|
}
|
|
|
|
if (!image.infos.empty()) {
|
|
bindInfo.imageBindCount = image.infos.size();
|
|
bindInfo.pImageBinds = image.infos.data();
|
|
}
|
|
|
|
if (!m_signalSemaphores.empty()) {
|
|
bindInfo.pNext = &timelineInfo;
|
|
bindInfo.signalSemaphoreCount = m_signalSemaphores.size();
|
|
bindInfo.pSignalSemaphores = m_signalSemaphores.data();
|
|
}
|
|
|
|
VkResult vr = vk->vkQueueBindSparse(queue, 1, &bindInfo, VK_NULL_HANDLE);
|
|
|
|
if (vr) {
|
|
Logger::err(str::format("Sparse binding failed: ", vr));
|
|
this->logSparseBindingInfo(LogLevel::Error, &bindInfo);
|
|
}
|
|
|
|
this->reset();
|
|
return vr;
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::reset() {
|
|
m_waitSemaphoreValues.clear();
|
|
m_waitSemaphores.clear();
|
|
m_signalSemaphoreValues.clear();
|
|
m_signalSemaphores.clear();
|
|
|
|
m_bufferBinds.clear();
|
|
m_imageBinds.clear();
|
|
m_imageOpaqueBinds.clear();
|
|
}
|
|
|
|
|
|
bool DxvkSparseBindSubmission::tryMergeMemoryBind(
|
|
VkSparseMemoryBind& oldBind,
|
|
const VkSparseMemoryBind& newBind) {
|
|
if (newBind.memory != oldBind.memory || newBind.flags != oldBind.flags)
|
|
return false;
|
|
|
|
// The resource region must be consistent
|
|
if (newBind.resourceOffset != oldBind.resourceOffset + oldBind.size)
|
|
return false;
|
|
|
|
// If memory is not null, the memory range must also be consistent
|
|
if (newBind.memory && newBind.memoryOffset != oldBind.memoryOffset + oldBind.size)
|
|
return false;
|
|
|
|
oldBind.size += newBind.size;
|
|
return true;
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::processBufferBinds(
|
|
DxvkSparseBufferBindArrays& buffer) {
|
|
std::vector<std::pair<VkBuffer, VkSparseMemoryBind>> ranges;
|
|
ranges.reserve(m_bufferBinds.size());
|
|
|
|
for (const auto& e : m_bufferBinds) {
|
|
const auto& key = e.first;
|
|
const auto& handle = e.second;
|
|
|
|
VkSparseMemoryBind bind = { };
|
|
bind.resourceOffset = key.offset;
|
|
bind.size = key.size;
|
|
bind.memory = handle.memory;
|
|
bind.memoryOffset = handle.offset;
|
|
|
|
bool merged = false;
|
|
|
|
if (!ranges.empty() && ranges.back().first == key.buffer)
|
|
merged = tryMergeMemoryBind(ranges.back().second, bind);
|
|
|
|
if (!merged)
|
|
ranges.push_back({ key.buffer, bind });
|
|
}
|
|
|
|
populateOutputArrays(buffer.binds, buffer.infos, ranges);
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::processImageBinds(
|
|
DxvkSparseImageBindArrays& image) {
|
|
std::vector<std::pair<VkImage, VkSparseImageMemoryBind>> ranges;
|
|
ranges.reserve(m_imageBinds.size());
|
|
|
|
for (const auto& e : m_imageBinds) {
|
|
const auto& key = e.first;
|
|
const auto& handle = e.second;
|
|
|
|
VkSparseImageMemoryBind bind = { };
|
|
bind.subresource = key.subresource;
|
|
bind.offset = key.offset;
|
|
bind.extent = key.extent;
|
|
bind.memory = handle.memory;
|
|
bind.memoryOffset = handle.offset;
|
|
|
|
ranges.push_back({ key.image, bind });
|
|
}
|
|
|
|
populateOutputArrays(image.binds, image.infos, ranges);
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::processOpaqueBinds(
|
|
DxvkSparseImageOpaqueBindArrays& opaque) {
|
|
std::vector<std::pair<VkImage, VkSparseMemoryBind>> ranges;
|
|
ranges.reserve(m_imageOpaqueBinds.size());
|
|
|
|
for (const auto& e : m_imageOpaqueBinds) {
|
|
const auto& key = e.first;
|
|
const auto& handle = e.second;
|
|
|
|
VkSparseMemoryBind bind = { };
|
|
bind.resourceOffset = key.offset;
|
|
bind.size = key.size;
|
|
bind.memory = handle.memory;
|
|
bind.memoryOffset = handle.offset;
|
|
bind.flags = key.flags;
|
|
|
|
bool merged = false;
|
|
|
|
if (!ranges.empty() && ranges.back().first == key.image)
|
|
merged = tryMergeMemoryBind(ranges.back().second, bind);
|
|
|
|
if (!merged)
|
|
ranges.push_back({ key.image, bind });
|
|
}
|
|
|
|
populateOutputArrays(opaque.binds, opaque.infos, ranges);
|
|
}
|
|
|
|
|
|
template<typename HandleType, typename BindType, typename InfoType>
|
|
void DxvkSparseBindSubmission::populateOutputArrays(
|
|
std::vector<BindType>& binds,
|
|
std::vector<InfoType>& infos,
|
|
const std::vector<std::pair<HandleType, BindType>>& input) {
|
|
HandleType handle = VK_NULL_HANDLE;
|
|
|
|
// Resize bind array so that pointers remain
|
|
// valid as we iterate over the input array
|
|
binds.resize(input.size());
|
|
|
|
for (size_t i = 0; i < input.size(); i++) {
|
|
binds[i] = input[i].second;
|
|
|
|
if (handle != input[i].first) {
|
|
// Create new info entry if the handle
|
|
// differs from that of the previous entry
|
|
handle = input[i].first;
|
|
infos.push_back({ handle, 1u, &binds[i] });
|
|
} else {
|
|
// Otherwise just increment the bind count
|
|
infos.back().bindCount += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DxvkSparseBindSubmission::logSparseBindingInfo(
|
|
LogLevel level,
|
|
const VkBindSparseInfo* info) {
|
|
std::stringstream str;
|
|
str << "VkBindSparseInfo:" << std::endl;
|
|
|
|
auto timelineInfo = static_cast<const VkTimelineSemaphoreSubmitInfo*>(info->pNext);
|
|
|
|
if (info->waitSemaphoreCount) {
|
|
str << " Wait semaphores (" << std::dec << info->waitSemaphoreCount << "):" << std::endl;
|
|
|
|
for (uint32_t i = 0; i < info->waitSemaphoreCount; i++)
|
|
str << " " << info->pWaitSemaphores[i] << " (" << timelineInfo->pWaitSemaphoreValues[i] << ")" << std::endl;
|
|
}
|
|
|
|
if (info->bufferBindCount) {
|
|
str << " Buffer binds (" << std::dec << info->bufferBindCount << "):" << std::endl;
|
|
|
|
for (uint32_t i = 0; i < info->bufferBindCount; i++) {
|
|
const auto* bindInfo = &info->pBufferBinds[i];
|
|
str << " VkBuffer " << bindInfo->buffer << " (" << bindInfo->bindCount << "):" << std::endl;
|
|
|
|
for (uint32_t j = 0; j < bindInfo->bindCount; j++) {
|
|
const auto* bind = &bindInfo->pBinds[j];
|
|
str << " " << bind->resourceOffset << " -> " << bind->memory
|
|
<< " (" << bind->memoryOffset << "," << bind->size << ")" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info->imageOpaqueBindCount) {
|
|
str << " Opaque image binds (" << std::dec << info->imageOpaqueBindCount << "):" << std::endl;
|
|
|
|
for (uint32_t i = 0; i < info->imageOpaqueBindCount; i++) {
|
|
const auto* bindInfo = &info->pImageOpaqueBinds[i];
|
|
str << " VkImage " << bindInfo->image << " (" << bindInfo->bindCount << "):" << std::endl;
|
|
|
|
for (uint32_t j = 0; j < bindInfo->bindCount; j++) {
|
|
const auto* bind = &bindInfo->pBinds[j];
|
|
str << " " << bind->resourceOffset << " -> " << bind->memory
|
|
<< " (" << bind->memoryOffset << "," << bind->size << ")" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info->imageBindCount) {
|
|
str << " Opaque image binds (" << std::dec << info->imageOpaqueBindCount << "):" << std::endl;
|
|
|
|
for (uint32_t i = 0; i < info->imageBindCount; i++) {
|
|
const auto* bindInfo = &info->pImageBinds[i];
|
|
str << " VkImage " << bindInfo->image << " (" << bindInfo->bindCount << "):" << std::endl;
|
|
|
|
for (uint32_t j = 0; j < bindInfo->bindCount; j++) {
|
|
const auto* bind = &bindInfo->pBinds[j];
|
|
|
|
str << " Aspect 0x" << std::hex << bind->subresource.aspectMask
|
|
<< ", Mip " << std::dec << bind->subresource.mipLevel
|
|
<< ", Layer " << bind->subresource.arrayLayer
|
|
<< ":" << std::endl;
|
|
|
|
str << " " << bind->offset.x << "," << bind->offset.y << "," << bind->offset.z << ":"
|
|
<< bind->extent.width << "x" << bind->extent.height << "x" << bind->extent.depth
|
|
<< " -> " << bind->memory << " (" << bind->memoryOffset << ")" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info->signalSemaphoreCount) {
|
|
str << " Signal semaphores (" << std::dec << info->signalSemaphoreCount << "):" << std::endl;
|
|
|
|
for (uint32_t i = 0; i < info->signalSemaphoreCount; i++)
|
|
str << " " << info->pSignalSemaphores[i] << " (" << timelineInfo->pSignalSemaphoreValues[i] << ")" << std::endl;
|
|
}
|
|
|
|
Logger::log(level, str.str());
|
|
}
|
|
|
|
}
|