diff --git a/src/dxvk/dxvk_buffer.cpp b/src/dxvk/dxvk_buffer.cpp index 78a5946cd..314a1b68d 100644 --- a/src/dxvk/dxvk_buffer.cpp +++ b/src/dxvk/dxvk_buffer.cpp @@ -65,25 +65,32 @@ namespace dxvk { DxvkBufferHandle handle; - if (vkd->vkCreateBuffer(vkd->device(), - &info, nullptr, &handle.buffer) != VK_SUCCESS) { + if (vkd->vkCreateBuffer(vkd->device(), &info, nullptr, &handle.buffer)) { throw DxvkError(str::format( "DxvkBuffer: Failed to create buffer:" "\n size: ", info.size, "\n usage: ", info.usage)); } - - VkMemoryDedicatedRequirements dedicatedRequirements = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS }; - VkMemoryRequirements2 memReq = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements }; - - VkBufferMemoryRequirementsInfo2 memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 }; - memReqInfo.buffer = handle.buffer; - - VkMemoryDedicatedAllocateInfo dedMemoryAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO }; - dedMemoryAllocInfo.buffer = handle.buffer; - vkd->vkGetBufferMemoryRequirements2( - vkd->device(), &memReqInfo, &memReq); + // Query memory requirements and whether to use a dedicated allocation + DxvkMemoryRequirements memoryRequirements = { }; + memoryRequirements.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS }; + memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &memoryRequirements.dedicated }; + + VkBufferMemoryRequirementsInfo2 memoryRequirementInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 }; + memoryRequirementInfo.buffer = handle.buffer; + + vkd->vkGetBufferMemoryRequirements2(vkd->device(), + &memoryRequirementInfo, &memoryRequirements.core); + + // Fill in desired memory properties + DxvkMemoryProperties memoryProperties = { }; + memoryProperties.flags = m_memFlags; + + if (memoryRequirements.dedicated.prefersDedicatedAllocation) { + memoryProperties.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO }; + memoryProperties.dedicated.buffer = handle.buffer; + } // Use high memory priority for GPU-writable resources bool isGpuWritable = (m_info.access & ( @@ -102,9 +109,7 @@ namespace dxvk { && (m_info.usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) hints.set(DxvkMemoryFlag::Transient); - // Ask driver whether we should be using a dedicated allocation - handle.memory = m_memAlloc->alloc(&memReq.memoryRequirements, - dedicatedRequirements, dedMemoryAllocInfo, m_memFlags, hints); + handle.memory = m_memAlloc->alloc(memoryRequirements, memoryProperties, hints); if (vkd->vkBindBufferMemory(vkd->device(), handle.buffer, handle.memory.memory(), handle.memory.offset()) != VK_SUCCESS) diff --git a/src/dxvk/dxvk_image.cpp b/src/dxvk/dxvk_image.cpp index d48fc05ed..013420386 100644 --- a/src/dxvk/dxvk_image.cpp +++ b/src/dxvk/dxvk_image.cpp @@ -22,7 +22,10 @@ namespace dxvk { VkImageFormatListCreateInfo formatList = { VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO }; formatList.viewFormatCount = createInfo.viewFormatCount; formatList.pViewFormats = createInfo.viewFormats; - + + VkExternalMemoryImageCreateInfo externalInfo = { VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO }; + externalInfo.handleTypes = createInfo.sharing.type; + VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, &formatList }; info.flags = createInfo.flags; info.imageType = createInfo.type; @@ -36,17 +39,10 @@ namespace dxvk { info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = createInfo.initialLayout; - m_shared = canShareImage(info, createInfo.sharing); - - VkExternalMemoryImageCreateInfo externalInfo = { VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO }; - - if (m_shared) { + if ((m_shared = canShareImage(info, createInfo.sharing))) externalInfo.pNext = std::exchange(info.pNext, &externalInfo); - externalInfo.handleTypes = createInfo.sharing.type; - } - - if (m_vkd->vkCreateImage(m_vkd->device(), - &info, nullptr, &m_image.image) != VK_SUCCESS) { + + if (m_vkd->vkCreateImage(m_vkd->device(), &info, nullptr, &m_image.image)) { throw DxvkError(str::format( "DxvkImage: Failed to create image:", "\n Type: ", info.imageType, @@ -61,40 +57,52 @@ namespace dxvk { "\n Tiling: ", info.tiling)); } - // Get memory requirements for the image. We may enforce strict - // alignment on non-linear images in order not to violate the - // bufferImageGranularity limit, which may be greater than the - // required resource memory alignment on some GPUs. - VkMemoryDedicatedRequirements dedicatedRequirements = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS }; - VkMemoryRequirements2 memReq = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicatedRequirements }; - - VkImageMemoryRequirementsInfo2 memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 }; - memReqInfo.image = m_image.image; + // Get memory requirements for the image and ask driver + // whether we need to use a dedicated allocation. + DxvkMemoryRequirements memoryRequirements = { }; + memoryRequirements.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS }; + memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &memoryRequirements.dedicated }; - VkMemoryDedicatedAllocateInfo dedMemoryAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO }; - dedMemoryAllocInfo.image = m_image.image; + VkImageMemoryRequirementsInfo2 memoryRequirementInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 }; + memoryRequirementInfo.image = m_image.image; - VkExportMemoryAllocateInfo exportInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO }; - if (m_shared && createInfo.sharing.mode == DxvkSharedHandleMode::Export) { - exportInfo.pNext = std::exchange(dedMemoryAllocInfo.pNext, &exportInfo); - exportInfo.handleTypes = createInfo.sharing.type; + m_vkd->vkGetImageMemoryRequirements2(m_vkd->device(), + &memoryRequirementInfo, &memoryRequirements.core); + + // Fill in desired memory properties + DxvkMemoryProperties memoryProperties = { }; + memoryProperties.flags = m_memFlags; + + if (m_shared) { + memoryRequirements.dedicated.prefersDedicatedAllocation = VK_TRUE; + memoryRequirements.dedicated.requiresDedicatedAllocation = VK_TRUE; + + if (createInfo.sharing.mode == DxvkSharedHandleMode::Export) { + memoryProperties.sharedExport = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO }; + memoryProperties.sharedExport.handleTypes = createInfo.sharing.type; + } + + if (createInfo.sharing.mode == DxvkSharedHandleMode::Import) { + memoryProperties.sharedImportWin32 = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR }; + memoryProperties.sharedImportWin32.handleType = createInfo.sharing.type; + memoryProperties.sharedImportWin32.handle = createInfo.sharing.handle; + } } -#ifdef _WIN32 - VkImportMemoryWin32HandleInfoKHR importInfo = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR }; - if (m_shared && createInfo.sharing.mode == DxvkSharedHandleMode::Import) { - importInfo.pNext = std::exchange(dedMemoryAllocInfo.pNext, &importInfo); - importInfo.handleType = createInfo.sharing.type; - importInfo.handle = createInfo.sharing.handle; + if (memoryRequirements.dedicated.prefersDedicatedAllocation) { + memoryProperties.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO }; + memoryProperties.dedicated.image = m_image.image; } -#endif - m_vkd->vkGetImageMemoryRequirements2( - m_vkd->device(), &memReqInfo, &memReq); + // If there's a chance we won't create the image with a dedicated + // allocation, enforce strict alignment for tiled images to not + // violate the bufferImageGranularity requirement on some GPUs. + if (info.tiling != VK_IMAGE_TILING_LINEAR && !memoryRequirements.dedicated.requiresDedicatedAllocation) { + VkDeviceSize granularity = memAlloc.bufferImageGranularity(); - if (info.tiling != VK_IMAGE_TILING_LINEAR && !dedicatedRequirements.prefersDedicatedAllocation) { - memReq.memoryRequirements.size = align(memReq.memoryRequirements.size, memAlloc.bufferImageGranularity()); - memReq.memoryRequirements.alignment = align(memReq.memoryRequirements.alignment , memAlloc.bufferImageGranularity()); + auto& core = memoryRequirements.core.memoryRequirements; + core.size = align(core.size, granularity); + core.alignment = align(core.alignment, granularity); } // Use high memory priority for GPU-writable resources @@ -110,14 +118,7 @@ namespace dxvk { if (isGpuWritable) hints.set(DxvkMemoryFlag::GpuWritable); - if (m_shared) { - dedicatedRequirements.prefersDedicatedAllocation = VK_TRUE; - dedicatedRequirements.requiresDedicatedAllocation = VK_TRUE; - } - - // Ask driver whether we should be using a dedicated allocation - m_image.memory = memAlloc.alloc(&memReq.memoryRequirements, - dedicatedRequirements, dedMemoryAllocInfo, memFlags, hints); + m_image.memory = memAlloc.alloc(memoryRequirements, memoryProperties, hints); // Try to bind the allocated memory slice to the image if (m_vkd->vkBindImageMemory(m_vkd->device(), m_image.image, diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index e59e7ddce..f0dfa6696 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -211,63 +211,66 @@ namespace dxvk { DxvkMemory DxvkMemoryAllocator::alloc( - const VkMemoryRequirements* req, - const VkMemoryDedicatedRequirements& dedAllocReq, - const VkMemoryDedicatedAllocateInfo& dedAllocInfo, - VkMemoryPropertyFlags flags, + const DxvkMemoryRequirements& req, + DxvkMemoryProperties info, DxvkMemoryFlags hints) { std::lock_guard lock(m_mutex); // Keep small allocations together to avoid fragmenting // chunks for larger resources with lots of small gaps, // as well as resources with potentially weird lifetimes - if (req->size <= SmallAllocationThreshold) { + if (req.core.memoryRequirements.size <= SmallAllocationThreshold) { hints.set(DxvkMemoryFlag::Small); hints.clr(DxvkMemoryFlag::GpuWritable, DxvkMemoryFlag::GpuReadable); } // Ignore most hints for host-visible allocations since they // usually don't make much sense for those resources - if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + if (info.flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) hints = hints & DxvkMemoryFlag::Transient; // Try to allocate from a memory type which supports the given flags exactly - auto dedAllocPtr = dedAllocReq.prefersDedicatedAllocation ? &dedAllocInfo : nullptr; - DxvkMemory result = this->tryAlloc(req, dedAllocPtr, flags, hints); + DxvkMemory result = this->tryAlloc(req, info, hints); - // If the first attempt failed, try ignoring the dedicated allocation - if (!result && dedAllocPtr && !dedAllocReq.requiresDedicatedAllocation) { - result = this->tryAlloc(req, nullptr, flags, hints); - dedAllocPtr = nullptr; + if (!result && !req.dedicated.requiresDedicatedAllocation) { + // If that failed, try without a dedicated allocation + if (info.dedicated.image || info.dedicated.buffer) { + info.dedicated.image = VK_NULL_HANDLE; + info.dedicated.buffer = VK_NULL_HANDLE; + + result = this->tryAlloc(req, info, hints); + } } - // Retry without the hint constraints if (!result) { + // Retry without the hint constraints hints.set(DxvkMemoryFlag::IgnoreConstraints); - result = this->tryAlloc(req, nullptr, flags, hints); + result = this->tryAlloc(req, info, hints); } - // If that still didn't work, probe slower memory types as well - VkMemoryPropertyFlags optFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT - | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; - VkMemoryPropertyFlags remFlags = 0; - - while (!result && (flags & optFlags)) { - remFlags |= optFlags & -optFlags; - optFlags &= ~remFlags; + if (!result) { + // If that still didn't work, probe slower memory types as well + VkMemoryPropertyFlags optFlags = info.flags & ( + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT); - result = this->tryAlloc(req, dedAllocPtr, flags & ~remFlags, hints); + while (!result && optFlags) { + VkMemoryPropertyFlags bit = optFlags & -optFlags; + optFlags &= ~bit; + + info.flags &= ~bit; + result = this->tryAlloc(req, info, hints); + } } - + if (!result) { DxvkAdapterMemoryInfo memHeapInfo = m_device->adapter()->getMemoryHeapInfo(); Logger::err(str::format( "DxvkMemoryAllocator: Memory allocation failed", - "\n Size: ", req->size, - "\n Alignment: ", req->alignment, - "\n Mem flags: ", "0x", std::hex, flags, - "\n Mem types: ", "0x", std::hex, req->memoryTypeBits)); + "\n Size: ", req.core.memoryRequirements.size, + "\n Alignment: ", req.core.memoryRequirements.alignment, + "\n Mem types: ", "0x", std::hex, req.core.memoryRequirements.memoryTypeBits)); for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) { Logger::err(str::format("Heap ", i, ": ", @@ -290,19 +293,20 @@ namespace dxvk { DxvkMemory DxvkMemoryAllocator::tryAlloc( - const VkMemoryRequirements* req, - const VkMemoryDedicatedAllocateInfo* dedAllocInfo, - VkMemoryPropertyFlags flags, + const DxvkMemoryRequirements& req, + const DxvkMemoryProperties& info, DxvkMemoryFlags hints) { DxvkMemory result; for (uint32_t i = 0; i < m_memProps.memoryTypeCount && !result; i++) { - const bool supported = (req->memoryTypeBits & (1u << i)) != 0; - const bool adequate = (m_memTypes[i].memType.propertyFlags & flags) == flags; + const bool supported = (req.core.memoryRequirements.memoryTypeBits & (1u << i)) != 0; + const bool adequate = (m_memTypes[i].memType.propertyFlags & info.flags) == info.flags; if (supported && adequate) { result = this->tryAllocFromType(&m_memTypes[i], - flags, req->size, req->alignment, hints, dedAllocInfo); + req.core.memoryRequirements.size, + req.core.memoryRequirements.alignment, + info, hints); } } @@ -312,27 +316,25 @@ namespace dxvk { DxvkMemory DxvkMemoryAllocator::tryAllocFromType( DxvkMemoryType* type, - VkMemoryPropertyFlags flags, VkDeviceSize size, VkDeviceSize align, - DxvkMemoryFlags hints, - const VkMemoryDedicatedAllocateInfo* dedAllocInfo) { + const DxvkMemoryProperties& info, + DxvkMemoryFlags hints) { VkDeviceSize chunkSize = pickChunkSize(type->memTypeId, hints); DxvkMemory memory; - if (size >= chunkSize || dedAllocInfo) { + if (size >= chunkSize || info.dedicated.buffer || info.dedicated.image) { if (this->shouldFreeEmptyChunks(type->heap, size)) this->freeEmptyChunks(type->heap); - DxvkDeviceMemory devMem = this->tryAllocDeviceMemory( - type, flags, size, hints, dedAllocInfo); + DxvkDeviceMemory devMem = this->tryAllocDeviceMemory(type, size, info, hints); if (devMem.memHandle != VK_NULL_HANDLE) memory = DxvkMemory(this, nullptr, type, devMem.memHandle, 0, size, devMem.memPointer); } else { for (uint32_t i = 0; i < type->chunks.size() && !memory; i++) - memory = type->chunks[i]->alloc(flags, size, align, hints); + memory = type->chunks[i]->alloc(info.flags, size, align, hints); if (!memory) { DxvkDeviceMemory devMem; @@ -341,11 +343,11 @@ namespace dxvk { this->freeEmptyChunks(type->heap); for (uint32_t i = 0; i < 6 && (chunkSize >> i) >= size && !devMem.memHandle; i++) - devMem = tryAllocDeviceMemory(type, flags, chunkSize >> i, hints, nullptr); + devMem = tryAllocDeviceMemory(type, chunkSize >> i, info, hints); if (devMem.memHandle) { Rc chunk = new DxvkMemoryChunk(this, type, devMem, hints); - memory = chunk->alloc(flags, size, align, hints); + memory = chunk->alloc(info.flags, size, align, hints); type->chunks.push_back(std::move(chunk)); } @@ -361,11 +363,10 @@ namespace dxvk { DxvkDeviceMemory DxvkMemoryAllocator::tryAllocDeviceMemory( DxvkMemoryType* type, - VkMemoryPropertyFlags flags, VkDeviceSize size, - DxvkMemoryFlags hints, - const VkMemoryDedicatedAllocateInfo* dedAllocInfo) { - bool useMemoryPriority = (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + DxvkMemoryProperties info, + DxvkMemoryFlags hints) { + bool useMemoryPriority = (info.flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && (m_device->features().extMemoryPriority.memoryPriority); if (type->heap->budget && type->heap->stats.memoryAllocated + size > type->heap->budget) @@ -380,23 +381,32 @@ namespace dxvk { DxvkDeviceMemory result; result.memSize = size; - result.memFlags = flags; + result.memFlags = info.flags; result.priority = priority; - VkMemoryPriorityAllocateInfoEXT prio = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; - prio.priority = priority; + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + priorityInfo.priority = priority; - VkMemoryAllocateInfo info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, dedAllocInfo }; - info.allocationSize = size; - info.memoryTypeIndex = type->memTypeId; + VkMemoryAllocateInfo memoryInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + memoryInfo.allocationSize = size; + memoryInfo.memoryTypeIndex = type->memTypeId; + + if (info.sharedExport.handleTypes) + info.sharedExport.pNext = std::exchange(memoryInfo.pNext, &info.sharedExport); + + if (info.sharedImportWin32.handleType) + info.sharedImportWin32.pNext = std::exchange(memoryInfo.pNext, &info.sharedImportWin32); + + if (info.dedicated.buffer || info.dedicated.image) + info.dedicated.pNext = std::exchange(memoryInfo.pNext, &info.dedicated); if (useMemoryPriority) - prio.pNext = std::exchange(info.pNext, &prio); + priorityInfo.pNext = std::exchange(memoryInfo.pNext, &priorityInfo); - if (m_vkd->vkAllocateMemory(m_vkd->device(), &info, nullptr, &result.memHandle) != VK_SUCCESS) + if (m_vkd->vkAllocateMemory(m_vkd->device(), &memoryInfo, nullptr, &result.memHandle) != VK_SUCCESS) return DxvkDeviceMemory(); - if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + if (info.flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { VkResult status = m_vkd->vkMapMemory(m_vkd->device(), result.memHandle, 0, VK_WHOLE_SIZE, 0, &result.memPointer); if (status != VK_SUCCESS) { diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index 5b8e829dc..af377a95f 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -271,8 +271,28 @@ namespace dxvk { bool checkHints(DxvkMemoryFlags hints) const; }; - - + + + /** + * \brief Memory requirement info + */ + struct DxvkMemoryRequirements { + VkMemoryDedicatedRequirements dedicated; + VkMemoryRequirements2 core; + }; + + + /** + * \brief Memory allocation info + */ + struct DxvkMemoryProperties { + VkExportMemoryAllocateInfo sharedExport; + VkImportMemoryWin32HandleInfoKHR sharedImportWin32; + VkMemoryDedicatedAllocateInfo dedicated; + VkMemoryPropertyFlags flags; + }; + + /** * \brief Memory allocator * @@ -305,17 +325,13 @@ namespace dxvk { * \brief Allocates device memory * * \param [in] req Memory requirements - * \param [in] dedAllocReq Dedicated allocation requirements - * \param [in] dedAllocInfo Dedicated allocation info - * \param [in] flags Memory type flags + * \param [in] info Memory properties * \param [in] hints Memory hints * \returns Allocated memory slice */ DxvkMemory alloc( - const VkMemoryRequirements* req, - const VkMemoryDedicatedRequirements& dedAllocReq, - const VkMemoryDedicatedAllocateInfo& dedAllocInfo, - VkMemoryPropertyFlags flags, + const DxvkMemoryRequirements& req, + DxvkMemoryProperties info, DxvkMemoryFlags hints); /** @@ -342,25 +358,22 @@ namespace dxvk { std::array m_memTypes; DxvkMemory tryAlloc( - const VkMemoryRequirements* req, - const VkMemoryDedicatedAllocateInfo* dedAllocInfo, - VkMemoryPropertyFlags flags, + const DxvkMemoryRequirements& req, + const DxvkMemoryProperties& info, DxvkMemoryFlags hints); DxvkMemory tryAllocFromType( DxvkMemoryType* type, - VkMemoryPropertyFlags flags, VkDeviceSize size, VkDeviceSize align, - DxvkMemoryFlags hints, - const VkMemoryDedicatedAllocateInfo* dedAllocInfo); + const DxvkMemoryProperties& info, + DxvkMemoryFlags hints); DxvkDeviceMemory tryAllocDeviceMemory( DxvkMemoryType* type, - VkMemoryPropertyFlags flags, VkDeviceSize size, - DxvkMemoryFlags hints, - const VkMemoryDedicatedAllocateInfo* dedAllocInfo); + DxvkMemoryProperties info, + DxvkMemoryFlags hints); void free( const DxvkMemory& memory);