skyline/app/src/main/cpp/skyline/gpu/memory_manager.cpp
PixelyIon 216e5cee81 Separate Guest and Host Presentation + AChoreographer V-Sync Event
We had issues when combining host and guest presentation since certain configurations in guest presentation such as double buffering were very unoptimal for the host and would significantly affect the FPS. As a result of this, we've now made host presentation have its own presentation textures which are copied into from the guest at presentation time, allowing us to change parameters of the host presentation independently of the guest.

We've implemented the infrastructure for this which includes being able to create images from host GPU memory using VMA, an optimized linear texture sync and a method to do on-GPU texture-to-texture copies.

We've also moved to driving the V-Sync event using AChoreographer on its on thread in this PR, which more accurately encapsulates HOS behavior and allows games such as ARMS to boot as they depend on the V-Sync event being signalled even when the game isn't presenting.
2021-07-12 21:27:49 +05:30

126 lines
5.7 KiB
C++

// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <gpu.h>
#include "memory_manager.h"
namespace skyline::gpu::memory {
/**
* @brief If the result isn't VK_SUCCESS then an exception is thrown
*/
void ThrowOnFail(VkResult result, const char *function = __builtin_FUNCTION()) {
if (result != VK_SUCCESS)
vk::throwResultException(vk::Result(result), function);
}
StagingBuffer::~StagingBuffer() {
if (vmaAllocator && vmaAllocation && vkBuffer)
vmaDestroyBuffer(vmaAllocator, vkBuffer, vmaAllocation);
}
Image::~Image() {
if (vmaAllocator && vmaAllocation && vkImage) {
if (pointer)
vmaUnmapMemory(vmaAllocator, vmaAllocation);
vmaDestroyImage(vmaAllocator, vkImage, vmaAllocation);
}
}
u8 *Image::data() {
if (pointer) [[likely]]
return pointer;
ThrowOnFail(vmaMapMemory(vmaAllocator, vmaAllocation, reinterpret_cast<void **>(&pointer)));
return pointer;
}
MemoryManager::MemoryManager(const GPU &pGpu) : gpu(pGpu) {
auto dispatcher{gpu.vkDevice.getDispatcher()};
VmaVulkanFunctions vulkanFunctions{
.vkGetPhysicalDeviceProperties = dispatcher->vkGetPhysicalDeviceProperties,
.vkGetPhysicalDeviceMemoryProperties = dispatcher->vkGetPhysicalDeviceMemoryProperties,
.vkAllocateMemory = dispatcher->vkAllocateMemory,
.vkFreeMemory = dispatcher->vkFreeMemory,
.vkMapMemory = dispatcher->vkMapMemory,
.vkUnmapMemory = dispatcher->vkUnmapMemory,
.vkFlushMappedMemoryRanges = dispatcher->vkFlushMappedMemoryRanges,
.vkInvalidateMappedMemoryRanges = dispatcher->vkInvalidateMappedMemoryRanges,
.vkBindBufferMemory = dispatcher->vkBindBufferMemory,
.vkBindImageMemory = dispatcher->vkBindImageMemory,
.vkGetBufferMemoryRequirements = dispatcher->vkGetBufferMemoryRequirements,
.vkGetImageMemoryRequirements = dispatcher->vkGetImageMemoryRequirements,
.vkCreateBuffer = dispatcher->vkCreateBuffer,
.vkDestroyBuffer = dispatcher->vkDestroyBuffer,
.vkCreateImage = dispatcher->vkCreateImage,
.vkDestroyImage = dispatcher->vkDestroyImage,
.vkCmdCopyBuffer = dispatcher->vkCmdCopyBuffer,
.vkGetBufferMemoryRequirements2KHR = dispatcher->vkGetBufferMemoryRequirements2,
.vkGetImageMemoryRequirements2KHR = dispatcher->vkGetImageMemoryRequirements2,
.vkBindBufferMemory2KHR = dispatcher->vkBindBufferMemory2,
.vkBindImageMemory2KHR = dispatcher->vkBindImageMemory2,
.vkGetPhysicalDeviceMemoryProperties2KHR = dispatcher->vkGetPhysicalDeviceMemoryProperties2,
};
VmaAllocatorCreateInfo allocatorCreateInfo{
.physicalDevice = *gpu.vkPhysicalDevice,
.device = *gpu.vkDevice,
.instance = *gpu.vkInstance,
.pVulkanFunctions = &vulkanFunctions,
.vulkanApiVersion = GPU::VkApiVersion,
};
ThrowOnFail(vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator));
// TODO: Use VK_KHR_dedicated_allocation when available (Should be on Adreno GPUs)
}
MemoryManager::~MemoryManager() {
vmaDestroyAllocator(vmaAllocator);
}
std::shared_ptr<StagingBuffer> MemoryManager::AllocateStagingBuffer(vk::DeviceSize size) {
vk::BufferCreateInfo bufferCreateInfo{
.size = size,
.usage = vk::BufferUsageFlagBits::eTransferSrc,
.sharingMode = vk::SharingMode::eExclusive,
.queueFamilyIndexCount = 1,
.pQueueFamilyIndices = &gpu.vkQueueFamilyIndex,
};
VmaAllocationCreateInfo allocationCreateInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = VMA_MEMORY_USAGE_CPU_ONLY,
};
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo allocationInfo;
ThrowOnFail(vmaCreateBuffer(vmaAllocator, &static_cast<const VkBufferCreateInfo &>(bufferCreateInfo), &allocationCreateInfo, &buffer, &allocation, &allocationInfo));
return std::make_shared<memory::StagingBuffer>(reinterpret_cast<u8 *>(allocationInfo.pMappedData), allocationInfo.size, vmaAllocator, buffer, allocation);
}
Image MemoryManager::AllocateImage(const vk::ImageCreateInfo &createInfo) {
VmaAllocationCreateInfo allocationCreateInfo{
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
};
VkImage image;
VmaAllocation allocation;
VmaAllocationInfo allocationInfo;
ThrowOnFail(vmaCreateImage(vmaAllocator, &static_cast<const VkImageCreateInfo &>(createInfo), &allocationCreateInfo, &image, &allocation, &allocationInfo));
return Image(vmaAllocator, image, allocation);
}
Image MemoryManager::AllocateMappedImage(const vk::ImageCreateInfo &createInfo) {
VmaAllocationCreateInfo allocationCreateInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = VMA_MEMORY_USAGE_UNKNOWN,
.memoryTypeBits = static_cast<u32>(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eDeviceLocal),
};
VkImage image;
VmaAllocation allocation;
VmaAllocationInfo allocationInfo;
ThrowOnFail(vmaCreateImage(vmaAllocator, &static_cast<const VkImageCreateInfo &>(createInfo), &allocationCreateInfo, &image, &allocation, &allocationInfo));
return Image(reinterpret_cast<u8 *>(allocationInfo.pMappedData), vmaAllocator, image, allocation);
}
}