mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-22 04:24:44 +00:00
imgui Ref-counted textures
- has a background thread to decode textures
This commit is contained in:
parent
0e61607f2e
commit
e9218c1395
9 changed files with 608 additions and 58 deletions
|
@ -613,6 +613,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
|||
set(IMGUI src/imgui/imgui_config.h
|
||||
src/imgui/imgui_layer.h
|
||||
src/imgui/imgui_std.h
|
||||
src/imgui/imgui_texture.h
|
||||
src/imgui/layer/video_info.cpp
|
||||
src/imgui/layer/video_info.h
|
||||
src/imgui/renderer/imgui_core.cpp
|
||||
|
@ -621,6 +622,8 @@ set(IMGUI src/imgui/imgui_config.h
|
|||
src/imgui/renderer/imgui_impl_sdl3.h
|
||||
src/imgui/renderer/imgui_impl_vulkan.cpp
|
||||
src/imgui/renderer/imgui_impl_vulkan.h
|
||||
src/imgui/renderer/texture_manager.cpp
|
||||
src/imgui/renderer/texture_manager.h
|
||||
)
|
||||
|
||||
set(INPUT src/input/controller.cpp
|
||||
|
|
|
@ -26,4 +26,7 @@ extern void assert_fail_debug_msg(const char* msg);
|
|||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
constexpr ImVec2(float _v) : x(_v), y(_v) {}
|
||||
constexpr ImVec2(float _v) : x(_v), y(_v) {}
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {}
|
45
src/imgui/imgui_texture.h
Normal file
45
src/imgui/imgui_texture.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <imgui.h>
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
namespace Core::TextureManager {
|
||||
struct Inner;
|
||||
} // namespace Core::TextureManager
|
||||
|
||||
class RefCountedTexture {
|
||||
Core::TextureManager::Inner* inner;
|
||||
|
||||
explicit RefCountedTexture(Core::TextureManager::Inner* inner);
|
||||
|
||||
public:
|
||||
struct Image {
|
||||
ImTextureID im_id;
|
||||
u32 width;
|
||||
u32 height;
|
||||
};
|
||||
|
||||
static RefCountedTexture DecodePngTexture(std::vector<u8> data);
|
||||
|
||||
static RefCountedTexture DecodePngFile(std::filesystem::path path);
|
||||
|
||||
RefCountedTexture();
|
||||
|
||||
RefCountedTexture(const RefCountedTexture& other);
|
||||
RefCountedTexture(RefCountedTexture&& other) noexcept;
|
||||
RefCountedTexture& operator=(const RefCountedTexture& other);
|
||||
RefCountedTexture& operator=(RefCountedTexture&& other) noexcept;
|
||||
|
||||
virtual ~RefCountedTexture();
|
||||
|
||||
[[nodiscard]] Image GetTexture() const;
|
||||
|
||||
explicit(false) operator bool() const;
|
||||
};
|
||||
|
||||
}; // namespace ImGui
|
|
@ -10,6 +10,7 @@
|
|||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "sdl_window.h"
|
||||
#include "texture_manager.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
|
||||
static void CheckVkResult(const vk::Result err) {
|
||||
|
@ -68,6 +69,8 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w
|
|||
.check_vk_result_fn = &CheckVkResult,
|
||||
};
|
||||
Vulkan::Init(vk_info);
|
||||
|
||||
TextureManager::StartWorker();
|
||||
}
|
||||
|
||||
void OnResize() {
|
||||
|
@ -77,6 +80,8 @@ void OnResize() {
|
|||
void Shutdown(const vk::Device& device) {
|
||||
device.waitIdle();
|
||||
|
||||
TextureManager::StopWorker();
|
||||
|
||||
const ImGuiIO& io = GetIO();
|
||||
const auto ini_filename = (void*)io.IniFilename;
|
||||
const auto log_filename = (void*)io.LogFilename;
|
||||
|
@ -130,7 +135,6 @@ void NewFrame() {
|
|||
}
|
||||
}
|
||||
|
||||
Vulkan::NewFrame();
|
||||
Sdl::NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
// Based on imgui_impl_vulkan.cpp from Dear ImGui repository
|
||||
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "imgui_impl_vulkan.h"
|
||||
|
@ -47,13 +49,15 @@ struct VkData {
|
|||
vk::ShaderModule shader_module_vert{};
|
||||
vk::ShaderModule shader_module_frag{};
|
||||
|
||||
std::mutex command_pool_mutex;
|
||||
vk::CommandPool command_pool{};
|
||||
vk::Sampler simple_sampler{};
|
||||
|
||||
// Font data
|
||||
vk::Sampler font_sampler{};
|
||||
vk::DeviceMemory font_memory{};
|
||||
vk::Image font_image{};
|
||||
vk::ImageView font_view{};
|
||||
vk::DescriptorSet font_descriptor_set{};
|
||||
vk::CommandPool font_command_pool{};
|
||||
vk::CommandBuffer font_command_buffer{};
|
||||
|
||||
// Render buffers
|
||||
|
@ -222,12 +226,53 @@ static inline vk::DeviceSize AlignBufferSize(vk::DeviceSize size, vk::DeviceSize
|
|||
return (size + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
// Register a texture
|
||||
vk::DescriptorSet AddTexture(vk::Sampler sampler, vk::ImageView image_view,
|
||||
vk::ImageLayout image_layout) {
|
||||
void UploadTextureData::Upload() {
|
||||
VkData* bd = GetBackendData();
|
||||
const InitInfo& v = bd->init_info;
|
||||
|
||||
vk::SubmitInfo submit_info{
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &command_buffer,
|
||||
};
|
||||
CheckVkErr(v.queue.submit({submit_info}));
|
||||
CheckVkErr(v.queue.waitIdle());
|
||||
|
||||
v.device.destroyBuffer(upload_buffer, v.allocator);
|
||||
v.device.freeMemory(upload_buffer_memory, v.allocator);
|
||||
{
|
||||
std::unique_lock lk(bd->command_pool_mutex);
|
||||
v.device.freeCommandBuffers(bd->command_pool, {command_buffer});
|
||||
}
|
||||
upload_buffer = VK_NULL_HANDLE;
|
||||
upload_buffer_memory = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void UploadTextureData::Destroy() {
|
||||
VkData* bd = GetBackendData();
|
||||
const InitInfo& v = bd->init_info;
|
||||
|
||||
CheckVkErr(v.device.waitIdle());
|
||||
RemoveTexture(descriptor_set);
|
||||
descriptor_set = VK_NULL_HANDLE;
|
||||
|
||||
v.device.destroyImageView(image_view, v.allocator);
|
||||
image_view = VK_NULL_HANDLE;
|
||||
v.device.destroyImage(image, v.allocator);
|
||||
image = VK_NULL_HANDLE;
|
||||
v.device.freeMemory(image_memory, v.allocator);
|
||||
image_memory = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Register a texture
|
||||
vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout,
|
||||
vk::Sampler sampler) {
|
||||
VkData* bd = GetBackendData();
|
||||
const InitInfo& v = bd->init_info;
|
||||
|
||||
if (sampler == VK_NULL_HANDLE) {
|
||||
sampler = bd->simple_sampler;
|
||||
}
|
||||
|
||||
// Create Descriptor Set:
|
||||
vk::DescriptorSet descriptor_set;
|
||||
{
|
||||
|
@ -262,6 +307,166 @@ vk::DescriptorSet AddTexture(vk::Sampler sampler, vk::ImageView image_view,
|
|||
}
|
||||
return descriptor_set;
|
||||
}
|
||||
UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, u32 height,
|
||||
size_t size) {
|
||||
ImGuiIO& io = GetIO();
|
||||
VkData* bd = GetBackendData();
|
||||
const InitInfo& v = bd->init_info;
|
||||
|
||||
UploadTextureData info{};
|
||||
{
|
||||
std::unique_lock lk(bd->command_pool_mutex);
|
||||
info.command_buffer =
|
||||
CheckVkResult(v.device.allocateCommandBuffers(vk::CommandBufferAllocateInfo{
|
||||
.commandPool = bd->command_pool,
|
||||
.commandBufferCount = 1,
|
||||
}))
|
||||
.front();
|
||||
CheckVkErr(info.command_buffer.begin(vk::CommandBufferBeginInfo{
|
||||
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit,
|
||||
}));
|
||||
}
|
||||
|
||||
// Create Image
|
||||
{
|
||||
vk::ImageCreateInfo image_info{
|
||||
.imageType = vk::ImageType::e2D,
|
||||
.format = format,
|
||||
.extent{
|
||||
.width = width,
|
||||
.height = height,
|
||||
.depth = 1,
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.tiling = vk::ImageTiling::eOptimal,
|
||||
.usage = vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
|
||||
.sharingMode = vk::SharingMode::eExclusive,
|
||||
.initialLayout = vk::ImageLayout::eUndefined,
|
||||
};
|
||||
info.image = CheckVkResult(v.device.createImage(image_info, v.allocator));
|
||||
auto req = v.device.getImageMemoryRequirements(info.image);
|
||||
vk::MemoryAllocateInfo alloc_info{
|
||||
.allocationSize = IM_MAX(v.min_allocation_size, req.size),
|
||||
.memoryTypeIndex =
|
||||
FindMemoryType(vk::MemoryPropertyFlagBits::eDeviceLocal, req.memoryTypeBits),
|
||||
};
|
||||
info.image_memory = CheckVkResult(v.device.allocateMemory(alloc_info, v.allocator));
|
||||
CheckVkErr(v.device.bindImageMemory(info.image, info.image_memory, 0));
|
||||
}
|
||||
|
||||
// Create Image View
|
||||
{
|
||||
vk::ImageViewCreateInfo view_info{
|
||||
.image = info.image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = format,
|
||||
.subresourceRange{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
info.image_view = CheckVkResult(v.device.createImageView(view_info, v.allocator));
|
||||
}
|
||||
|
||||
// Create descriptor set (ImTextureID)
|
||||
info.descriptor_set = AddTexture(info.image_view, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
// Create Upload Buffer
|
||||
{
|
||||
vk::BufferCreateInfo buffer_info{
|
||||
.size = size,
|
||||
.usage = vk::BufferUsageFlagBits::eTransferSrc,
|
||||
.sharingMode = vk::SharingMode::eExclusive,
|
||||
};
|
||||
info.upload_buffer = CheckVkResult(v.device.createBuffer(buffer_info, v.allocator));
|
||||
auto req = v.device.getBufferMemoryRequirements(info.upload_buffer);
|
||||
auto alignemtn = IM_MAX(bd->buffer_memory_alignment, req.alignment);
|
||||
vk::MemoryAllocateInfo alloc_info{
|
||||
.allocationSize = IM_MAX(v.min_allocation_size, req.size),
|
||||
.memoryTypeIndex =
|
||||
FindMemoryType(vk::MemoryPropertyFlagBits::eHostVisible, req.memoryTypeBits),
|
||||
};
|
||||
info.upload_buffer_memory = CheckVkResult(v.device.allocateMemory(alloc_info, v.allocator));
|
||||
CheckVkErr(v.device.bindBufferMemory(info.upload_buffer, info.upload_buffer_memory, 0));
|
||||
}
|
||||
|
||||
// Upload to Buffer
|
||||
{
|
||||
char* map = (char*)CheckVkResult(v.device.mapMemory(info.upload_buffer_memory, 0, size));
|
||||
memcpy(map, data, size);
|
||||
vk::MappedMemoryRange range[1]{
|
||||
{
|
||||
.memory = info.upload_buffer_memory,
|
||||
.size = size,
|
||||
},
|
||||
};
|
||||
CheckVkErr(v.device.flushMappedMemoryRanges(range));
|
||||
v.device.unmapMemory(info.upload_buffer_memory);
|
||||
}
|
||||
|
||||
// Copy to Image
|
||||
{
|
||||
vk::ImageMemoryBarrier copy_barrier[1]{
|
||||
{
|
||||
.sType = vk::StructureType::eImageMemoryBarrier,
|
||||
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
|
||||
.oldLayout = vk::ImageLayout::eUndefined,
|
||||
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = info.image,
|
||||
.subresourceRange{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
info.command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost,
|
||||
vk::PipelineStageFlagBits::eTransfer, {}, {}, {},
|
||||
{copy_barrier});
|
||||
|
||||
vk::BufferImageCopy region{
|
||||
.imageSubresource{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.imageExtent{
|
||||
.width = width,
|
||||
.height = height,
|
||||
.depth = 1,
|
||||
},
|
||||
};
|
||||
info.command_buffer.copyBufferToImage(info.upload_buffer, info.image,
|
||||
vk::ImageLayout::eTransferDstOptimal, {region});
|
||||
|
||||
vk::ImageMemoryBarrier use_barrier[1]{{
|
||||
.sType = vk::StructureType::eImageMemoryBarrier,
|
||||
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
|
||||
.dstAccessMask = vk::AccessFlagBits::eShaderRead,
|
||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = info.image,
|
||||
.subresourceRange{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1,
|
||||
},
|
||||
}};
|
||||
info.command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
|
||||
{use_barrier});
|
||||
}
|
||||
|
||||
CheckVkErr(info.command_buffer.end());
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void RemoveTexture(vk::DescriptorSet descriptor_set) {
|
||||
VkData* bd = GetBackendData();
|
||||
|
@ -517,27 +722,20 @@ static bool CreateFontsTexture() {
|
|||
DestroyFontsTexture();
|
||||
}
|
||||
|
||||
// Create command pool/buffer
|
||||
if (bd->font_command_pool == VK_NULL_HANDLE) {
|
||||
vk::CommandPoolCreateInfo info{
|
||||
.sType = vk::StructureType::eCommandPoolCreateInfo,
|
||||
.flags = vk::CommandPoolCreateFlags{},
|
||||
.queueFamilyIndex = v.queue_family,
|
||||
};
|
||||
bd->font_command_pool = CheckVkResult(v.device.createCommandPool(info, v.allocator));
|
||||
}
|
||||
// Create command buffer
|
||||
if (bd->font_command_buffer == VK_NULL_HANDLE) {
|
||||
vk::CommandBufferAllocateInfo info{
|
||||
.sType = vk::StructureType::eCommandBufferAllocateInfo,
|
||||
.commandPool = bd->font_command_pool,
|
||||
.commandPool = bd->command_pool,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
std::unique_lock lk(bd->command_pool_mutex);
|
||||
bd->font_command_buffer = CheckVkResult(v.device.allocateCommandBuffers(info)).front();
|
||||
}
|
||||
|
||||
// Start command buffer
|
||||
{
|
||||
CheckVkErr(v.device.resetCommandPool(bd->font_command_pool, vk::CommandPoolResetFlags{}));
|
||||
CheckVkErr(bd->font_command_buffer.reset());
|
||||
vk::CommandBufferBeginInfo begin_info{};
|
||||
begin_info.sType = vk::StructureType::eCommandBufferBeginInfo;
|
||||
begin_info.flags |= vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
|
||||
|
@ -597,8 +795,7 @@ static bool CreateFontsTexture() {
|
|||
}
|
||||
|
||||
// Create the Descriptor Set:
|
||||
bd->font_descriptor_set =
|
||||
AddTexture(bd->font_sampler, bd->font_view, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
bd->font_descriptor_set = AddTexture(bd->font_view, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
// Create the Upload Buffer:
|
||||
vk::DeviceMemory upload_buffer_memory{};
|
||||
|
@ -956,25 +1153,6 @@ bool CreateDeviceObjects() {
|
|||
bd->descriptor_pool = CheckVkResult(v.device.createDescriptorPool(pool_info));
|
||||
}
|
||||
|
||||
if (!bd->font_sampler) {
|
||||
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |=
|
||||
// ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow
|
||||
// point/nearest sampling.
|
||||
vk::SamplerCreateInfo info{
|
||||
.sType = vk::StructureType::eSamplerCreateInfo,
|
||||
.magFilter = vk::Filter::eLinear,
|
||||
.minFilter = vk::Filter::eLinear,
|
||||
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
||||
.addressModeU = vk::SamplerAddressMode::eRepeat,
|
||||
.addressModeV = vk::SamplerAddressMode::eRepeat,
|
||||
.addressModeW = vk::SamplerAddressMode::eRepeat,
|
||||
.maxAnisotropy = 1.0f,
|
||||
.minLod = -1000,
|
||||
.maxLod = 1000,
|
||||
};
|
||||
bd->font_sampler = CheckVkResult(v.device.createSampler(info, v.allocator));
|
||||
}
|
||||
|
||||
if (!bd->descriptor_set_layout) {
|
||||
vk::DescriptorSetLayoutBinding binding[1]{
|
||||
{
|
||||
|
@ -1016,6 +1194,35 @@ bool CreateDeviceObjects() {
|
|||
|
||||
CreatePipeline(v.device, v.allocator, v.pipeline_cache, nullptr, &bd->pipeline, v.subpass);
|
||||
|
||||
if (bd->command_pool == VK_NULL_HANDLE) {
|
||||
vk::CommandPoolCreateInfo info{
|
||||
.sType = vk::StructureType::eCommandPoolCreateInfo,
|
||||
.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
|
||||
.queueFamilyIndex = v.queue_family,
|
||||
};
|
||||
std::unique_lock lk(bd->command_pool_mutex);
|
||||
bd->command_pool = CheckVkResult(v.device.createCommandPool(info, v.allocator));
|
||||
}
|
||||
|
||||
if (!bd->simple_sampler) {
|
||||
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |=
|
||||
// ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow
|
||||
// point/nearest sampling.
|
||||
vk::SamplerCreateInfo info{
|
||||
.sType = vk::StructureType::eSamplerCreateInfo,
|
||||
.magFilter = vk::Filter::eLinear,
|
||||
.minFilter = vk::Filter::eLinear,
|
||||
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
||||
.addressModeU = vk::SamplerAddressMode::eRepeat,
|
||||
.addressModeV = vk::SamplerAddressMode::eRepeat,
|
||||
.addressModeW = vk::SamplerAddressMode::eRepeat,
|
||||
.maxAnisotropy = 1.0f,
|
||||
.minLod = -1000,
|
||||
.maxLod = 1000,
|
||||
};
|
||||
bd->simple_sampler = CheckVkResult(v.device.createSampler(info, v.allocator));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1026,12 +1233,14 @@ void ImGuiImplVulkanDestroyDeviceObjects() {
|
|||
DestroyFontsTexture();
|
||||
|
||||
if (bd->font_command_buffer) {
|
||||
v.device.freeCommandBuffers(bd->font_command_pool, {bd->font_command_buffer});
|
||||
std::unique_lock lk(bd->command_pool_mutex);
|
||||
v.device.freeCommandBuffers(bd->command_pool, {bd->font_command_buffer});
|
||||
bd->font_command_buffer = VK_NULL_HANDLE;
|
||||
}
|
||||
if (bd->font_command_pool) {
|
||||
v.device.destroyCommandPool(bd->font_command_pool, v.allocator);
|
||||
bd->font_command_pool = VK_NULL_HANDLE;
|
||||
if (bd->command_pool) {
|
||||
std::unique_lock lk(bd->command_pool_mutex);
|
||||
v.device.destroyCommandPool(bd->command_pool, v.allocator);
|
||||
bd->command_pool = VK_NULL_HANDLE;
|
||||
}
|
||||
if (bd->shader_module_vert) {
|
||||
v.device.destroyShaderModule(bd->shader_module_vert, v.allocator);
|
||||
|
@ -1041,9 +1250,9 @@ void ImGuiImplVulkanDestroyDeviceObjects() {
|
|||
v.device.destroyShaderModule(bd->shader_module_frag, v.allocator);
|
||||
bd->shader_module_frag = VK_NULL_HANDLE;
|
||||
}
|
||||
if (bd->font_sampler) {
|
||||
v.device.destroySampler(bd->font_sampler, v.allocator);
|
||||
bd->font_sampler = VK_NULL_HANDLE;
|
||||
if (bd->simple_sampler) {
|
||||
v.device.destroySampler(bd->simple_sampler, v.allocator);
|
||||
bd->simple_sampler = VK_NULL_HANDLE;
|
||||
}
|
||||
if (bd->descriptor_set_layout) {
|
||||
v.device.destroyDescriptorSetLayout(bd->descriptor_set_layout, v.allocator);
|
||||
|
@ -1095,13 +1304,4 @@ void Shutdown() {
|
|||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
void NewFrame() {
|
||||
VkData* bd = GetBackendData();
|
||||
IM_ASSERT(bd != nullptr &&
|
||||
"Context or backend not initialized! Did you call ImGuiImplVulkanInit()?");
|
||||
|
||||
if (!bd->font_descriptor_set)
|
||||
CreateFontsTexture();
|
||||
}
|
||||
|
||||
} // namespace ImGui::Vulkan
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#pragma once
|
||||
|
||||
#define VULKAN_HPP_NO_EXCEPTIONS
|
||||
#include "common/types.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
struct ImDrawData;
|
||||
|
@ -29,14 +30,33 @@ struct InitInfo {
|
|||
void (*check_vk_result_fn)(vk::Result err);
|
||||
};
|
||||
|
||||
vk::DescriptorSet AddTexture(vk::Sampler sampler, vk::ImageView image_view,
|
||||
vk::ImageLayout image_layout);
|
||||
// Prepare all resources needed for uploading textures
|
||||
// Caller should clean up the returned data.
|
||||
struct UploadTextureData {
|
||||
vk::Image image;
|
||||
vk::ImageView image_view;
|
||||
vk::DescriptorSet descriptor_set;
|
||||
vk::DeviceMemory image_memory;
|
||||
|
||||
vk::CommandBuffer command_buffer; // Submit to the queue
|
||||
vk::Buffer upload_buffer;
|
||||
vk::DeviceMemory upload_buffer_memory;
|
||||
|
||||
void Upload();
|
||||
|
||||
void Destroy();
|
||||
};
|
||||
|
||||
vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout,
|
||||
vk::Sampler sampler = VK_NULL_HANDLE);
|
||||
|
||||
UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, u32 height,
|
||||
size_t size);
|
||||
|
||||
void RemoveTexture(vk::DescriptorSet descriptor_set);
|
||||
|
||||
bool Init(InitInfo info);
|
||||
void Shutdown();
|
||||
void NewFrame();
|
||||
void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer,
|
||||
vk::Pipeline pipeline = VK_NULL_HANDLE);
|
||||
|
||||
|
|
243
src/imgui/renderer/texture_manager.cpp
Normal file
243
src/imgui/renderer/texture_manager.cpp
Normal file
|
@ -0,0 +1,243 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
|
||||
#include <externals/stb_image.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "texture_manager.h"
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
namespace Core::TextureManager {
|
||||
struct Inner {
|
||||
std::atomic_int count = 0;
|
||||
ImTextureID texture_id = nullptr;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
|
||||
Vulkan::UploadTextureData upload_data;
|
||||
|
||||
~Inner();
|
||||
};
|
||||
} // namespace Core::TextureManager
|
||||
|
||||
using namespace Core::TextureManager;
|
||||
|
||||
RefCountedTexture::RefCountedTexture(Inner* inner) : inner(inner) {
|
||||
++inner->count;
|
||||
}
|
||||
|
||||
RefCountedTexture RefCountedTexture::DecodePngTexture(std::vector<u8> data) {
|
||||
const auto core = new Inner;
|
||||
Core::TextureManager::DecodePngTexture(std::move(data), core);
|
||||
return RefCountedTexture(core);
|
||||
}
|
||||
|
||||
RefCountedTexture RefCountedTexture::DecodePngFile(std::filesystem::path path) {
|
||||
const auto core = new Inner;
|
||||
Core::TextureManager::DecodePngFile(std::move(path), core);
|
||||
return RefCountedTexture(core);
|
||||
}
|
||||
|
||||
RefCountedTexture::RefCountedTexture() : inner(nullptr) {}
|
||||
|
||||
RefCountedTexture::RefCountedTexture(const RefCountedTexture& other) : inner(other.inner) {
|
||||
if (inner != nullptr) {
|
||||
++inner->count;
|
||||
}
|
||||
}
|
||||
|
||||
RefCountedTexture::RefCountedTexture(RefCountedTexture&& other) noexcept : inner(other.inner) {
|
||||
other.inner = nullptr;
|
||||
}
|
||||
|
||||
RefCountedTexture& RefCountedTexture::operator=(const RefCountedTexture& other) {
|
||||
if (this == &other)
|
||||
return *this;
|
||||
inner = other.inner;
|
||||
if (inner != nullptr) {
|
||||
++inner->count;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RefCountedTexture& RefCountedTexture::operator=(RefCountedTexture&& other) noexcept {
|
||||
if (this == &other)
|
||||
return *this;
|
||||
std::swap(inner, other.inner);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RefCountedTexture::~RefCountedTexture() {
|
||||
if (inner != nullptr) {
|
||||
if (inner->count.fetch_sub(1) == 1) {
|
||||
delete inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
RefCountedTexture::Image RefCountedTexture::GetTexture() const {
|
||||
if (inner == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return Image{
|
||||
.im_id = inner->texture_id,
|
||||
.width = inner->width,
|
||||
.height = inner->height,
|
||||
};
|
||||
}
|
||||
RefCountedTexture::operator bool() const {
|
||||
return inner != nullptr && inner->texture_id != nullptr;
|
||||
}
|
||||
|
||||
struct Job {
|
||||
Inner* core;
|
||||
std::vector<u8> data;
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
struct UploadJob {
|
||||
Inner* core = nullptr;
|
||||
Vulkan::UploadTextureData data;
|
||||
int tick = 0; // Used to skip the first frame when destroying to await the current frame to draw
|
||||
};
|
||||
|
||||
static bool g_is_worker_running = false;
|
||||
static std::jthread g_worker_thread;
|
||||
static std::condition_variable g_worker_cv;
|
||||
|
||||
static std::mutex g_job_list_mtx;
|
||||
static std::deque<Job> g_job_list;
|
||||
|
||||
static std::mutex g_upload_mtx;
|
||||
static std::deque<UploadJob> g_upload_list;
|
||||
|
||||
namespace Core::TextureManager {
|
||||
|
||||
Inner::~Inner() {
|
||||
if (upload_data.descriptor_set != nullptr) {
|
||||
std::unique_lock lk{g_upload_mtx};
|
||||
g_upload_list.emplace_back(UploadJob{
|
||||
.data = this->upload_data,
|
||||
.tick = 2,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void WorkerLoop() {
|
||||
std::mutex mtx;
|
||||
while (g_is_worker_running) {
|
||||
std::unique_lock lk{mtx};
|
||||
g_worker_cv.wait(lk);
|
||||
if (!g_is_worker_running) {
|
||||
break;
|
||||
}
|
||||
while (true) {
|
||||
g_job_list_mtx.lock();
|
||||
if (g_job_list.empty()) {
|
||||
g_job_list_mtx.unlock();
|
||||
break;
|
||||
}
|
||||
auto [core, png_raw, path] = std::move(g_job_list.front());
|
||||
g_job_list.pop_front();
|
||||
g_job_list_mtx.unlock();
|
||||
|
||||
if (!path.empty()) { // Decode PNG from file
|
||||
Common::FS::IOFile file(path, Common::FS::FileAccessMode::Read);
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(ImGui, "Failed to open PNG file: {}", path.string());
|
||||
continue;
|
||||
}
|
||||
png_raw.resize(file.GetSize());
|
||||
file.Seek(0);
|
||||
file.ReadRaw<u8>(png_raw.data(), png_raw.size());
|
||||
file.Close();
|
||||
}
|
||||
|
||||
int width, height;
|
||||
const stbi_uc* pixels =
|
||||
stbi_load_from_memory(png_raw.data(), png_raw.size(), &width, &height, nullptr, 4);
|
||||
|
||||
auto texture = Vulkan::UploadTexture(pixels, vk::Format::eR8G8B8A8Unorm, width, height,
|
||||
width * height * 4 * sizeof(stbi_uc));
|
||||
|
||||
core->upload_data = texture;
|
||||
core->width = width;
|
||||
core->height = height;
|
||||
|
||||
std::unique_lock upload_lk{g_upload_mtx};
|
||||
g_upload_list.emplace_back(UploadJob{
|
||||
.core = core,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StartWorker() {
|
||||
ASSERT(!g_is_worker_running);
|
||||
g_worker_thread = std::jthread(WorkerLoop);
|
||||
g_is_worker_running = true;
|
||||
}
|
||||
|
||||
void StopWorker() {
|
||||
ASSERT(g_is_worker_running);
|
||||
g_is_worker_running = false;
|
||||
g_worker_cv.notify_one();
|
||||
}
|
||||
|
||||
void DecodePngTexture(std::vector<u8> data, Inner* core) {
|
||||
++core->count;
|
||||
Job job{
|
||||
.core = core,
|
||||
.data = std::move(data),
|
||||
};
|
||||
std::unique_lock lk{g_job_list_mtx};
|
||||
g_job_list.push_back(std::move(job));
|
||||
g_worker_cv.notify_one();
|
||||
}
|
||||
|
||||
void DecodePngFile(std::filesystem::path path, Inner* core) {
|
||||
++core->count;
|
||||
Job job{
|
||||
.core = core,
|
||||
.path = std::move(path),
|
||||
};
|
||||
std::unique_lock lk{g_job_list_mtx};
|
||||
g_job_list.push_back(std::move(job));
|
||||
g_worker_cv.notify_one();
|
||||
}
|
||||
|
||||
void Submit() {
|
||||
UploadJob upload;
|
||||
{
|
||||
std::unique_lock lk{g_upload_mtx};
|
||||
if (g_upload_list.empty()) {
|
||||
return;
|
||||
}
|
||||
// Upload one texture at a time to avoid slow down
|
||||
upload = g_upload_list.front();
|
||||
g_upload_list.pop_front();
|
||||
if (upload.tick > 0) {
|
||||
--upload.tick;
|
||||
g_upload_list.emplace_back(upload);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (upload.core != nullptr) {
|
||||
upload.core->upload_data.Upload();
|
||||
upload.core->texture_id = upload.core->upload_data.descriptor_set;
|
||||
if (upload.core->count.fetch_sub(1) == 1) {
|
||||
delete upload.core;
|
||||
}
|
||||
} else {
|
||||
upload.data.Destroy();
|
||||
}
|
||||
}
|
||||
} // namespace Core::TextureManager
|
||||
|
||||
} // namespace ImGui
|
30
src/imgui/renderer/texture_manager.h
Normal file
30
src/imgui/renderer/texture_manager.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "imgui/imgui_texture.h"
|
||||
|
||||
namespace vk {
|
||||
class CommandBuffer;
|
||||
}
|
||||
|
||||
namespace ImGui::Core::TextureManager {
|
||||
|
||||
struct Inner;
|
||||
|
||||
void StartWorker();
|
||||
|
||||
void StopWorker();
|
||||
|
||||
void DecodePngTexture(std::vector<u8> data, Inner* core);
|
||||
|
||||
void DecodePngFile(std::filesystem::path path, Inner* core);
|
||||
|
||||
void Submit();
|
||||
|
||||
}; // namespace ImGui::Core::TextureManager
|
|
@ -4,6 +4,7 @@
|
|||
#include <mutex>
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "imgui/renderer/texture_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
|
||||
|
@ -190,6 +191,7 @@ void Scheduler::SubmitExecution(SubmitInfo& info) {
|
|||
};
|
||||
|
||||
try {
|
||||
ImGui::Core::TextureManager::Submit();
|
||||
instance.GetGraphicsQueue().submit(submit_info, info.fence);
|
||||
} catch (vk::DeviceLostError& err) {
|
||||
UNREACHABLE_MSG("Device lost during submit: {}", err.what());
|
||||
|
|
Loading…
Add table
Reference in a new issue