From bc3affc26463eb004ebd42829da5e7de1adb5e1d Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 20 Aug 2022 14:36:49 +0200 Subject: [PATCH] [dxvk] Add functionality to query sparse metadata --- src/dxvk/dxvk_sparse.cpp | 186 ++++++++++++++++++++++++++++++++++++ src/dxvk/dxvk_sparse.h | 200 +++++++++++++++++++++++++++++++++++++++ src/dxvk/meson.build | 1 + 3 files changed, 387 insertions(+) create mode 100644 src/dxvk/dxvk_sparse.cpp create mode 100644 src/dxvk/dxvk_sparse.h diff --git a/src/dxvk/dxvk_sparse.cpp b/src/dxvk/dxvk_sparse.cpp new file mode 100644 index 000000000..367eb4a34 --- /dev/null +++ b/src/dxvk/dxvk_sparse.cpp @@ -0,0 +1,186 @@ +#include "dxvk_buffer.h" +#include "dxvk_device.h" +#include "dxvk_image.h" +#include "dxvk_sparse.h" + +namespace dxvk { + + 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); + + 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 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); + + 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); + } + } + } + } + +} diff --git a/src/dxvk/dxvk_sparse.h b/src/dxvk/dxvk_sparse.h new file mode 100644 index 000000000..46c373ba3 --- /dev/null +++ b/src/dxvk/dxvk_sparse.h @@ -0,0 +1,200 @@ +#pragma once + +#include "dxvk_memory.h" +#include "dxvk_resource.h" + +namespace dxvk { + + class DxvkCommandList; + class DxvkDevice; + class DxvkBuffer; + class DxvkImage; + class DxvkSparsePage; + class DxvkSparsePagePool; + + constexpr static VkDeviceSize SparseMemoryPageSize = 1ull << 16; + + /** + * \brief Sparse page handle + */ + struct DxvkSparsePageHandle { + VkDeviceMemory memory; + VkDeviceSize offset; + VkDeviceSize length; + }; + + + /** + * \brief Buffer info for sparse page + * + * Stores the buffer region backed by + * any given page. + */ + struct DxvkSparsePageBufferInfo { + VkDeviceSize offset; + VkDeviceSize length; + }; + + + /** + * \brief Image info for sparse page + * + * Stores the image region backed by + * any given page. + */ + struct DxvkSparsePageImageInfo { + VkImageSubresource subresource; + VkOffset3D offset; + VkExtent3D extent; + }; + + + /** + * \brief Image mip tail info for sparse page + * + * Stores the virtual resource offset and size + * within the mip tail backed by any given page. + */ + struct DxvkSparsePageMipTailInfo { + VkDeviceSize resourceOffset; + VkDeviceSize resourceLength; + }; + + + /** + * \brief Page type + */ + enum class DxvkSparsePageType : uint32_t { + None = 0, + Buffer = 1, + Image = 2, + ImageMipTail = 3, + }; + + + /** + * \brief Sparse page table metadata + * + * Stores the resource region backed by any given page. + */ + struct DxvkSparsePageInfo { + DxvkSparsePageType type; + union { + DxvkSparsePageBufferInfo buffer; + DxvkSparsePageImageInfo image; + DxvkSparsePageMipTailInfo mipTail; + }; + }; + + + /** + * \brief Image tiling info + */ + struct DxvkSparseImageProperties { + VkSparseImageFormatFlags flags; + VkExtent3D pageRegionExtent; + uint32_t pagedMipCount; + uint32_t metadataPageCount; + uint32_t mipTailPageIndex; + VkDeviceSize mipTailOffset; + VkDeviceSize mipTailSize; + VkDeviceSize mipTailStride; + }; + + + /** + * \brief Image subresource tiling info + */ + struct DxvkSparseImageSubresourceProperties { + VkBool32 isMipTail; + VkExtent3D pageCount; + uint32_t pageIndex; + }; + + + /** + * \brief Sparse page table + * + * Stores mappings from a resource region to a given memory page, + * as well as mapping tile indices to the given resource region. + */ + class DxvkSparsePageTable { + + public: + + DxvkSparsePageTable(); + + DxvkSparsePageTable( + DxvkDevice* device, + const DxvkBuffer* buffer); + + DxvkSparsePageTable( + DxvkDevice* device, + const DxvkImage* image); + + /** + * \brief Counts total number of pages in the resources + * + * Counts the number of pages for the entire resource, both + * for paged subresources as well as the mip tail. + * \returns Total number of pages + */ + uint32_t getPageCount() const { + return uint32_t(m_metadata.size()); + } + + /** + * \brief Counts number of subresource infos + * \returns Subresource info count + */ + uint32_t getSubresourceCount() const { + return uint32_t(m_subresources.size()); + } + + /** + * \brief Retrieves image properties + * + * Only contains meaningful info if the page + * table object was created for an image. + * \returns Image properties + */ + DxvkSparseImageProperties getProperties() const { + return m_properties; + } + + /** + * \brief Retrieves image subresource properties + * + * \param [in] subresource The subresource to query + * \returns Properties of the given subresource + */ + DxvkSparseImageSubresourceProperties getSubresourceProperties(uint32_t subresource) const { + return subresource < getSubresourceCount() + ? m_subresources[subresource] + : DxvkSparseImageSubresourceProperties(); + } + + /** + * \brief Queries info for a given page + * + * \param [in] page Page index + * \returns Page info + */ + DxvkSparsePageInfo getPageInfo(uint32_t page) const { + return page < getPageCount() + ? m_metadata[page] + : DxvkSparsePageInfo(); + } + + private: + + const DxvkBuffer* m_buffer = nullptr; + const DxvkImage* m_image = nullptr; + + DxvkSparseImageProperties m_properties = { }; + std::vector m_subresources; + std::vector m_metadata; + + }; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 56aa42102..8c4b81784 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -98,6 +98,7 @@ dxvk_src = [ 'dxvk_shader.cpp', 'dxvk_shader_key.cpp', 'dxvk_signal.cpp', + 'dxvk_sparse.cpp', 'dxvk_staging.cpp', 'dxvk_state_cache.cpp', 'dxvk_stats.cpp',