mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 22:25:41 +12:00
Allocate and present separate top/bottom screen framebuffer images
Instead of operating directly on the swapchain images, we have our own top/bottom framebuffer images that will be rendered to independent of having an available swapchain. The images are blitted into the swapchain images, allowing for resizing too!
This commit is contained in:
parent
d0832ca558
commit
e3699fe8f8
5 changed files with 354 additions and 15 deletions
|
@ -239,11 +239,11 @@ if(ENABLE_VULKAN)
|
|||
)
|
||||
|
||||
set(RENDERER_VK_INCLUDE_FILES include/renderer_vk/renderer_vk.hpp
|
||||
include/renderer_vk/vulkan_api.hpp include/renderer_vk/vk_debug.hpp
|
||||
include/renderer_vk/vulkan_api.hpp include/renderer_vk/vk_debug.hpp include/renderer_vk/vk_memory.hpp
|
||||
)
|
||||
|
||||
set(RENDERER_VK_SOURCE_FILES src/core/renderer_vk/renderer_vk.cpp
|
||||
src/core/renderer_vk/vulkan_api.cpp src/core/renderer_vk/vk_debug.cpp
|
||||
src/core/renderer_vk/vulkan_api.cpp src/core/renderer_vk/vk_debug.cpp src/core/renderer_vk/vk_memory.cpp
|
||||
)
|
||||
|
||||
set(HEADER_FILES ${HEADER_FILES} ${RENDERER_VK_INCLUDE_FILES})
|
||||
|
|
|
@ -37,6 +37,8 @@ class RendererVK final : public Renderer {
|
|||
// Todo: make this a configuration option
|
||||
static constexpr usize frameBufferingCount = 3;
|
||||
|
||||
vk::UniqueDeviceMemory framebufferMemory = {};
|
||||
|
||||
// Frame-buffering data
|
||||
// Each vector is `frameBufferingCount` in size
|
||||
std::vector<vk::UniqueCommandBuffer> frameCommandBuffers = {};
|
||||
|
@ -44,6 +46,9 @@ class RendererVK final : public Renderer {
|
|||
std::vector<vk::UniqueSemaphore> renderFinishedSemaphore = {};
|
||||
std::vector<vk::UniqueFence> frameFinishedFences = {};
|
||||
|
||||
std::vector<vk::UniqueImage> topScreenImages = {};
|
||||
std::vector<vk::UniqueImage> bottomScreenImages = {};
|
||||
|
||||
// Recreate the swapchain, possibly re-using the old one in the case of a resize
|
||||
vk::Result recreateSwapchain(vk::SurfaceKHR surface, vk::Extent2D swapchainExtent);
|
||||
|
||||
|
|
36
include/renderer_vk/vk_memory.hpp
Normal file
36
include/renderer_vk/vk_memory.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "vulkan_api.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
// Will try to find a memory type that is suitable for the given requirements.
|
||||
// Returns -1 if no suitable memory type was found.
|
||||
s32 findMemoryTypeIndex(
|
||||
vk::PhysicalDevice physicalDevice, u32 memoryTypeMask, vk::MemoryPropertyFlags memoryProperties,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties = vk::MemoryPropertyFlagBits::eProtected
|
||||
);
|
||||
|
||||
// Given an array of valid Vulkan image-handles or buffer-handles, these
|
||||
// functions will allocate a single block of device-memory for all of them
|
||||
// and bind them consecutively.
|
||||
// There may be a case that all the buffers or images cannot be allocated
|
||||
// to the same device memory due to their required memory-type.
|
||||
std::tuple<vk::Result, vk::UniqueDeviceMemory> commitImageHeap(
|
||||
vk::Device device, vk::PhysicalDevice physicalDevice, const std::span<const vk::Image> images,
|
||||
vk::MemoryPropertyFlags memoryProperties = vk::MemoryPropertyFlagBits::eDeviceLocal,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties = vk::MemoryPropertyFlagBits::eProtected
|
||||
);
|
||||
|
||||
std::tuple<vk::Result, vk::UniqueDeviceMemory> commitBufferHeap(
|
||||
vk::Device device, vk::PhysicalDevice physicalDevice, const std::span<const vk::Buffer> buffers,
|
||||
vk::MemoryPropertyFlags memoryProperties = vk::MemoryPropertyFlagBits::eDeviceLocal,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties = vk::MemoryPropertyFlagBits::eProtected
|
||||
);
|
||||
|
||||
} // namespace Vulkan
|
|
@ -1,12 +1,14 @@
|
|||
#include "renderer_vk/renderer_vk.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "SDL_vulkan.h"
|
||||
#include "helpers.hpp"
|
||||
#include "renderer_vk/vk_debug.hpp"
|
||||
#include "renderer_vk/vk_memory.hpp"
|
||||
|
||||
// Finds the first queue family that satisfies `queueMask` and excludes `queueExcludeMask` bits
|
||||
// Returns -1 if not found
|
||||
|
@ -221,15 +223,27 @@ void RendererVK::display() {
|
|||
|
||||
Vulkan::DebugLabelScope debugScope(frameCommandBuffer.get(), frameScopeColor, "Frame");
|
||||
|
||||
// Prepare for color-clear
|
||||
if (swapchainImageIndex != swapchainImageInvalid) {
|
||||
// Prepare images for color-clear
|
||||
frameCommandBuffer->pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {}, {},
|
||||
{vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex],
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
)}
|
||||
{
|
||||
vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex],
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
),
|
||||
vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
|
||||
topScreenImages[frameBufferingIndex].get(), vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
),
|
||||
vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
|
||||
bottomScreenImages[frameBufferingIndex].get(), vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
static const std::array<float, 4> clearColor = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
|
@ -238,6 +252,56 @@ void RendererVK::display() {
|
|||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
);
|
||||
|
||||
//// Simulated rendering work, just clear the screens and get them ready to blit(transfer-src layout)
|
||||
{
|
||||
static const std::array<float, 4> topClearColor = {{1.0f, 0.0f, 0.0f, 1.0f}};
|
||||
static const std::array<float, 4> bottomClearColor = {{0.0f, 1.0f, 0.0f, 1.0f}};
|
||||
frameCommandBuffer->clearColorImage(
|
||||
topScreenImages[frameBufferingIndex].get(), vk::ImageLayout::eTransferDstOptimal, topClearColor,
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
);
|
||||
frameCommandBuffer->clearColorImage(
|
||||
bottomScreenImages[frameBufferingIndex].get(), vk::ImageLayout::eTransferDstOptimal, bottomClearColor,
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
);
|
||||
frameCommandBuffer->pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {}, {},
|
||||
{
|
||||
vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eTransferRead, vk::ImageLayout::eTransferDstOptimal,
|
||||
vk::ImageLayout::eTransferSrcOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
|
||||
topScreenImages[frameBufferingIndex].get(), vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
),
|
||||
vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eTransferRead, vk::ImageLayout::eTransferDstOptimal,
|
||||
vk::ImageLayout::eTransferSrcOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
|
||||
bottomScreenImages[frameBufferingIndex].get(), vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Blip top/bottom screen onto swapchain image
|
||||
{
|
||||
static const vk::ImageBlit topScreenBlit(
|
||||
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), {vk::Offset3D{}, vk::Offset3D{400, 240, 1}},
|
||||
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), {vk::Offset3D{}, vk::Offset3D{400, 240, 1}}
|
||||
);
|
||||
static const vk::ImageBlit bottomScreenBlit(
|
||||
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), {vk::Offset3D{}, vk::Offset3D{320, 240, 1}},
|
||||
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1),
|
||||
{vk::Offset3D{(400 / 2) - (320 / 2), 240, 0}, vk::Offset3D{(400 / 2) + (320 / 2), 240 + 240, 1}}
|
||||
);
|
||||
frameCommandBuffer->blitImage(
|
||||
topScreenImages[frameBufferingIndex].get(), vk::ImageLayout::eTransferSrcOptimal, swapchainImages[swapchainImageIndex],
|
||||
vk::ImageLayout::eTransferDstOptimal, {topScreenBlit}, vk::Filter::eNearest
|
||||
);
|
||||
frameCommandBuffer->blitImage(
|
||||
bottomScreenImages[frameBufferingIndex].get(), vk::ImageLayout::eTransferSrcOptimal, swapchainImages[swapchainImageIndex],
|
||||
vk::ImageLayout::eTransferDstOptimal, {bottomScreenBlit}, vk::Filter::eNearest
|
||||
);
|
||||
}
|
||||
|
||||
// Prepare for present
|
||||
frameCommandBuffer->pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::DependencyFlags(), {}, {},
|
||||
|
@ -555,14 +619,74 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
|
|||
swapImageFreeSemaphore.resize(frameBufferingCount);
|
||||
renderFinishedSemaphore.resize(frameBufferingCount);
|
||||
frameFinishedFences.resize(frameBufferingCount);
|
||||
vk::Extent2D swapchainExtent;
|
||||
{
|
||||
int windowWidth, windowHeight;
|
||||
SDL_Vulkan_GetDrawableSize(window, &windowWidth, &windowHeight);
|
||||
swapchainExtent.width = windowWidth;
|
||||
swapchainExtent.height = windowHeight;
|
||||
|
||||
vk::ImageCreateInfo topScreenInfo = {};
|
||||
topScreenInfo.setImageType(vk::ImageType::e2D);
|
||||
topScreenInfo.setFormat(vk::Format::eR8G8B8A8Unorm);
|
||||
topScreenInfo.setExtent(vk::Extent3D(400, 240, 1));
|
||||
topScreenInfo.setMipLevels(1);
|
||||
topScreenInfo.setArrayLayers(2); // Two image layers, for 3D mode
|
||||
topScreenInfo.setSamples(vk::SampleCountFlagBits::e1);
|
||||
topScreenInfo.setTiling(vk::ImageTiling::eOptimal);
|
||||
topScreenInfo.setUsage(
|
||||
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment | vk::ImageUsageFlagBits::eTransferSrc |
|
||||
vk::ImageUsageFlagBits::eTransferDst
|
||||
);
|
||||
topScreenInfo.setSharingMode(vk::SharingMode::eExclusive);
|
||||
topScreenInfo.setInitialLayout(vk::ImageLayout::eUndefined);
|
||||
|
||||
vk::ImageCreateInfo bottomScreenInfo = topScreenInfo;
|
||||
bottomScreenInfo.setExtent(vk::Extent3D(320, 240, 1));
|
||||
bottomScreenInfo.setArrayLayers(1);
|
||||
|
||||
topScreenImages.resize(frameBufferingCount);
|
||||
bottomScreenImages.resize(frameBufferingCount);
|
||||
|
||||
for (usize i = 0; i < frameBufferingCount; i++) {
|
||||
if (auto createResult = device->createSemaphoreUnique(semaphoreInfo); createResult.result == vk::Result::eSuccess) {
|
||||
swapImageFreeSemaphore[i] = std::move(createResult.value);
|
||||
} else {
|
||||
Helpers::panic("Error creating 'present-ready' semaphore: %s\n", vk::to_string(createResult.result).c_str());
|
||||
}
|
||||
|
||||
if (auto createResult = device->createSemaphoreUnique(semaphoreInfo); createResult.result == vk::Result::eSuccess) {
|
||||
renderFinishedSemaphore[i] = std::move(createResult.value);
|
||||
} else {
|
||||
Helpers::panic("Error creating 'post-render' semaphore: %s\n", vk::to_string(createResult.result).c_str());
|
||||
}
|
||||
|
||||
if (auto createResult = device->createFenceUnique(fenceInfo); createResult.result == vk::Result::eSuccess) {
|
||||
frameFinishedFences[i] = std::move(createResult.value);
|
||||
} else {
|
||||
Helpers::panic("Error creating 'present-ready' semaphore: %s\n", vk::to_string(createResult.result).c_str());
|
||||
}
|
||||
|
||||
if (auto createResult = device->createImageUnique(topScreenInfo); createResult.result == vk::Result::eSuccess) {
|
||||
topScreenImages[i] = std::move(createResult.value);
|
||||
} else {
|
||||
Helpers::panic("Error creating top-screen image: %s\n", vk::to_string(createResult.result).c_str());
|
||||
}
|
||||
|
||||
if (auto createResult = device->createImageUnique(bottomScreenInfo); createResult.result == vk::Result::eSuccess) {
|
||||
bottomScreenImages[i] = std::move(createResult.value);
|
||||
} else {
|
||||
Helpers::panic("Error creating bottom-screen image: %s\n", vk::to_string(createResult.result).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Commit memory to all of our images
|
||||
{
|
||||
const auto getImage = [](const vk::UniqueImage& image) -> vk::Image { return image.get(); };
|
||||
std::vector<vk::Image> images;
|
||||
std::ranges::transform(topScreenImages, std::back_inserter(images), getImage);
|
||||
std::ranges::transform(bottomScreenImages, std::back_inserter(images), getImage);
|
||||
|
||||
if (auto [result, imageMemory] = Vulkan::commitImageHeap(device.get(), physicalDevice, images); result == vk::Result::eSuccess) {
|
||||
framebufferMemory = std::move(imageMemory);
|
||||
} else {
|
||||
Helpers::panic("Error allocating framebuffer memory: %s\n", vk::to_string(result).c_str());
|
||||
}
|
||||
}
|
||||
recreateSwapchain(surface.get(), swapchainExtent);
|
||||
}
|
||||
|
||||
void RendererVK::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {}
|
||||
|
|
174
src/core/renderer_vk/vk_memory.cpp
Normal file
174
src/core/renderer_vk/vk_memory.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
#include "renderer_vk/vk_memory.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
static constexpr vk::DeviceSize alignUp(vk::DeviceSize value, std::size_t size) {
|
||||
const vk::DeviceSize mod = static_cast<vk::DeviceSize>(value % size);
|
||||
value -= mod;
|
||||
return static_cast<vk::DeviceSize>(mod == vk::DeviceSize{0} ? value : value + size);
|
||||
}
|
||||
|
||||
// Given a speculative heap-allocation, defined by its current size and
|
||||
// memory-type bits, appends a memory requirements structure to it, updating
|
||||
// both the size and the required memory-type-bits. Returns the offset within
|
||||
// the heap for the current MemoryRequirements Todo: Sun Apr 23 13:28:25 PDT
|
||||
// 2023 Rather than using a running-size of the heap, look at all of the memory
|
||||
// requests and optimally create a packing for all of the offset and alignment
|
||||
// requirements. Such as by satisfying all of the largest alignments first, and
|
||||
// then the smallest, to reduce padding
|
||||
static vk::DeviceSize commitMemoryRequestToHeap(
|
||||
const vk::MemoryRequirements& curMemoryRequirements, vk::DeviceSize& curHeapEnd, u32& curMemoryTypeBits, vk::DeviceSize sizeAlignment
|
||||
) {
|
||||
// Accumulate a mask of all the memory types that satisfies each of the
|
||||
// handles
|
||||
curMemoryTypeBits &= curMemoryRequirements.memoryTypeBits;
|
||||
|
||||
// Pad up the memory sizes so they are not considered aliasing
|
||||
const vk::DeviceSize curMemoryOffset = alignUp(curHeapEnd, curMemoryRequirements.alignment);
|
||||
// Pad the size by the required size-alignment.
|
||||
// Intended for BufferImageGranularity
|
||||
const vk::DeviceSize curMemorySize = alignUp(curMemoryRequirements.size, sizeAlignment);
|
||||
|
||||
curHeapEnd = (curMemoryOffset + curMemorySize);
|
||||
return curMemoryOffset;
|
||||
}
|
||||
|
||||
s32 findMemoryTypeIndex(
|
||||
vk::PhysicalDevice physicalDevice, u32 memoryTypeMask, vk::MemoryPropertyFlags memoryProperties,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties
|
||||
) {
|
||||
const vk::PhysicalDeviceMemoryProperties deviceMemoryProperties = physicalDevice.getMemoryProperties();
|
||||
// Iterate the physical device's memory types until we find a match
|
||||
for (std::size_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++) {
|
||||
if(
|
||||
// Is within memory type mask
|
||||
(((memoryTypeMask >> i) & 0b1) == 0b1) &&
|
||||
// Has property flags
|
||||
(deviceMemoryProperties.memoryTypes[i].propertyFlags
|
||||
& memoryProperties)
|
||||
== memoryProperties
|
||||
&&
|
||||
// None of the excluded properties are enabled
|
||||
!(deviceMemoryProperties.memoryTypes[i].propertyFlags
|
||||
& memoryExcludeProperties) )
|
||||
{
|
||||
return static_cast<u32>(i);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::tuple<vk::Result, vk::UniqueDeviceMemory> commitImageHeap(
|
||||
vk::Device device, vk::PhysicalDevice physicalDevice, const std::span<const vk::Image> images, vk::MemoryPropertyFlags memoryProperties,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties
|
||||
) {
|
||||
vk::MemoryAllocateInfo imageHeapAllocInfo = {};
|
||||
u32 imageHeapMemoryTypeBits = 0xFFFFFFFF;
|
||||
std::vector<vk::BindImageMemoryInfo> imageHeapBinds;
|
||||
|
||||
const vk::DeviceSize bufferImageGranularity = physicalDevice.getProperties().limits.bufferImageGranularity;
|
||||
|
||||
for (const vk::Image& curImage : images) {
|
||||
const vk::DeviceSize curBindOffset = commitMemoryRequestToHeap(
|
||||
device.getImageMemoryRequirements(curImage), imageHeapAllocInfo.allocationSize, imageHeapMemoryTypeBits, bufferImageGranularity
|
||||
);
|
||||
|
||||
if (imageHeapMemoryTypeBits == 0) {
|
||||
// No possible memory heap for all of the images to share
|
||||
return std::make_tuple(vk::Result::eErrorOutOfDeviceMemory, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
// Put nullptr for the device memory for now
|
||||
imageHeapBinds.emplace_back(vk::BindImageMemoryInfo{curImage, nullptr, curBindOffset});
|
||||
}
|
||||
|
||||
const s32 memoryTypeIndex = findMemoryTypeIndex(physicalDevice, imageHeapMemoryTypeBits, memoryProperties, memoryExcludeProperties);
|
||||
|
||||
if (memoryTypeIndex < 0) {
|
||||
// Unable to find a memory heap that satisfies all the images
|
||||
return std::make_tuple(vk::Result::eErrorOutOfDeviceMemory, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
imageHeapAllocInfo.memoryTypeIndex = memoryTypeIndex;
|
||||
|
||||
vk::UniqueDeviceMemory imageHeapMemory = {};
|
||||
|
||||
if (auto allocResult = device.allocateMemoryUnique(imageHeapAllocInfo); allocResult.result == vk::Result::eSuccess) {
|
||||
imageHeapMemory = std::move(allocResult.value);
|
||||
} else {
|
||||
return std::make_tuple(allocResult.result, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
// Assign the device memory to the bindings
|
||||
for (vk::BindImageMemoryInfo& curBind : imageHeapBinds) {
|
||||
curBind.memory = imageHeapMemory.get();
|
||||
}
|
||||
|
||||
// Now bind them all in one call
|
||||
if (const vk::Result bindResult = device.bindImageMemory2(imageHeapBinds); bindResult == vk::Result::eSuccess) {
|
||||
// Binding memory succeeded
|
||||
} else {
|
||||
return std::make_tuple(bindResult, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
return std::make_tuple(vk::Result::eSuccess, std::move(imageHeapMemory));
|
||||
}
|
||||
|
||||
std::tuple<vk::Result, vk::UniqueDeviceMemory> commitBufferHeap(
|
||||
vk::Device device, vk::PhysicalDevice physicalDevice, const std::span<const vk::Buffer> buffers, vk::MemoryPropertyFlags memoryProperties,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties
|
||||
) {
|
||||
vk::MemoryAllocateInfo bufferHeapAllocInfo = {};
|
||||
u32 bufferHeapMemoryTypeBits = 0xFFFFFFFF;
|
||||
std::vector<vk::BindBufferMemoryInfo> bufferHeapBinds;
|
||||
|
||||
const vk::DeviceSize bufferImageGranularity = physicalDevice.getProperties().limits.bufferImageGranularity;
|
||||
|
||||
for (const vk::Buffer& curBuffer : buffers) {
|
||||
const vk::DeviceSize curBindOffset = commitMemoryRequestToHeap(
|
||||
device.getBufferMemoryRequirements(curBuffer), bufferHeapAllocInfo.allocationSize, bufferHeapMemoryTypeBits, bufferImageGranularity
|
||||
);
|
||||
|
||||
if (bufferHeapMemoryTypeBits == 0) {
|
||||
// No possible memory heap for all of the buffers to share
|
||||
return std::make_tuple(vk::Result::eErrorOutOfDeviceMemory, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
// Put nullptr for the device memory for now
|
||||
bufferHeapBinds.emplace_back(vk::BindBufferMemoryInfo{curBuffer, nullptr, curBindOffset});
|
||||
}
|
||||
|
||||
const s32 memoryTypeIndex = findMemoryTypeIndex(physicalDevice, bufferHeapMemoryTypeBits, memoryProperties, memoryExcludeProperties);
|
||||
|
||||
if (memoryTypeIndex < 0) {
|
||||
// Unable to find a memory heap that satisfies all the buffers
|
||||
return std::make_tuple(vk::Result::eErrorOutOfDeviceMemory, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
bufferHeapAllocInfo.memoryTypeIndex = memoryTypeIndex;
|
||||
|
||||
vk::UniqueDeviceMemory bufferHeapMemory = {};
|
||||
|
||||
if (auto allocResult = device.allocateMemoryUnique(bufferHeapAllocInfo); allocResult.result == vk::Result::eSuccess) {
|
||||
bufferHeapMemory = std::move(allocResult.value);
|
||||
} else {
|
||||
return std::make_tuple(allocResult.result, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
// Assign the device memory to the bindings
|
||||
for (vk::BindBufferMemoryInfo& curBind : bufferHeapBinds) {
|
||||
curBind.memory = bufferHeapMemory.get();
|
||||
}
|
||||
|
||||
// Now bind them all in one call
|
||||
if (const vk::Result bindResult = device.bindBufferMemory2(bufferHeapBinds); bindResult == vk::Result::eSuccess) {
|
||||
// Binding memory succeeded
|
||||
} else {
|
||||
return std::make_tuple(bindResult, vk::UniqueDeviceMemory());
|
||||
}
|
||||
|
||||
return std::make_tuple(vk::Result::eSuccess, std::move(bufferHeapMemory));
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
Loading…
Add table
Reference in a new issue