mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-07 06:18:23 +00:00
Adjustments on UMA
This commit is contained in:
@@ -525,7 +525,7 @@
|
|||||||
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
||||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
||||||
<string name="renderer_unified_memory">Unified memory access (UMA)</string>
|
<string name="renderer_unified_memory">Unified memory access (UMA)</string>
|
||||||
<string name="renderer_unified_memory_description">Lets the GPU read guest memory directly for buffer uploads, skipping the CPU staging copy. Requires driver support for host memory import and is automatically disabled when unsupported.</string>
|
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
|
||||||
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
||||||
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
||||||
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
||||||
|
|||||||
@@ -231,9 +231,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
|||||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||||
tr("May reduce shader stutter."));
|
tr("May reduce shader stutter."));
|
||||||
INSERT(Settings, use_unified_memory, tr("Enable unified memory access (UMA)"),
|
INSERT(Settings, use_unified_memory, tr("Enable unified memory access (UMA)"),
|
||||||
tr("Lets the GPU read guest memory directly for buffer uploads, skipping the CPU "
|
tr("Lets the GPU write buffer readbacks directly into guest memory."));
|
||||||
"staging copy.\nRequires driver support for host memory import and may cause "
|
|
||||||
"issues in some games."));
|
|
||||||
INSERT(Settings, pipeline_worker_count, tr("Pipeline Worker Threads"),
|
INSERT(Settings, pipeline_worker_count, tr("Pipeline Worker Threads"),
|
||||||
tr("Number of threads used to build Vulkan pipelines.\n"
|
tr("Number of threads used to build Vulkan pipelines.\n"
|
||||||
"Higher values speed up compilation at the cost of heat and power."));
|
"Higher values speed up compilation at the cost of heat and power."));
|
||||||
|
|||||||
@@ -1815,53 +1815,73 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::TryUnifiedUploadMemory([[maybe_unused]] Buffer& buffer,
|
bool BufferCache<P>::TryUnifiedDownloadMemory([[maybe_unused]] Buffer& buffer,
|
||||||
[[maybe_unused]] std::span<BufferCopy> copies) {
|
[[maybe_unused]] std::span<BufferCopy> copies) {
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
if constexpr (USE_UNIFIED_MEMORY) {
|
||||||
boost::container::small_vector<BufferCopy, 16> host_copies;
|
|
||||||
const u8* const physical_base = device_memory.GetPhysicalBase();
|
const u8* const physical_base = device_memory.GetPhysicalBase();
|
||||||
const u64 unified_size = runtime.UnifiedMemorySize();
|
const u64 unified_size = runtime.UnifiedMemorySize();
|
||||||
|
const u64 window_size = runtime.UnifiedMemoryWindowSize();
|
||||||
|
if (window_size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boost::container::small_vector<u64, 4> window_ids;
|
||||||
|
boost::container::small_vector<boost::container::small_vector<BufferCopy, 16>, 4> groups;
|
||||||
|
const auto group_for = [&](u64 window) -> boost::container::small_vector<BufferCopy, 16>& {
|
||||||
|
for (size_t i = 0; i < window_ids.size(); ++i) {
|
||||||
|
if (window_ids[i] == window) {
|
||||||
|
return groups[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window_ids.push_back(window);
|
||||||
|
groups.emplace_back();
|
||||||
|
return groups.back();
|
||||||
|
};
|
||||||
for (const BufferCopy& copy : copies) {
|
for (const BufferCopy& copy : copies) {
|
||||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
|
||||||
u64 uploaded = 0;
|
u64 downloaded = 0;
|
||||||
while (uploaded < copy.size) {
|
while (downloaded < copy.size) {
|
||||||
const DAddr page_addr = device_addr + uploaded;
|
const DAddr page_addr = device_addr + downloaded;
|
||||||
const u8* const ptr = device_memory.GetPointer<u8>(page_addr);
|
const u8* const ptr = device_memory.GetPointer<u8>(page_addr);
|
||||||
if (ptr == nullptr) {
|
if (ptr == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const u64 page_offset = page_addr & Core::DEVICE_PAGEMASK;
|
const u64 page_offset = page_addr & Core::DEVICE_PAGEMASK;
|
||||||
const u64 chunk = (std::min)(copy.size - uploaded,
|
u64 chunk = (std::min)(copy.size - downloaded,
|
||||||
static_cast<u64>(Core::DEVICE_PAGESIZE) - page_offset);
|
static_cast<u64>(Core::DEVICE_PAGESIZE) - page_offset);
|
||||||
const u64 src_offset = static_cast<u64>(ptr - physical_base);
|
const u64 phys_offset = static_cast<u64>(ptr - physical_base);
|
||||||
if (src_offset + chunk > unified_size) {
|
if (phys_offset + chunk > unified_size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!host_copies.empty()) {
|
const u64 window = phys_offset / window_size;
|
||||||
BufferCopy& last = host_copies.back();
|
const u64 local_offset = phys_offset % window_size;
|
||||||
if (last.src_offset + last.size == src_offset &&
|
chunk = (std::min)(chunk, window_size - local_offset);
|
||||||
last.dst_offset + last.size == copy.dst_offset + uploaded) {
|
auto& group = group_for(window);
|
||||||
|
if (!group.empty()) {
|
||||||
|
BufferCopy& last = group.back();
|
||||||
|
if (last.src_offset + last.size == copy.src_offset + downloaded &&
|
||||||
|
last.dst_offset + last.size == local_offset) {
|
||||||
last.size += chunk;
|
last.size += chunk;
|
||||||
uploaded += chunk;
|
downloaded += chunk;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
host_copies.push_back(BufferCopy{
|
group.push_back(BufferCopy{
|
||||||
.src_offset = src_offset,
|
.src_offset = copy.src_offset + downloaded,
|
||||||
.dst_offset = copy.dst_offset + uploaded,
|
.dst_offset = local_offset,
|
||||||
.size = chunk,
|
.size = chunk,
|
||||||
});
|
});
|
||||||
uploaded += chunk;
|
downloaded += chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const BufferCopy& copy : copies) {
|
for (const BufferCopy& copy : copies) {
|
||||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
buffer.MarkUsage(copy.src_offset, copy.size);
|
||||||
DownloadBufferMemory(buffer, buffer.CpuAddr() + copy.dst_offset, copy.size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const std::span<BufferCopy> host_span(host_copies.data(), host_copies.size());
|
for (size_t i = 0; i < window_ids.size(); ++i) {
|
||||||
const bool can_reorder = runtime.CanReorderUpload(buffer, host_span);
|
const std::span<BufferCopy> group_span(groups[i].data(), groups[i].size());
|
||||||
runtime.CopyBuffer(buffer, runtime.UnifiedMemoryBuffer(), host_span, true, can_reorder);
|
runtime.CopyBuffer(runtime.UnifiedMemoryWindowBuffer(window_ids[i]), buffer,
|
||||||
|
group_span, true);
|
||||||
|
}
|
||||||
|
runtime.Finish();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -1873,11 +1893,6 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
[[maybe_unused]] u64 total_size_bytes,
|
[[maybe_unused]] u64 total_size_bytes,
|
||||||
[[maybe_unused]] std::span<BufferCopy> copies) {
|
[[maybe_unused]] std::span<BufferCopy> copies) {
|
||||||
if constexpr (USE_MEMORY_MAPS) {
|
if constexpr (USE_MEMORY_MAPS) {
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
if (runtime.HasUnifiedMemory() && TryUnifiedUploadMemory(buffer, copies)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
|
auto upload_staging = runtime.UploadStagingBuffer(total_size_bytes);
|
||||||
const std::span<u8> staging_pointer = upload_staging.mapped_span;
|
const std::span<u8> staging_pointer = upload_staging.mapped_span;
|
||||||
for (BufferCopy& copy : copies) {
|
for (BufferCopy& copy : copies) {
|
||||||
@@ -1976,6 +1991,12 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
|
|||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (USE_MEMORY_MAPS) {
|
if constexpr (USE_MEMORY_MAPS) {
|
||||||
|
if constexpr (USE_UNIFIED_MEMORY) {
|
||||||
|
if (runtime.HasUnifiedMemory() &&
|
||||||
|
TryUnifiedDownloadMemory(buffer, std::span(copies.data(), copies.size()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
||||||
const u8* const mapped_memory = download_staging.mapped_span.data();
|
const u8* const mapped_memory = download_staging.mapped_span.data();
|
||||||
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
|
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ private:
|
|||||||
|
|
||||||
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
|
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
|
||||||
|
|
||||||
bool TryUnifiedUploadMemory(Buffer& buffer, std::span<BufferCopy> copies);
|
bool TryUnifiedDownloadMemory(Buffer& buffer, std::span<BufferCopy> copies);
|
||||||
|
|
||||||
void DownloadBufferMemory(Buffer& buffer_id);
|
void DownloadBufferMemory(Buffer& buffer_id);
|
||||||
|
|
||||||
|
|||||||
@@ -104,14 +104,18 @@ public:
|
|||||||
return unified_memory != nullptr && unified_memory->IsValid();
|
return unified_memory != nullptr && unified_memory->IsValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkBuffer UnifiedMemoryBuffer() const noexcept {
|
|
||||||
return unified_memory ? unified_memory->GetBuffer() : VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u64 UnifiedMemorySize() const noexcept {
|
[[nodiscard]] u64 UnifiedMemorySize() const noexcept {
|
||||||
return unified_memory ? unified_memory->GetSize() : 0;
|
return unified_memory ? unified_memory->GetSize() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 UnifiedMemoryWindowSize() const noexcept {
|
||||||
|
return unified_memory ? unified_memory->GetWindowSize() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkBuffer UnifiedMemoryWindowBuffer(size_t index) const noexcept {
|
||||||
|
return unified_memory ? unified_memory->GetWindowBuffer(index) : VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
u64 CurrentTick();
|
u64 CurrentTick();
|
||||||
|
|
||||||
u64 KnownGpuTick();
|
u64 KnownGpuTick();
|
||||||
|
|||||||
@@ -83,52 +83,21 @@ namespace Vulkan {
|
|||||||
alignment);
|
alignment);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
using namespace Common::Literals;
|
||||||
|
VkDeviceSize candidate_window = 1_GiB;
|
||||||
const u64 max_buffer_size = device.GetMaxBufferSize();
|
const u64 max_buffer_size = device.GetMaxBufferSize();
|
||||||
if (max_buffer_size != 0 && max_buffer_size < size) {
|
if (max_buffer_size != 0 && max_buffer_size < candidate_window) {
|
||||||
size = static_cast<size_t>(Common::AlignDown(max_buffer_size, alignment));
|
candidate_window = Common::AlignDown(max_buffer_size, alignment);
|
||||||
if (size == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (candidate_window == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window_size = candidate_window;
|
||||||
|
|
||||||
const auto &logical = device.GetLogical();
|
const auto &logical = device.GetLogical();
|
||||||
VkMemoryHostPointerPropertiesEXT host_props{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.memoryTypeBits = 0,
|
|
||||||
};
|
|
||||||
if (logical.GetMemoryHostPointerPropertiesEXT(
|
|
||||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, base, &host_props) !=
|
|
||||||
VK_SUCCESS ||
|
|
||||||
host_props.memoryTypeBits == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const VkExternalMemoryBufferCreateInfo external_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
|
|
||||||
};
|
|
||||||
const VkBufferCreateInfo buffer_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = &external_info,
|
|
||||||
.flags = 0,
|
|
||||||
.size = size,
|
|
||||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
.queueFamilyIndexCount = 0,
|
|
||||||
.pQueueFamilyIndices = nullptr,
|
|
||||||
};
|
|
||||||
VkBuffer new_buffer{};
|
|
||||||
if (logical.CreateBufferRaw(buffer_ci, &new_buffer) != VK_SUCCESS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const VkMemoryRequirements requirements = logical.GetBufferMemoryRequirements(new_buffer);
|
|
||||||
const u32 type_mask = requirements.memoryTypeBits & host_props.memoryTypeBits;
|
|
||||||
if (type_mask == 0 || requirements.size > size) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties;
|
const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties;
|
||||||
const auto find_type = [&](VkMemoryPropertyFlags wanted) -> std::optional<u32> {
|
const auto find_type = [&](u32 type_mask, VkMemoryPropertyFlags wanted)
|
||||||
|
-> std::optional<u32> {
|
||||||
for (u32 i = 0; i < memory_props.memoryTypeCount; ++i) {
|
for (u32 i = 0; i < memory_props.memoryTypeCount; ++i) {
|
||||||
if (((type_mask >> i) & 1u) != 0 &&
|
if (((type_mask >> i) & 1u) != 0 &&
|
||||||
(memory_props.memoryTypes[i].propertyFlags & wanted) == wanted) {
|
(memory_props.memoryTypes[i].propertyFlags & wanted) == wanted) {
|
||||||
@@ -137,48 +106,100 @@ namespace Vulkan {
|
|||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
};
|
};
|
||||||
auto type_index = find_type(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
||||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
for (size_t offset = 0; offset < size; offset += window_size) {
|
||||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
|
u8 *const window_base = static_cast<u8 *>(base) + offset;
|
||||||
if (!type_index) {
|
const VkDeviceSize window_len =
|
||||||
type_index = find_type(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
(std::min)(static_cast<VkDeviceSize>(size - offset), window_size);
|
||||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
VkMemoryHostPointerPropertiesEXT host_props{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.memoryTypeBits = 0,
|
||||||
|
};
|
||||||
|
if (logical.GetMemoryHostPointerPropertiesEXT(
|
||||||
|
VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, window_base,
|
||||||
|
&host_props) != VK_SUCCESS ||
|
||||||
|
host_props.memoryTypeBits == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const VkExternalMemoryBufferCreateInfo external_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
|
||||||
|
};
|
||||||
|
const VkBufferCreateInfo buffer_ci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
|
.pNext = &external_info,
|
||||||
|
.flags = 0,
|
||||||
|
.size = window_len,
|
||||||
|
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||||
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
|
.queueFamilyIndexCount = 0,
|
||||||
|
.pQueueFamilyIndices = nullptr,
|
||||||
|
};
|
||||||
|
VkBuffer new_buffer{};
|
||||||
|
if (logical.CreateBufferRaw(buffer_ci, &new_buffer) != VK_SUCCESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const VkMemoryRequirements requirements =
|
||||||
|
logical.GetBufferMemoryRequirements(new_buffer);
|
||||||
|
const u32 type_mask = requirements.memoryTypeBits & host_props.memoryTypeBits;
|
||||||
|
if (type_mask == 0 || requirements.size > window_len) {
|
||||||
|
logical.DestroyBufferRaw(new_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto type_index = find_type(type_mask, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||||
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
||||||
|
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
|
||||||
|
if (!type_index) {
|
||||||
|
type_index = find_type(type_mask, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||||
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||||
|
}
|
||||||
|
if (!type_index) {
|
||||||
|
logical.DestroyBufferRaw(new_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const VkImportMemoryHostPointerInfoEXT import_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
|
||||||
|
.pHostPointer = window_base,
|
||||||
|
};
|
||||||
|
const VkMemoryAllocateInfo alloc_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||||
|
.pNext = &import_info,
|
||||||
|
.allocationSize = window_len,
|
||||||
|
.memoryTypeIndex = *type_index,
|
||||||
|
};
|
||||||
|
vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info);
|
||||||
|
if (!memory) {
|
||||||
|
logical.DestroyBufferRaw(new_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
|
||||||
|
logical.DestroyBufferRaw(new_buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
windows.push_back(Window{
|
||||||
|
.memory = std::move(memory),
|
||||||
|
.buffer = new_buffer,
|
||||||
|
});
|
||||||
|
imported_size += static_cast<size_t>(window_len);
|
||||||
}
|
}
|
||||||
if (!type_index) {
|
if (windows.empty()) {
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
LOG_INFO(Render_Vulkan, "Unified memory disabled, host memory import failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const VkImportMemoryHostPointerInfoEXT import_info{
|
LOG_INFO(Render_Vulkan,
|
||||||
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT,
|
"Imported {} MiB of guest memory for unified memory access in {} windows",
|
||||||
.pNext = nullptr,
|
imported_size >> 20, windows.size());
|
||||||
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
|
|
||||||
.pHostPointer = base,
|
|
||||||
};
|
|
||||||
const VkMemoryAllocateInfo alloc_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
||||||
.pNext = &import_info,
|
|
||||||
.allocationSize = size,
|
|
||||||
.memoryTypeIndex = *type_index,
|
|
||||||
};
|
|
||||||
memory = logical.TryAllocateMemory(alloc_info);
|
|
||||||
if (!memory) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
memory = vk::DeviceMemory{};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buffer = new_buffer;
|
|
||||||
imported_size = size;
|
|
||||||
LOG_INFO(Render_Vulkan, "Imported {} MiB of guest memory for unified memory access",
|
|
||||||
size >> 20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HostMemoryImport::~HostMemoryImport() {
|
HostMemoryImport::~HostMemoryImport() {
|
||||||
if (buffer != VK_NULL_HANDLE) {
|
for (Window &window : windows) {
|
||||||
device.GetLogical().DestroyBufferRaw(buffer);
|
if (window.buffer != VK_NULL_HANDLE) {
|
||||||
|
device.GetLogical().DestroyBufferRaw(window.buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,21 +51,34 @@ namespace Vulkan {
|
|||||||
HostMemoryImport &operator=(const HostMemoryImport &) = delete;
|
HostMemoryImport &operator=(const HostMemoryImport &) = delete;
|
||||||
|
|
||||||
[[nodiscard]] bool IsValid() const noexcept {
|
[[nodiscard]] bool IsValid() const noexcept {
|
||||||
return buffer != VK_NULL_HANDLE;
|
return !windows.empty();
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkBuffer GetBuffer() const noexcept {
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] size_t GetSize() const noexcept {
|
[[nodiscard]] size_t GetSize() const noexcept {
|
||||||
return imported_size;
|
return imported_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkDeviceSize GetWindowSize() const noexcept {
|
||||||
|
return window_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkBuffer GetWindowBuffer(size_t index) const noexcept {
|
||||||
|
return windows[index].buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t GetWindowCount() const noexcept {
|
||||||
|
return windows.size();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Window {
|
||||||
|
vk::DeviceMemory memory;
|
||||||
|
VkBuffer buffer{};
|
||||||
|
};
|
||||||
|
|
||||||
const Device &device;
|
const Device &device;
|
||||||
vk::DeviceMemory memory;
|
std::vector<Window> windows;
|
||||||
VkBuffer buffer{};
|
VkDeviceSize window_size{};
|
||||||
size_t imported_size{};
|
size_t imported_size{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user