Merge 37c3672f14
into 848fd4b34c
This commit is contained in:
commit
e3f4d5ac91
11 changed files with 563 additions and 66 deletions
|
@ -1,4 +1,6 @@
|
|||
# SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project / Eden Emulator Project
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# SPDX-FileCopyrightText: 2023 EDEN Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
@ -90,8 +92,6 @@ if (ANDROID OR WIN32 OR APPLE)
|
|||
# - macOS defaults to the SecureTransport backend.
|
||||
# - Android currently has no SSL backend as the NDK doesn't include any SSL
|
||||
# library; a proper 'native' backend would have to go through Java.
|
||||
# But you can force builds for those platforms to use OpenSSL if you have
|
||||
# your own copy of it.
|
||||
set(DEFAULT_ENABLE_OPENSSL OFF)
|
||||
endif()
|
||||
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
|
||||
|
@ -651,3 +651,8 @@ if(ENABLE_QT AND UNIX AND NOT APPLE)
|
|||
install(FILES "dist/org.yuzu_emu.yuzu.metainfo.xml"
|
||||
DESTINATION "share/metainfo")
|
||||
endif()
|
||||
|
||||
target_include_directories(video_core PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src/video_core/vulkan_common
|
||||
)
|
||||
target_link_libraries(video_core PRIVATE Vulkan::Vulkan)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# SPDX-FileCopyrightText: 2025 EDEN Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Enable modules to include each other's files
|
||||
include_directories(.)
|
||||
|
@ -193,6 +195,15 @@ add_subdirectory(input_common)
|
|||
add_subdirectory(frontend_common)
|
||||
add_subdirectory(shader_recompiler)
|
||||
|
||||
# Ensure the Vulkan common directory is included
|
||||
target_include_directories(video_core PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src/video_core/vulkan_common
|
||||
)
|
||||
|
||||
# Link Vulkan library
|
||||
find_package(Vulkan REQUIRED)
|
||||
target_link_libraries(video_core PRIVATE Vulkan::Vulkan)
|
||||
|
||||
if (YUZU_ROOM)
|
||||
add_subdirectory(dedicated_room)
|
||||
endif()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
@ -35,6 +37,7 @@
|
|||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_surface.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include "video_core/vulkan_common/vulkan_raii.h"
|
||||
|
||||
namespace Vulkan {
|
||||
namespace {
|
||||
|
@ -106,7 +109,9 @@ RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& emu_window,
|
|||
debug_messenger(Settings::values.renderer_debug ? CreateDebugUtilsCallback(instance)
|
||||
: vk::DebugUtilsMessenger{}),
|
||||
surface(CreateSurface(instance, render_window.GetWindowInfo())),
|
||||
device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(),
|
||||
device(CreateDevice(instance, dld, *surface)),
|
||||
memory_allocator(device),
|
||||
state_tracker(),
|
||||
scheduler(device, state_tracker),
|
||||
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
|
||||
render_window.GetFramebufferLayout().height),
|
||||
|
@ -257,10 +262,10 @@ std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() {
|
|||
void RendererVulkan::RenderAppletCaptureLayer(
|
||||
std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||
if (!applet_frame.image) {
|
||||
applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat);
|
||||
applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat);
|
||||
applet_frame.framebuffer = blit_applet.CreateFramebuffer(
|
||||
VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat);
|
||||
applet_frame.image = std::make_unique<VulkanImage>(device, CaptureImageSize, CaptureFormat);
|
||||
applet_frame.image_view = std::make_unique<VulkanImageView>(device, applet_frame.image->Get(), CaptureFormat);
|
||||
applet_frame.framebuffer = std::make_unique<VulkanFramebuffer>(
|
||||
blit_applet.CreateFramebuffer(VideoCore::Capture::Layout, *applet_frame.image_view, CaptureFormat));
|
||||
}
|
||||
|
||||
blit_applet.DrawToFrame(rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
|
||||
#include <adrenotools/driver.h>
|
||||
|
@ -11,6 +13,8 @@
|
|||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
#include "video_core/renderer_vulkan/vk_turbo_mode.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_raii.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
|
@ -40,18 +44,14 @@ void TurboMode::Run(std::stop_token stop_token) {
|
|||
#ifndef ANDROID
|
||||
auto& dld = m_device.GetLogical();
|
||||
|
||||
// Allocate buffer. 2MiB should be sufficient.
|
||||
// Allocate buffer using RAII wrapper
|
||||
const VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = 2_MiB,
|
||||
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
};
|
||||
vk::Buffer buffer = m_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
||||
VulkanBuffer buffer = m_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
||||
|
||||
// Create the descriptor pool to contain our descriptor.
|
||||
static constexpr VkDescriptorPoolSize pool_size{
|
||||
|
@ -59,10 +59,9 @@ void TurboMode::Run(std::stop_token stop_token) {
|
|||
.descriptorCount = 1,
|
||||
};
|
||||
|
||||
auto descriptor_pool = dld.CreateDescriptorPool(VkDescriptorPoolCreateInfo{
|
||||
// Create descriptor pool using RAII wrapper
|
||||
VulkanDescriptorPool descriptor_pool = dld.CreateDescriptorPool(VkDescriptorPoolCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.maxSets = 1,
|
||||
.poolSizeCount = 1,
|
||||
.pPoolSizes = &pool_size,
|
||||
|
@ -77,7 +76,7 @@ void TurboMode::Run(std::stop_token stop_token) {
|
|||
.pImmutableSamplers = nullptr,
|
||||
};
|
||||
|
||||
auto descriptor_set_layout = dld.CreateDescriptorSetLayout(VkDescriptorSetLayoutCreateInfo{
|
||||
VulkanDescriptorSetLayout descriptor_set_layout = dld.CreateDescriptorSetLayout(VkDescriptorSetLayoutCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
|
@ -86,7 +85,7 @@ void TurboMode::Run(std::stop_token stop_token) {
|
|||
});
|
||||
|
||||
// Actually create the descriptor set.
|
||||
auto descriptor_set = descriptor_pool.Allocate(VkDescriptorSetAllocateInfo{
|
||||
VulkanDescriptorSet descriptor_set = descriptor_pool.AllocateDescriptorSet(VkDescriptorSetAllocateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.descriptorPool = *descriptor_pool,
|
||||
|
@ -97,15 +96,11 @@ void TurboMode::Run(std::stop_token stop_token) {
|
|||
// Create the shader.
|
||||
auto shader = BuildShader(m_device, VULKAN_TURBO_MODE_COMP_SPV);
|
||||
|
||||
// Create the pipeline layout.
|
||||
auto pipeline_layout = dld.CreatePipelineLayout(VkPipelineLayoutCreateInfo{
|
||||
// Create pipeline layout using RAII wrapper
|
||||
VulkanPipelineLayout pipeline_layout = dld.CreatePipelineLayout(VkPipelineLayoutCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = descriptor_set_layout.address(),
|
||||
.pushConstantRangeCount = 0,
|
||||
.pPushConstantRanges = nullptr,
|
||||
});
|
||||
|
||||
// Actually create the pipeline.
|
||||
|
@ -119,7 +114,7 @@ void TurboMode::Run(std::stop_token stop_token) {
|
|||
.pSpecializationInfo = nullptr,
|
||||
};
|
||||
|
||||
auto pipeline = dld.CreateComputePipeline(VkComputePipelineCreateInfo{
|
||||
VulkanPipeline pipeline = dld.CreateComputePipeline(VkComputePipelineCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
|
@ -130,24 +125,22 @@ void TurboMode::Run(std::stop_token stop_token) {
|
|||
});
|
||||
|
||||
// Create a fence to wait on.
|
||||
auto fence = dld.CreateFence(VkFenceCreateInfo{
|
||||
VulkanFence fence = dld.CreateFence(VkFenceCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
});
|
||||
|
||||
// Create a command pool to allocate a command buffer from.
|
||||
auto command_pool = dld.CreateCommandPool(VkCommandPoolCreateInfo{
|
||||
// Create command pool using RAII wrapper
|
||||
VulkanCommandPool command_pool = dld.CreateCommandPool(VkCommandPoolCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags =
|
||||
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = m_device.GetGraphicsFamily(),
|
||||
});
|
||||
|
||||
// Create a single command buffer.
|
||||
auto cmdbufs = command_pool.Allocate(1, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
|
||||
auto cmdbuf = vk::CommandBuffer{cmdbufs[0], m_device.GetDispatchLoader()};
|
||||
// Allocate command buffer using RAII wrapper
|
||||
VulkanCommandBuffer cmdbuf = command_pool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
|
||||
#endif
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
@ -18,6 +20,7 @@
|
|||
#include "video_core/vulkan_common/vma.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include "video_core/vulkan_common/vulkan_raii.h"
|
||||
|
||||
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
|
||||
#include <adrenotools/bcenabler.h>
|
||||
|
@ -724,11 +727,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||
dynamic_state3_enables = false;
|
||||
}
|
||||
|
||||
logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions),
|
||||
VulkanLogicalDevice logical_device(physical, queue_cis, ExtensionListForVulkan(loaded_extensions),
|
||||
first_next, dld);
|
||||
|
||||
graphics_queue = logical.GetQueue(graphics_family);
|
||||
present_queue = logical.GetQueue(present_family);
|
||||
graphics_queue = logical_device.Get().GetQueue(graphics_family);
|
||||
present_queue = logical_device.Get().GetQueue(present_family);
|
||||
|
||||
VmaVulkanFunctions functions{};
|
||||
functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr;
|
||||
|
@ -748,11 +750,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||
.pTypeExternalMemoryHandleTypes = nullptr,
|
||||
};
|
||||
|
||||
vk::Check(vmaCreateAllocator(&allocator_info, &allocator));
|
||||
VulkanMemoryAllocator memory_allocator(allocator_info);
|
||||
}
|
||||
|
||||
Device::~Device() {
|
||||
vmaDestroyAllocator(allocator);
|
||||
vmaDestroyAllocator(memory_allocator.Get());
|
||||
}
|
||||
|
||||
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// // SPDX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// // SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
|
@ -13,6 +15,7 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include "video_core/vulkan_common/vulkan_raii.h"
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocator)
|
||||
|
||||
|
@ -191,8 +194,76 @@ enum class NvidiaArchitecture {
|
|||
class Device {
|
||||
public:
|
||||
explicit Device(VkInstance instance, vk::PhysicalDevice physical, VkSurfaceKHR surface,
|
||||
const vk::InstanceDispatch& dld);
|
||||
~Device();
|
||||
const vk::InstanceDispatch& dld)
|
||||
: instance(instance), physical(physical), dld(dld) {
|
||||
// Create logical device
|
||||
VkDeviceCreateInfo device_info = {};
|
||||
// ... populate device_info ...
|
||||
if (vkCreateDevice(physical, &device_info, nullptr, &logical) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create Vulkan logical device");
|
||||
}
|
||||
|
||||
// Create memory allocator
|
||||
VmaAllocatorCreateInfo allocator_info = {};
|
||||
allocator_info.physicalDevice = physical;
|
||||
allocator_info.device = logical;
|
||||
allocator_info.instance = instance;
|
||||
if (vmaCreateAllocator(&allocator_info, &allocator) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create Vulkan memory allocator");
|
||||
}
|
||||
}
|
||||
|
||||
~Device() {
|
||||
if (allocator) {
|
||||
vmaDestroyAllocator(allocator);
|
||||
}
|
||||
if (logical) {
|
||||
vkDestroyDevice(logical, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
Device(Device&& other) noexcept
|
||||
: instance(other.instance),
|
||||
allocator(other.allocator),
|
||||
dld(std::move(other.dld)),
|
||||
physical(other.physical),
|
||||
logical(other.logical),
|
||||
graphics_queue(other.graphics_queue),
|
||||
present_queue(other.present_queue) {
|
||||
other.allocator = nullptr;
|
||||
other.logical = nullptr;
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
Device& operator=(Device&& other) noexcept {
|
||||
if (this != &other) {
|
||||
// Clean up existing resources
|
||||
if (allocator) {
|
||||
vmaDestroyAllocator(allocator);
|
||||
}
|
||||
if (logical) {
|
||||
vkDestroyDevice(logical, nullptr);
|
||||
}
|
||||
|
||||
// Transfer ownership
|
||||
instance = other.instance;
|
||||
allocator = other.allocator;
|
||||
dld = std::move(other.dld);
|
||||
physical = other.physical;
|
||||
logical = other.logical;
|
||||
graphics_queue = other.graphics_queue;
|
||||
present_queue = other.present_queue;
|
||||
|
||||
other.allocator = nullptr;
|
||||
other.logical = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Deleted copy constructor and assignment operator
|
||||
Device(const Device&) = delete;
|
||||
Device& operator=(const Device&) = delete;
|
||||
|
||||
/**
|
||||
* Returns a format supported by the device for the passed requirements.
|
||||
|
@ -744,10 +815,10 @@ private:
|
|||
|
||||
private:
|
||||
VkInstance instance; ///< Vulkan instance.
|
||||
VmaAllocator allocator; ///< VMA allocator.
|
||||
VmaAllocator allocator = nullptr; ///< VMA allocator.
|
||||
vk::DeviceDispatch dld; ///< Device function pointers.
|
||||
vk::PhysicalDevice physical; ///< Physical device.
|
||||
vk::Device logical; ///< Logical device.
|
||||
vk::Device logical = nullptr; ///< Logical device.
|
||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||
vk::Queue present_queue; ///< Main present queue.
|
||||
u32 instance_version{}; ///< Vulkan instance version.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <future>
|
||||
#include <optional>
|
||||
|
@ -111,7 +113,7 @@ void RemoveUnavailableLayers(const vk::InstanceDispatch& dld, std::vector<const
|
|||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
|
||||
VulkanInstance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
|
||||
u32 required_version, Core::Frontend::WindowSystemType window_type,
|
||||
bool enable_validation) {
|
||||
if (!library.IsOpen()) {
|
||||
|
@ -129,6 +131,10 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
|
|||
const std::vector<const char*> extensions =
|
||||
RequiredExtensions(dld, window_type, enable_validation);
|
||||
if (!AreExtensionsSupported(dld, extensions)) {
|
||||
LOG_ERROR(Render_Vulkan, "Required extensions are not supported:");
|
||||
for (const char* extension : extensions) {
|
||||
LOG_ERROR(Render_Vulkan, " - {}", extension);
|
||||
}
|
||||
throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
|
||||
}
|
||||
std::vector<const char*> layers = Layers(enable_validation);
|
||||
|
@ -149,7 +155,7 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
|
|||
LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
|
||||
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
|
||||
}
|
||||
return instance;
|
||||
return VulkanInstance(instance, dld);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SDPX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
|
@ -16,6 +18,7 @@
|
|||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include "video_core/vulkan_common/vulkan_raii.h"
|
||||
|
||||
namespace Vulkan {
|
||||
namespace {
|
||||
|
@ -230,7 +233,11 @@ MemoryAllocator::MemoryAllocator(const Device& device_)
|
|||
|
||||
MemoryAllocator::~MemoryAllocator() = default;
|
||||
|
||||
vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
|
||||
VulkanImage MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
|
||||
if (ci.extent.width == 0 || ci.extent.height == 0) {
|
||||
throw std::invalid_argument("Image extent must have non-zero width and height");
|
||||
}
|
||||
|
||||
const VmaAllocationCreateInfo alloc_ci = {
|
||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
|
||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
|
||||
|
@ -244,14 +251,16 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const {
|
|||
|
||||
VkImage handle{};
|
||||
VmaAllocation allocation{};
|
||||
|
||||
vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr));
|
||||
|
||||
return vk::Image(handle, ci.usage, *device.GetLogical(), allocator, allocation,
|
||||
device.GetDispatchLoader());
|
||||
return VulkanImage(device, handle, allocation, ci.extent);
|
||||
}
|
||||
|
||||
VulkanBuffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const {
|
||||
if (ci.size == 0) {
|
||||
throw std::invalid_argument("Buffer size must be greater than 0");
|
||||
}
|
||||
|
||||
vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const {
|
||||
const VmaAllocationCreateInfo alloc_ci = {
|
||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
|
||||
.usage = MemoryUsageVma(usage),
|
||||
|
@ -264,19 +273,10 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsa
|
|||
};
|
||||
|
||||
VkBuffer handle{};
|
||||
VmaAllocationInfo alloc_info{};
|
||||
VmaAllocation allocation{};
|
||||
VkMemoryPropertyFlags property_flags{};
|
||||
vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr));
|
||||
|
||||
vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info));
|
||||
vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags);
|
||||
|
||||
u8* data = reinterpret_cast<u8*>(alloc_info.pMappedData);
|
||||
const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{};
|
||||
const bool is_coherent = property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
|
||||
return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent,
|
||||
device.GetDispatchLoader());
|
||||
return VulkanBuffer(device, handle, allocation, ci.size);
|
||||
}
|
||||
|
||||
MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SDPX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
|
@ -9,6 +11,7 @@
|
|||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include "video_core/vulkan_common/vulkan_raii.h"
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocator)
|
||||
|
||||
|
@ -91,7 +94,8 @@ public:
|
|||
*
|
||||
* @throw vk::Exception on failure
|
||||
*/
|
||||
explicit MemoryAllocator(const Device& device_);
|
||||
explicit MemoryAllocator(const Device& device_)
|
||||
: device(device_), allocator(device_.GetMemoryAllocator()) {}
|
||||
~MemoryAllocator();
|
||||
|
||||
MemoryAllocator& operator=(const MemoryAllocator&) = delete;
|
||||
|
@ -99,8 +103,54 @@ public:
|
|||
|
||||
vk::Image CreateImage(const VkImageCreateInfo& ci) const;
|
||||
|
||||
VulkanImage CreateImage(const VkImageCreateInfo& ci) const {
|
||||
if (ci.extent.width == 0 || ci.extent.height == 0) {
|
||||
throw std::invalid_argument("Image extent must have non-zero width and height");
|
||||
}
|
||||
|
||||
const VmaAllocationCreateInfo alloc_ci = {
|
||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT,
|
||||
.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE,
|
||||
.requiredFlags = 0,
|
||||
.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
.memoryTypeBits = 0,
|
||||
.pool = VK_NULL_HANDLE,
|
||||
.pUserData = nullptr,
|
||||
.priority = 0.f,
|
||||
};
|
||||
|
||||
VkImage handle{};
|
||||
VmaAllocation allocation{};
|
||||
vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr));
|
||||
|
||||
return VulkanImage(device, handle, allocation, ci.extent);
|
||||
}
|
||||
|
||||
vk::Buffer CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const;
|
||||
|
||||
VulkanBuffer CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const {
|
||||
if (ci.size == 0) {
|
||||
throw std::invalid_argument("Buffer size must be greater than 0");
|
||||
}
|
||||
|
||||
const VmaAllocationCreateInfo alloc_ci = {
|
||||
.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage),
|
||||
.usage = MemoryUsageVma(usage),
|
||||
.requiredFlags = 0,
|
||||
.preferredFlags = MemoryUsagePreferredVmaFlags(usage),
|
||||
.memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types,
|
||||
.pool = VK_NULL_HANDLE,
|
||||
.pUserData = nullptr,
|
||||
.priority = 0.f,
|
||||
};
|
||||
|
||||
VkBuffer handle{};
|
||||
VmaAllocation allocation{};
|
||||
vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr));
|
||||
|
||||
return VulkanBuffer(device, handle, allocation, ci.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a memory with the specified requirements.
|
||||
*
|
||||
|
|
342
src/video_core/vulkan_common/vulkan_raii.h
Normal file
342
src/video_core/vulkan_common/vulkan_raii.h
Normal file
|
@ -0,0 +1,342 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan.h"
|
||||
#include "video_core/vulkan_common/vma.h"
|
||||
|
||||
// VulkanBuffer is an RAII wrapper for a Vulkan buffer
|
||||
class VulkanBuffer {
|
||||
public:
|
||||
VulkanBuffer(const Device& device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties)
|
||||
: device_(device.GetLogical()) {
|
||||
if (size == 0) {
|
||||
throw std::invalid_argument("Buffer size must be greater than 0");
|
||||
}
|
||||
|
||||
// Create Vulkan buffer
|
||||
VkBufferCreateInfo buffer_info{};
|
||||
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
buffer_info.size = size;
|
||||
buffer_info.usage = usage;
|
||||
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
|
||||
if (vkCreateBuffer(device_, &buffer_info, nullptr, &buffer_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create Vulkan buffer");
|
||||
}
|
||||
|
||||
// Allocate memory for the buffer
|
||||
VkMemoryRequirements mem_requirements;
|
||||
vkGetBufferMemoryRequirements(device_, buffer_, &mem_requirements);
|
||||
|
||||
VkMemoryAllocateInfo alloc_info{};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
alloc_info.allocationSize = mem_requirements.size;
|
||||
alloc_info.memoryTypeIndex = device.FindMemoryType(mem_requirements.memoryTypeBits, properties);
|
||||
|
||||
if (vkAllocateMemory(device_, &alloc_info, nullptr, &buffer_memory_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to allocate Vulkan buffer memory");
|
||||
}
|
||||
|
||||
if Vulkan::vk::DeviceDispatch::vkBindBufferMemory(device_, buffer_, buffer_memory_, 0) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to bind Vulkan buffer memory");
|
||||
}
|
||||
}
|
||||
|
||||
~VulkanBuffer() {
|
||||
if (buffer_) {
|
||||
vkDestroyBuffer(device_, buffer_, nullptr);
|
||||
}
|
||||
if (buffer_memory_) {
|
||||
vkFreeMemory(device_, buffer_memory_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanBuffer(const VulkanBuffer&) = delete; // Copy semantics are disabled
|
||||
VulkanBuffer& operator=(const VulkanBuffer&) = delete;
|
||||
|
||||
// Move Constructor
|
||||
VulkanBuffer(VulkanBuffer&& other) noexcept
|
||||
: device_(other.device_), buffer_(other.buffer_), buffer_memory_(other.buffer_memory_) {
|
||||
other.buffer_ = VK_NULL_HANDLE;
|
||||
other.buffer_memory_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Move Assignment Operator
|
||||
VulkanBuffer& operator=(VulkanBuffer&& other) noexcept {
|
||||
if (this != &other) { // Check for self-assignment
|
||||
// Clean up existing resources
|
||||
if (buffer_) {
|
||||
vkDestroyBuffer(device_, buffer_, nullptr);
|
||||
}
|
||||
if (buffer_memory_) {
|
||||
vkFreeMemory(device_, buffer_memory_, nullptr);
|
||||
}
|
||||
|
||||
// Move resources
|
||||
device_ = other.device_;
|
||||
buffer_ = other.buffer_;
|
||||
buffer_memory_ = other.buffer_memory_;
|
||||
|
||||
other.buffer_ = VK_NULL_HANDLE;
|
||||
other.buffer_memory_ = VK_NULL_HANDLE;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
VkBuffer Get() const { return buffer_; }
|
||||
|
||||
private:
|
||||
VkDevice device_;
|
||||
VkBuffer buffer_ = VK_NULL_HANDLE;
|
||||
VkDeviceMemory buffer_memory_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
class VulkanImage {
|
||||
public:
|
||||
VulkanImage(const Device& device, VkExtent2D extent, VkFormat format, VkImageUsageFlags usage)
|
||||
: device_(device.GetLogical()) {
|
||||
// Create Vulkan image
|
||||
VkImageCreateInfo image_info{};
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.pNext = nullptr;
|
||||
image_info.flags = 0; // Default to no flags
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.extent = {extent.width, extent.height, 1};
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.format = format;
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image_info.usage = usage;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_info.queueFamilyIndexCount = 0;
|
||||
image_info.pQueueFamilyIndices = nullptr;
|
||||
|
||||
if (vkCreateImage(device_, &image_info, nullptr, &image_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create Vulkan image");
|
||||
}
|
||||
|
||||
// Allocate memory for the image
|
||||
VkMemoryRequirements mem_requirements;
|
||||
vkGetImageMemoryRequirements(device_, image_, &mem_requirements);
|
||||
|
||||
VkMemoryAllocateInfo alloc_info{};
|
||||
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
alloc_info.allocationSize = mem_requirements.size;
|
||||
alloc_info.memoryTypeIndex = device.FindMemoryType(mem_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
if (vkAllocateMemory(device_, &alloc_info, nullptr, &image_memory_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to allocate Vulkan image memory");
|
||||
}
|
||||
|
||||
if (vkBindImageMemory(device_, image_, image_memory_, 0) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to bind Vulkan image memory");
|
||||
}
|
||||
}
|
||||
|
||||
~VulkanImage() {
|
||||
if (image_) {
|
||||
vkDestroyImage(device_, image_, nullptr);
|
||||
}
|
||||
if (image_memory_) {
|
||||
vkFreeMemory(device_, image_memory_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VkImage Get() const { return image_; }
|
||||
|
||||
private:
|
||||
VkDevice device_;
|
||||
VkImage image_ = VK_NULL_HANDLE;
|
||||
VkDeviceMemory image_memory_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
typedef struct VkBufferCreateInfo {
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkBufferCreateFlags flags;
|
||||
VkDeviceSize size;
|
||||
VkBufferUsageFlags usage;
|
||||
VkSharingMode sharingMode;
|
||||
uint32_t queueFamilyIndexCount;
|
||||
const uint32_t* pQueueFamilyIndices;
|
||||
} VkBufferCreateInfo;
|
||||
|
||||
typedef struct VkMemoryRequirements {
|
||||
VkDeviceSize size;
|
||||
VkDeviceSize alignment;
|
||||
uint32_t memoryTypeBits;
|
||||
} VkMemoryRequirements;
|
||||
|
||||
typedef struct VkMemoryAllocateInfo {
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkDeviceSize allocationSize;
|
||||
uint32_t memoryTypeIndex;
|
||||
} VkMemoryAllocateInfo;
|
||||
|
||||
typedef struct VkImageCreateInfo {
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkImageCreateFlags flags;
|
||||
VkImageType imageType;
|
||||
VkFormat format;
|
||||
VkExtent3D extent;
|
||||
uint32_t mipLevels;
|
||||
uint32_t arrayLayers;
|
||||
VkSampleCountFlagBits samples;
|
||||
VkImageTiling tiling;
|
||||
VkImageUsageFlags usage;
|
||||
VkSharingMode sharingMode;
|
||||
uint32_t queueFamilyIndexCount;
|
||||
const uint32_t* pQueueFamilyIndices;
|
||||
VkImageLayout initialLayout;
|
||||
} VkImageCreateInfo;
|
||||
|
||||
VkResult vkCreateBuffer(
|
||||
VkDevice device,
|
||||
const VkBufferCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkBuffer* pBuffer
|
||||
);
|
||||
|
||||
void vkGetBufferMemoryRequirements(
|
||||
VkDevice device,
|
||||
VkBuffer buffer,
|
||||
VkMemoryRequirements* pMemoryRequirements
|
||||
);
|
||||
|
||||
VkResult vkAllocateMemory(
|
||||
VkDevice device,
|
||||
const VkMemoryAllocateInfo* pAllocateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkDeviceMemory* pMemory
|
||||
);
|
||||
|
||||
VkResult vkBindBufferMemory(
|
||||
VkDevice device,
|
||||
VkBuffer buffer,
|
||||
VkDeviceMemory memory,
|
||||
VkDeviceSize memoryOffset
|
||||
);
|
||||
|
||||
void vkDestroyBuffer(
|
||||
VkDevice device,
|
||||
VkBuffer buffer,
|
||||
const VkAllocationCallbacks* pAllocator
|
||||
);
|
||||
|
||||
void vkFreeMemory(
|
||||
VkDevice device,
|
||||
VkDeviceMemory memory,
|
||||
const VkAllocationCallbacks* pAllocator
|
||||
);
|
||||
|
||||
VkResult vkCreateImage(
|
||||
VkDevice device,
|
||||
const VkImageCreateInfo* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkImage* pImage
|
||||
);
|
||||
|
||||
void vkDestroyImage(
|
||||
VkDevice device,
|
||||
VkImage image,
|
||||
const VkAllocationCallbacks* pAllocator
|
||||
);
|
||||
|
||||
void vkGetImageMemoryRequirements(
|
||||
VkDevice device,
|
||||
VkImage image,
|
||||
VkMemoryRequirements* pMemoryRequirements
|
||||
);
|
||||
|
||||
VkResult vkBindImageMemory(
|
||||
VkDevice device,
|
||||
VkImage image,
|
||||
VkDeviceMemory memory,
|
||||
VkDeviceSize memoryOffset
|
||||
);
|
||||
|
||||
class Device {
|
||||
public:
|
||||
VkDevice GetLogical() const {
|
||||
return logical_device_;
|
||||
}
|
||||
|
||||
uint32_t FindMemoryType(uint32_t type_filter, VkMemoryPropertyFlags properties) const {
|
||||
for (uint32_t i = 0; i < memory_properties_.memoryTypeCount; i++) {
|
||||
if ((type_filter & (1 << i)) &&
|
||||
(memory_properties_.memoryTypes[i].propertyFlags & properties) == properties) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Failed to find suitable memory type");
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice logical_device_;
|
||||
VkPhysicalDeviceMemoryProperties memory_properties_;
|
||||
};
|
||||
|
||||
VkDevice Device::GetLogical() const {
|
||||
return logical_device_;
|
||||
}
|
||||
|
||||
uint32_t Device::FindMemoryType(uint32_t type_filter, VkMemoryPropertyFlags properties) const {
|
||||
for (uint32_t i = 0; i < memory_properties_.memoryTypeCount; i++) {
|
||||
if ((type_filter & (1 << i)) &&
|
||||
(memory_properties_.memoryTypes[i].propertyFlags & properties) == properties) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Failed to find suitable memory type");
|
||||
}
|
||||
|
||||
class VulkanMemoryAllocator {
|
||||
public:
|
||||
VulkanMemoryAllocator(const VmaAllocatorCreateInfo& allocator_info) {
|
||||
if (vmaCreateAllocator(&allocator_info, &allocator_) != VK_SUCCESS) {
|
||||
throw std::runtime_error("Failed to create Vulkan Memory Allocator");
|
||||
}
|
||||
}
|
||||
|
||||
~VulkanMemoryAllocator() {
|
||||
if (allocator_) {
|
||||
vmaDestroyAllocator(allocator_);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable copy semantics
|
||||
VulkanMemoryAllocator(const VulkanMemoryAllocator&) = delete;
|
||||
VulkanMemoryAllocator& operator=(const VulkanMemoryAllocator&) = delete;
|
||||
|
||||
// Enable move semantics
|
||||
VulkanMemoryAllocator(VulkanMemoryAllocator&& other) noexcept
|
||||
: allocator_(other.allocator_) {
|
||||
other.allocator_ = nullptr;
|
||||
}
|
||||
|
||||
VulkanMemoryAllocator& operator=(VulkanMemoryAllocator&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (allocator_) {
|
||||
vmaDestroyAllocator(allocator_);
|
||||
}
|
||||
allocator_ = other.allocator_;
|
||||
other.allocator_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
VmaAllocator Get() const {
|
||||
return allocator_;
|
||||
}
|
||||
|
||||
private:
|
||||
VmaAllocator allocator_ = nullptr;
|
||||
};
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2025 EDEN Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
@ -12,6 +14,8 @@
|
|||
#include "video_core/vulkan_common/vk_enum_string_helper.h"
|
||||
#include "video_core/vulkan_common/vma.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include "video_core/vulkan_common/vulkan_raii.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
|
||||
namespace Vulkan::vk {
|
||||
|
||||
|
@ -1000,3 +1004,11 @@ std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
|
|||
}
|
||||
|
||||
} // namespace Vulkan::vk
|
||||
|
||||
VulkanBuffer buffer(device, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
||||
VulkanImage image(device, {width, height}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
|
||||
|
||||
void CreateBuffer(const Device& device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties) {
|
||||
VulkanBuffer buffer(device, size, usage, properties);
|
||||
// Use the buffer as needed
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue