diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index a3e8082cc2..8971b2cb96 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -86,8 +86,12 @@ add_subdirectory(libfat16) add_library(imgui STATIC imgui/imgui.cpp imgui/imgui_draw.cpp imgui/imgui_tables.cpp imgui/imgui_widgets.cpp imgui/misc/cpp/imgui_stdlib.cpp) target_compile_definitions(imgui PRIVATE IMGUI_DISABLE_DEMO_WINDOWS) target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/imgui" + "${CMAKE_CURRENT_SOURCE_DIR}/imgui/backends" "${CMAKE_CURRENT_SOURCE_DIR}/imgui_club/imgui_memory_editor/") +target_sources(imgui PRIVATE imgui/backends/imgui_impl_opengl3.cpp imgui/backends/imgui_impl_sdl2.cpp imgui/backends/imgui_impl_vulkan.cpp) +target_link_libraries(imgui PRIVATE sdl2 vulkan) + add_library(miniz STATIC miniz/miniz.c miniz/miniz.h) target_include_directories(miniz PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/miniz") @@ -236,6 +240,7 @@ add_library(CLI11 INTERFACE) target_include_directories(CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/cli11") add_library(vulkan INTERFACE) +target_compile_definitions(vulkan INTERFACE VK_NO_PROTOTYPES VULKAN_HPP_NO_CONSTRUCTORS VULKAN_HPP_NO_SPACESHIP_OPERATOR) target_include_directories(vulkan INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/Vulkan-Headers/include") if(APPLE) if(NOT EXISTS "${CMAKE_BINARY_DIR}/external/MoltenVK-macos.tar") diff --git a/vita3k/app/include/app/functions.h b/vita3k/app/include/app/functions.h index e077403a83..9b2ff088a3 100644 --- a/vita3k/app/include/app/functions.h +++ b/vita3k/app/include/app/functions.h @@ -22,7 +22,7 @@ struct Config; struct EmuEnvState; struct SDL_Window; -struct ImGui_State; +struct GuiState; class Root; namespace app { @@ -38,7 +38,7 @@ enum class AppRunType { void init_paths(Root &root_paths); bool init(EmuEnvState &state, Config &cfg, const Root &root_paths); bool late_init(EmuEnvState &state); -void destroy(EmuEnvState &emuenv, ImGui_State *imgui); +void destroy(EmuEnvState &emuenv, GuiState &gui); void update_viewport(EmuEnvState &state); void switch_state(EmuEnvState &emuenv, const bool pause); void error_dialog(const std::string &message, SDL_Window *window = nullptr); diff --git a/vita3k/app/src/app_init.cpp b/vita3k/app/src/app_init.cpp index d5df7f6ea0..e08f868b0d 100644 --- a/vita3k/app/src/app_init.cpp +++ b/vita3k/app/src/app_init.cpp @@ -22,12 +22,14 @@ #include #include #include +#include +#include #include -#include #include #include #include #include +#include #include #include @@ -45,6 +47,10 @@ #include #include +#include +#include +#include + #ifdef _WIN32 #include #include @@ -262,11 +268,15 @@ bool init(EmuEnvState &state, Config &cfg, const Root &root_paths) { #endif LOG_INFO("User pref path: {}", state.pref_path); + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); if (ImGui::GetCurrentContext() == NULL) { ImGui::CreateContext(); } ImGuiIO &io = ImGui::GetIO(); - io.IniFilename = NULL; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.IniFilename = nullptr; state.backend_renderer = renderer::Backend::Vulkan; @@ -406,8 +416,54 @@ bool late_init(EmuEnvState &state) { return true; } -void destroy(EmuEnvState &emuenv, ImGui_State *imgui) { - ImGui_ImplSdl_Shutdown(imgui); +void destroy(EmuEnvState &emuenv, GuiState &gui) { + switch (emuenv.renderer->current_backend) { + case renderer::Backend::OpenGL: + gui.app_selector.sys_apps_icon.clear(); + gui.app_selector.user_apps_icon.clear(); + gui.users_avatar.clear(); + gui.themes_preview.clear(); + gui.theme_backgrounds.clear(); + gui.theme_information_bar_notice.clear(); + gui.notice_info_icon.clear(); + gui.user_backgrounds.clear(); + gui.trophy_np_com_id_list_icons.clear(); + gui.trophy_list.clear(); + gui.start_background = {}; + gui.apps_background.clear(); + gui.live_area_contents.clear(); + gui.live_items.clear(); + gui.manuals.clear(); + gui.trophy_window_icon = {}; + ImGui_ImplOpenGL3_Shutdown(); + break; + case renderer::Backend::Vulkan: + gui.app_selector.sys_apps_icon.clear(); + gui.app_selector.user_apps_icon.clear(); + gui.users_avatar.clear(); + gui.themes_preview.clear(); + gui.theme_backgrounds.clear(); + gui.theme_information_bar_notice.clear(); + gui.notice_info_icon.clear(); + gui.user_backgrounds.clear(); + gui.trophy_np_com_id_list_icons.clear(); + gui.trophy_list.clear(); + gui.start_background = {}; + gui.apps_background.clear(); + gui.live_area_contents.clear(); + gui.live_items.clear(); + gui.manuals.clear(); + gui.trophy_window_icon = {}; + ImGui_ImplVulkan_Shutdown(); + dynamic_cast(*emuenv.renderer).device.destroy(imgui_descriptor_pool); + break; + default: + LOG_ERROR("Missing ImGui init for backend {}.", static_cast(emuenv.renderer->current_backend)); + break; + } + + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); #ifdef USE_DISCORD discordrpc::shutdown(); diff --git a/vita3k/dialog/include/dialog/state.h b/vita3k/dialog/include/dialog/state.h index 8cb8aa2342..14d39bfcdb 100644 --- a/vita3k/dialog/include/dialog/state.h +++ b/vita3k/dialog/include/dialog/state.h @@ -18,9 +18,8 @@ #pragma once #include - +#include #include - #include enum DialogType { @@ -64,7 +63,7 @@ struct SavedataState { uint32_t button_id = SCE_SAVEDATA_DIALOG_BUTTON_ID_INVALID; std::vector icon_buffer; - std::vector icon_texture; + std::vector icon_texture; uint32_t mode; uint32_t mode_to_display; diff --git a/vita3k/gui/CMakeLists.txt b/vita3k/gui/CMakeLists.txt index f9553f1b1c..749e71d6fa 100644 --- a/vita3k/gui/CMakeLists.txt +++ b/vita3k/gui/CMakeLists.txt @@ -2,10 +2,9 @@ add_library( gui STATIC include/gui/functions.h - include/gui/imgui_impl_sdl_gl3.h + include/gui/imgui_impl_sdl_gl3_texture.h include/gui/imgui_impl_sdl_state.h - include/gui/imgui_impl_sdl_vulkan.h - include/gui/imgui_impl_sdl.h + include/gui/imgui_impl_sdl_vulkan_texture.h include/gui/state.h src/app_context_menu.cpp src/archive_install_dialog.cpp @@ -18,9 +17,9 @@ add_library( src/gui.cpp src/home_screen.cpp src/ime.cpp - src/imgui_impl_sdl_gl3.cpp - src/imgui_impl_sdl_vulkan.cpp - src/imgui_impl_sdl.cpp + src/imgui_impl_sdl_gl3_texture.cpp + src/imgui_impl_sdl_texture.cpp + src/imgui_impl_sdl_vulkan_texture.cpp src/information_bar.cpp src/initial_setup.cpp src/license_install_dialog.cpp diff --git a/vita3k/gui/include/gui/functions.h b/vita3k/gui/include/gui/functions.h index 33a28ae45d..eef4c8d8a9 100644 --- a/vita3k/gui/include/gui/functions.h +++ b/vita3k/gui/include/gui/functions.h @@ -87,7 +87,7 @@ void init_user_apps(GuiState &gui, EmuEnvState &emuenv); bool init_user_background(GuiState &gui, EmuEnvState &emuenv, const std::string &background_path); bool init_user_backgrounds(GuiState &gui, EmuEnvState &emuenv); void init_user_management(GuiState &gui, EmuEnvState &emuenv); -bool init_user_start_background(GuiState &gui, const std::string &image_path); +bool init_user_start_background(GuiState &gui, EmuEnvState &emuenv, const std::string &image_path); void load_and_update_compat_user_apps(GuiState &gui, EmuEnvState &emuenv); void open_live_area(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path); void open_manual(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path); @@ -113,7 +113,7 @@ void update_time_app_used(GuiState &gui, EmuEnvState &emuenv, const std::string void save_notice_list(EmuEnvState &emuenv); void draw_begin(GuiState &gui, EmuEnvState &emuenv); -void draw_end(GuiState &gui); +void draw_end(GuiState &gui, EmuEnvState &emuenv); void draw_vita_area(GuiState &gui, EmuEnvState &emuenv); void draw_ui(GuiState &gui, EmuEnvState &emuenv); @@ -130,6 +130,4 @@ void draw_trophies_unlocked(GuiState &gui, EmuEnvState &emuenv); void draw_touchpad_cursor(EmuEnvState &emuenv); void draw_perf_overlay(GuiState &gui, EmuEnvState &emuenv); -ImTextureID load_image(GuiState &gui, const uint8_t *data, const int size); - } // namespace gui diff --git a/vita3k/gui/include/gui/imgui_impl_sdl.h b/vita3k/gui/include/gui/imgui_impl_sdl.h deleted file mode 100644 index ae6b8d8d9a..0000000000 --- a/vita3k/gui/include/gui/imgui_impl_sdl.h +++ /dev/null @@ -1,41 +0,0 @@ -// Vita3K emulator project -// Copyright (C) 2024 Vita3K team -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#pragma once - -#include - -#include -#include - -union SDL_Event; -struct SDL_Window; -struct SDL_Cursor; - -IMGUI_API ImGui_State *ImGui_ImplSdl_Init(renderer::State *renderer, SDL_Window *window); -IMGUI_API void ImGui_ImplSdl_Shutdown(ImGui_State *state); -IMGUI_API void ImGui_ImplSdl_NewFrame(ImGui_State *state); -IMGUI_API void ImGui_ImplSdl_RenderDrawData(ImGui_State *state); -IMGUI_API bool ImGui_ImplSdl_ProcessEvent(ImGui_State *state, SDL_Event *event); -IMGUI_API void ImGui_ImplSdl_GetDrawableSize(ImGui_State *state, int &width, int &height); - -IMGUI_API ImTextureID ImGui_ImplSdl_CreateTexture(ImGui_State *state, void *data, int width, int height); -IMGUI_API void ImGui_ImplSdl_DeleteTexture(ImGui_State *state, ImTextureID texture); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplSdl_InvalidateDeviceObjects(ImGui_State *state); -IMGUI_API bool ImGui_ImplSdl_CreateDeviceObjects(ImGui_State *state); diff --git a/vita3k/gui/include/gui/imgui_impl_sdl_gl3.h b/vita3k/gui/include/gui/imgui_impl_sdl_gl3.h deleted file mode 100644 index d49c3cacdb..0000000000 --- a/vita3k/gui/include/gui/imgui_impl_sdl_gl3.h +++ /dev/null @@ -1,48 +0,0 @@ -// ImGui SDL2 binding with OpenGL3 -// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// Missing features: -// [ ] SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -#pragma once - -#include - -#include -#include - -struct SDL_Window; -union SDL_Event; - -struct ImGui_GLState : public ImGui_State { - char glsl_version[32] = "#version 150"; - uint32_t font_texture = 0; - uint32_t shader_handle = 0, vertex_handle = 0, fragment_handle = 0; - uint32_t attribute_location_tex = 0, attribute_projection_mat = 0; - uint32_t attribute_position_location = 0, attribute_uv_location = 0, attribute_color_location = 0; - uint32_t vbo = 0, elements = 0; - - // GL actually never needs the renderer. Putting it here just in case it is needed in the future. - inline renderer::gl::GLState &get_renderer() { - return dynamic_cast(*renderer); - } -}; - -IMGUI_API ImGui_GLState *ImGui_ImplSdlGL3_Init(renderer::State *renderer, SDL_Window *window, const char *glsl_version = NULL); -IMGUI_API void ImGui_ImplSdlGL3_Shutdown(ImGui_GLState &state); -IMGUI_API void ImGui_ImplSdlGL3_RenderDrawData(ImGui_GLState &state); - -IMGUI_API ImTextureID ImGui_ImplSdlGL3_CreateTexture(void *data, int width, int height); -IMGUI_API void ImGui_ImplSdlGL3_DeleteTexture(ImTextureID texture); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplSdlGL3_InvalidateDeviceObjects(ImGui_GLState &state); -IMGUI_API bool ImGui_ImplSdlGL3_CreateDeviceObjects(ImGui_GLState &state); diff --git a/vita3k/gui/include/gui/imgui_impl_sdl_gl3_texture.h b/vita3k/gui/include/gui/imgui_impl_sdl_gl3_texture.h new file mode 100644 index 0000000000..0a35e30a3c --- /dev/null +++ b/vita3k/gui/include/gui/imgui_impl_sdl_gl3_texture.h @@ -0,0 +1,33 @@ +// Vita3K emulator project +// Copyright (C) 2024 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#pragma once + +#include +#include + +struct GLTextureData : ImguiTextureData +{ +private: + GLuint texture = 0; + +public: + GLTextureData(unsigned char *data, int width, int height); + ~GLTextureData() override; + + ImTextureID GetImTextureID() const override; +}; diff --git a/vita3k/gui/include/gui/imgui_impl_sdl_state.h b/vita3k/gui/include/gui/imgui_impl_sdl_state.h index 3bc5f16d2a..67821ddeb8 100644 --- a/vita3k/gui/include/gui/imgui_impl_sdl_state.h +++ b/vita3k/gui/include/gui/imgui_impl_sdl_state.h @@ -19,47 +19,41 @@ #include -#include - -struct SDL_Window; -struct SDL_Cursor; +#include +#include namespace renderer { struct State; } -struct ImGui_State { - SDL_Window *window{}; - renderer::State *renderer{}; - - uint64_t time = 0; - bool mouse_pressed[3] = { false, false, false }; - SDL_Cursor *mouse_cursors[ImGuiMouseCursor_COUNT] = { nullptr }; - - bool init = false; - bool do_clear_screen = true; - - virtual ~ImGui_State() = default; +struct ImguiTextureData +{ + virtual ~ImguiTextureData() = default; + virtual ImTextureID GetImTextureID() const = 0; }; class ImGui_Texture { - ImGui_State *state = nullptr; - ImTextureID texture_id = nullptr; + std::unique_ptr texture; + bool loadTexture(renderer::State *state, unsigned char *data, int height, int width); public: + int height = 0; + int width = 0; + ImGui_Texture() = default; - ImGui_Texture(ImGui_State *new_state, void *data, int width, int height); + ImGui_Texture(renderer::State *state, FILE *f); + ImGui_Texture(renderer::State *state, unsigned char const *buffer, int len); ImGui_Texture(ImGui_Texture &&texture) noexcept; - void init(ImGui_State *new_state, ImTextureID texture); - void init(ImGui_State *new_state, void *data, int width, int height); + bool loadTextureFromFile(renderer::State *state, FILE *f); + bool loadTextureFromMemory(renderer::State *state, unsigned char const *buffer, int len); operator bool() const; operator ImTextureID() const; - bool operator==(const ImGui_Texture &texture); + bool operator==(const ImGui_Texture &texture) const; ImGui_Texture &operator=(ImGui_Texture &&texture) noexcept; ImGui_Texture &operator=(const ImGui_Texture &texture) = delete; - ~ImGui_Texture(); + ~ImGui_Texture() = default; }; diff --git a/vita3k/gui/include/gui/imgui_impl_sdl_vulkan.h b/vita3k/gui/include/gui/imgui_impl_sdl_vulkan.h deleted file mode 100644 index 140e738318..0000000000 --- a/vita3k/gui/include/gui/imgui_impl_sdl_vulkan.h +++ /dev/null @@ -1,103 +0,0 @@ -// dear imgui: Renderer Backend for Vulkan -// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) - -// Implemented features: -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. - -// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. -// See imgui_impl_vulkan.cpp file for details. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. -// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ - -// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. -// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. -// You will use those if you want to use this rendering backend in your engine/app. -// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by -// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. -// Read comments in imgui_impl_vulkan.h. - -#pragma once - -#include - -#include -#include -#include - -typedef union SDL_Event SDL_Event; - -// Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() -// [Please zero-clear before use!] -struct ImGui_ImplVulkanH_FrameRenderBuffers { - vma::Allocation VertexBufferAllocation; - vma::Allocation IndexBufferAllocation; - vk::DeviceSize VertexBufferSize; - vk::DeviceSize IndexBufferSize; - vk::Buffer VertexBuffer; - vk::Buffer IndexBuffer; -}; - -// Each viewport will hold 1 ImGui_ImplVulkanH_WindowRenderBuffers -// [Please zero-clear before use!] -struct ImGui_ImplVulkanH_WindowRenderBuffers { - uint32_t Index; - uint32_t Count; - ImGui_ImplVulkanH_FrameRenderBuffers *FrameRenderBuffers = nullptr; -}; - -struct TextureState { - static constexpr uint32_t nb_descriptor_sets = 1024; - - vma::Allocation allocation; - vk::Image image; - vk::ImageView image_view; - vk::DescriptorSet descriptor_set; - uint64_t last_frame_used = 0; -}; - -struct ImGui_VulkanState : public ImGui_State { - vk::RenderPass RenderPass{}; - vk::DeviceSize BufferMemoryAlignment = 256; - vk::PipelineCreateFlags PipelineCreateFlags{}; - vk::DescriptorSetLayout DescriptorSetLayout{}; - vk::PipelineLayout PipelineLayout{}; - vk::Pipeline Pipeline{}; - uint32_t Subpass{}; - vk::ShaderModule ShaderModuleVert{}; - vk::ShaderModule ShaderModuleFrag{}; - - // Font data - vk::Sampler FontSampler{}; - TextureState *Font; - vma::Allocation UploadBufferAllocation{}; - vk::Buffer UploadBuffer{}; - vk::CommandBuffer CommandBuffer{}; - - vk::DescriptorPool DescriptorPool; - std::vector DescriptorSets; - uint32_t DescriptorIdx = 0; - - uint64_t frame_timestamp = 1; - - // Render buffers - ImGui_ImplVulkanH_WindowRenderBuffers MainWindowRenderBuffers; -}; - -IMGUI_API ImGui_VulkanState *ImGui_ImplSdlVulkan_Init(renderer::State *renderer, SDL_Window *window); -IMGUI_API void ImGui_ImplSdlVulkan_Shutdown(ImGui_VulkanState &state); -IMGUI_API void ImGui_ImplSdlVulkan_RenderDrawData(ImGui_VulkanState &state); - -// if is_alpha is set to true, the texture only has one alpha component, the other channels map to 1 -IMGUI_API ImTextureID ImGui_ImplSdlVulkan_CreateTexture(ImGui_VulkanState &state, void *pixels, int width, int height, bool is_alpha = false); -IMGUI_API void ImGui_ImplSdlVulkan_DeleteTexture(ImGui_VulkanState &state, ImTextureID texture); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplSdlVulkan_InvalidateDeviceObjects(ImGui_VulkanState &state); -IMGUI_API bool ImGui_ImplSdlVulkan_CreateDeviceObjects(ImGui_VulkanState &state); diff --git a/vita3k/gui/include/gui/imgui_impl_sdl_vulkan_texture.h b/vita3k/gui/include/gui/imgui_impl_sdl_vulkan_texture.h new file mode 100644 index 0000000000..5a1f4e2faa --- /dev/null +++ b/vita3k/gui/include/gui/imgui_impl_sdl_vulkan_texture.h @@ -0,0 +1,46 @@ +// Vita3K emulator project +// Copyright (C) 2024 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#pragma once + +#include +#include +#include + +extern VkDescriptorPool imgui_descriptor_pool; + +struct VKTextureData : ImguiTextureData +{ +private: + renderer::vulkan::VKState *state = nullptr; + + VkDescriptorSet DS; // Descriptor set: this is what you'll pass to Image() + + // Need to keep track of these to properly cleanup + vk::ImageView ImageView; + vk::Image Image; + vk::DeviceMemory ImageMemory; + vk::Sampler Sampler; + vk::Buffer UploadBuffer; + vk::DeviceMemory UploadBufferMemory; + +public: + VKTextureData(renderer::vulkan::VKState *vk_state, unsigned char *data, int width, int height); + ~VKTextureData() override; + + ImTextureID GetImTextureID() const override; +}; diff --git a/vita3k/gui/include/gui/state.h b/vita3k/gui/include/gui/state.h index 8cad431e45..47c0487177 100644 --- a/vita3k/gui/include/gui/state.h +++ b/vita3k/gui/include/gui/state.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -75,24 +76,15 @@ struct AppInfo { size_t size; }; -struct IconData { - int32_t width = 0; - int32_t height = 0; - - std::unique_ptr data; - - IconData(); -}; - struct IconAsyncLoader { std::mutex mutex; - std::unordered_map icon_data; + std::unordered_map icon_data; std::thread thread; std::atomic_bool quit = false; - void commit(GuiState &gui); + void commit(GuiState &gui, EmuEnvState &emuenv); IconAsyncLoader(GuiState &gui, EmuEnvState &emuenv, const std::vector &app_list); ~IconAsyncLoader(); @@ -265,8 +257,6 @@ struct InfoMessage { }; struct GuiState { - std::unique_ptr imgui_state; - bool renderer_focused = true; gui::FileMenuState file_menu; gui::DebugMenuState debug_menu; @@ -355,7 +345,7 @@ struct GuiState { std::uint32_t trophy_window_frame_count{ 0 }; TrophyAnimationStage trophy_window_frame_stage{ TrophyAnimationStage::SLIDE_IN }; - ImTextureID trophy_window_icon{}; + ImGui_Texture trophy_window_icon{}; std::vector trophy_unlock_display_requests; std::mutex trophy_unlock_display_requests_access_mutex; diff --git a/vita3k/gui/src/app_context_menu.cpp b/vita3k/gui/src/app_context_menu.cpp index 68c6e4e8d2..f27edb73cb 100644 --- a/vita3k/gui/src/app_context_menu.cpp +++ b/vita3k/gui/src/app_context_menu.cpp @@ -252,7 +252,6 @@ void delete_app(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path) fs::remove_all(IMPORT_TEXTURES_PATH); if (gui.app_selector.user_apps_icon.contains(app_path)) { - gui.app_selector.user_apps_icon[app_path] = {}; gui.app_selector.user_apps_icon.erase(app_path); } diff --git a/vita3k/gui/src/common_dialog.cpp b/vita3k/gui/src/common_dialog.cpp index 5c02e4efd4..da45b8f391 100644 --- a/vita3k/gui/src/common_dialog.cpp +++ b/vita3k/gui/src/common_dialog.cpp @@ -325,11 +325,14 @@ void browse_save_data_dialog(GuiState &gui, EmuEnvState &emuenv, const uint32_t } } -static ImTextureID check_and_init_icon_texture(GuiState &gui, EmuEnvState &emuenv, const uint32_t index) { +static ImTextureID check_and_init_icon_texture(EmuEnvState &emuenv, const uint32_t index) { auto &icon_texture = emuenv.common_dialog.savedata.icon_texture[index]; if (!icon_texture && !emuenv.common_dialog.savedata.icon_buffer[index].empty()) { - icon_texture = load_image(gui, emuenv.common_dialog.savedata.icon_buffer[index].data(), - static_cast(emuenv.common_dialog.savedata.icon_buffer[index].size())); + icon_texture.loadTextureFromMemory( + emuenv.renderer.get(), + emuenv.common_dialog.savedata.icon_buffer[index].data(), + static_cast(emuenv.common_dialog.savedata.icon_buffer[index].size()) + ); } return icon_texture; @@ -441,7 +444,7 @@ static void draw_savedata_dialog_list(GuiState &gui, EmuEnvState &emuenv, float ImGui::EndGroup(); ImGui::SetWindowFontScale(1.2f * FONT_SCALE); const ImVec2 THUMBNAIL_POS(150.f * SCALE.x, save_pos.y); - const auto icon_texture = check_and_init_icon_texture(gui, emuenv, loop_index); + const auto icon_texture = check_and_init_icon_texture(emuenv, loop_index); if (icon_texture) { ImGui::SetCursorPos(THUMBNAIL_POS); ImGui::Image(icon_texture, THUMBNAIL_SIZE); @@ -564,7 +567,7 @@ static void draw_savedata_dialog(GuiState &gui, EmuEnvState &emuenv, float FONT_ const ImVec2 ICON_POS(48 * SCALE.x, 34 * SCALE.y); // Draw the save data icon - const auto icon_texture = check_and_init_icon_texture(gui, emuenv, emuenv.common_dialog.savedata.selected_save); + const auto icon_texture = check_and_init_icon_texture(emuenv, emuenv.common_dialog.savedata.selected_save); if (icon_texture) { ImGui::SetCursorPos(ImVec2(48 * SCALE.x, 34 * SCALE.y)); ImGui::Image(icon_texture, THUMBNAIL_SIZE); diff --git a/vita3k/gui/src/gui.cpp b/vita3k/gui/src/gui.cpp index 0045137355..4cdcfc5789 100644 --- a/vita3k/gui/src/gui.cpp +++ b/vita3k/gui/src/gui.cpp @@ -17,15 +17,12 @@ #include "private.h" -#include - -#include -#include - -#include #include #include #include +#include +#include +#include #include #include #include @@ -33,12 +30,16 @@ #include #include #include +#include #include #include #include -#define STB_IMAGE_IMPLEMENTATION -#include +#include + +#include +#include +#include #include #include @@ -290,8 +291,7 @@ vfs::FileBuffer init_default_icon(GuiState &gui, EmuEnvState &emuenv) { return buffer; } -static IconData load_app_icon(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path) { - IconData image; +static vfs::FileBuffer load_app_icon(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path) { vfs::FileBuffer buffer; const auto APP_INDEX = get_app_index(gui, app_path); @@ -305,34 +305,23 @@ static IconData load_app_icon(GuiState &gui, EmuEnvState &emuenv, const std::str } else LOG_INFO("Default icon found for App {}, [{}] in path {}.", APP_INDEX->title_id, APP_INDEX->title, app_path); } - image.data.reset(stbi_load_from_memory( - buffer.data(), static_cast(buffer.size()), - &image.width, &image.height, nullptr, STBI_rgb_alpha)); - if (!image.data || image.width != 128 || image.height != 128) { - LOG_ERROR("Invalid icon for title {}, [{}] in path {}.", - APP_INDEX->title_id, APP_INDEX->title, app_path); - return {}; - } - return image; + return buffer; } void init_app_icon(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path) { - IconData data = load_app_icon(gui, emuenv, app_path); - if (data.data) { - gui.app_selector.user_apps_icon[app_path].init(gui.imgui_state.get(), data.data.get(), data.width, data.height); + vfs::FileBuffer buffer = load_app_icon(gui, emuenv, app_path); + if (!buffer.empty()) { + gui.app_selector.user_apps_icon[app_path].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } } -IconData::IconData() - : data(nullptr, stbi_image_free) {} - -void IconAsyncLoader::commit(GuiState &gui) { +void IconAsyncLoader::commit(GuiState &gui, EmuEnvState &emuenv) { std::lock_guard lock(mutex); - for (const auto &pair : icon_data) { - if (pair.second.data) { - gui.app_selector.user_apps_icon[pair.first].init(gui.imgui_state.get(), pair.second.data.get(), pair.second.width, pair.second.height); + for (const auto &[app_path, buffer] : icon_data) { + if (!buffer.empty()) { + gui.app_selector.user_apps_icon[app_path].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } } @@ -356,7 +345,7 @@ IconAsyncLoader::IconAsyncLoader(GuiState &gui, EmuEnvState &emuenv, const std:: return; // load the actual texture - IconData data = load_app_icon(gui, emuenv, path); + vfs::FileBuffer data = load_app_icon(gui, emuenv, path); // Duplicate code here from init_app_icon { @@ -381,8 +370,6 @@ void init_app_background(GuiState &gui, EmuEnvState &emuenv, const std::string & return; const auto APP_INDEX = get_app_index(gui, app_path); - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; const auto is_sys = app_path.starts_with("NPXS") && (app_path != "NPXS10007"); @@ -398,13 +385,7 @@ void init_app_background(GuiState &gui, EmuEnvState &emuenv, const std::string & return; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid background for application {} [{}].", title, app_path); - return; - } - gui.apps_background[app_path].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.apps_background[app_path].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } std::string get_sys_lang_name(uint32_t lang_id) { @@ -684,36 +665,71 @@ std::map get_date_time(GuiState &gui, EmuEnvState &emuenv return date_time_str; } -ImTextureID load_image(GuiState &gui, const uint8_t *data, const int size) { - int width; - int height; - - stbi_uc *img_data = stbi_load_from_memory(data, size, &width, &height, - nullptr, STBI_rgb_alpha); - - if (!img_data) - return nullptr; - - const auto handle = ImGui_ImplSdl_CreateTexture(gui.imgui_state.get(), img_data, width, height); - stbi_image_free(img_data); - - return handle; -} - void pre_init(GuiState &gui, EmuEnvState &emuenv) { if (ImGui::GetCurrentContext() == NULL) { ImGui::CreateContext(); } - gui.imgui_state.reset(ImGui_ImplSdl_Init(emuenv.renderer.get(), emuenv.window.get())); - assert(gui.imgui_state); + if (emuenv.renderer->current_backend == renderer::Backend::OpenGL) { + ImGui_ImplOpenGL3_Init(nullptr); + ImGui_ImplSDL2_InitForOpenGL(emuenv.window.get(), nullptr); + } else if (emuenv.renderer->current_backend == renderer::Backend::Vulkan) { + auto& vk_state = dynamic_cast(*emuenv.renderer); + + /** + * Create descriptor pool + */ + static constexpr uint32_t nb_descriptor_sets = 1024; + + vk::DescriptorPoolSize pool_size{ + .type = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = nb_descriptor_sets + }; + vk::DescriptorPoolCreateInfo pool_info{ + .maxSets = nb_descriptor_sets, + }; + pool_info.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet); + pool_info.setPoolSizes(pool_size); + imgui_descriptor_pool = vk_state.device.createDescriptorPool(pool_info); + + ImGui_ImplSDL2_InitForVulkan(emuenv.window.get()); + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = vk_state.instance; + init_info.PhysicalDevice = vk_state.physical_device; + init_info.Device = vk_state.device; + init_info.QueueFamily = vk_state.general_family_index; + init_info.Queue = vk_state.general_queue; + init_info.PipelineCache = VK_NULL_HANDLE; + init_info.DescriptorPool = imgui_descriptor_pool; + init_info.RenderPass = vk_state.screen_renderer.default_render_pass; + init_info.Subpass = 0; + init_info.MinImageCount = vk_state.screen_renderer.surface_capabilities.minImageCount; + init_info.ImageCount = vk_state.screen_renderer.swapchain_size; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.Allocator = nullptr; + init_info.CheckVkResultFn = [](VkResult err) { + if (err == 0) + return; + LOG_ERROR("[vulkan] Error: VkResult = {}", static_cast(err)); + if (err < 0) + abort(); + }; + +#if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1 + ImGui_ImplVulkan_LoadFunctions([](const char *function, void *user_data) { + return VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr((VkInstance)user_data, function); + }, init_info.Instance); +#endif + + ImGui_ImplVulkan_Init(&init_info); + } else { + LOG_ERROR("Missing ImGui init for backend {}.", static_cast(emuenv.renderer->current_backend)); + return; + } init_style(emuenv); init_font(gui, emuenv); lang::init_lang(gui.lang, emuenv); - - bool result = ImGui_ImplSdl_CreateDeviceObjects(gui.imgui_state.get()); - assert(result); } void init(GuiState &gui, EmuEnvState &emuenv) { @@ -737,18 +753,51 @@ void init(GuiState &gui, EmuEnvState &emuenv) { } void draw_begin(GuiState &gui, EmuEnvState &emuenv) { - ImGui_ImplSdl_NewFrame(gui.imgui_state.get()); + if (emuenv.renderer->current_backend == renderer::Backend::OpenGL) { + ImGui_ImplOpenGL3_NewFrame(); + } else if (emuenv.renderer->current_backend == renderer::Backend::Vulkan) { + ImGui_ImplVulkan_NewFrame(); + } + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); emuenv.renderer_focused = !ImGui::GetIO().WantCaptureMouse; // async loading, renderer texture creation needs to be synchronous // cant bind opengl context outside main thread on macos now if (gui.app_selector.icon_async_loader) - gui.app_selector.icon_async_loader->commit(gui); + gui.app_selector.icon_async_loader->commit(gui, emuenv); } -void draw_end(GuiState &gui) { +void draw_end(GuiState &gui, EmuEnvState &emuenv) { ImGui::Render(); - ImGui_ImplSdl_RenderDrawData(gui.imgui_state.get()); + + switch (emuenv.renderer->current_backend) { + case renderer::Backend::OpenGL: + return ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + case renderer::Backend::Vulkan: + auto &vk_state = dynamic_cast(*emuenv.renderer); + + if (vk_state.screen_renderer.swapchain_image_idx == ~0) { + // this happen in the game selection screen + if (!vk_state.screen_renderer.acquire_swapchain_image(true)) + return; + } else if (!vk_state.screen_renderer.current_cmd_buffer) { + // Can happen while resizing the window + return; + } + +#if 0 + // FIXME + if (vk_state.screen_renderer.need_rebuild) { + vk_state.device.destroy(bd->Pipeline); + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, v->RenderPass, v->MSAASamples, &bd->Pipeline, v->Subpass); + vk_state.screen_renderer.need_rebuild = false; + } +#endif + + return ImGui_ImplVulkan_RenderDrawData( ImGui::GetDrawData(), vk_state.screen_renderer.current_cmd_buffer); + } } void draw_touchpad_cursor(EmuEnvState &emuenv) { diff --git a/vita3k/gui/src/home_screen.cpp b/vita3k/gui/src/home_screen.cpp index 65c6dd5bb2..4858ecc4a8 100644 --- a/vita3k/gui/src/home_screen.cpp +++ b/vita3k/gui/src/home_screen.cpp @@ -190,8 +190,6 @@ void close_system_app(GuiState &gui, EmuEnvState &emuenv) { gui.vita_area.manual = false; // Free manual textures from memory when manual is closed - for (auto &manual : gui.manuals) - manual = {}; gui.manuals.clear(); } else if (gui.vita_area.settings) { gui.vita_area.settings = false; diff --git a/vita3k/gui/src/imgui_impl_sdl.cpp b/vita3k/gui/src/imgui_impl_sdl.cpp deleted file mode 100644 index caf68d3014..0000000000 --- a/vita3k/gui/src/imgui_impl_sdl.cpp +++ /dev/null @@ -1,348 +0,0 @@ -// Vita3K emulator project -// Copyright (C) 2024 Vita3K team -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include -#include -#include - -#include -#include - -#include -#include -#include - -static const char *ImGui_ImplSdl_GetClipboardText(ImGuiContext *) { - return SDL_GetClipboardText(); -} - -static void ImGui_ImplSdl_SetClipboardText(ImGuiContext *, const char *text) { - SDL_SetClipboardText(text); -} - -IMGUI_API ImGui_State *ImGui_ImplSdl_Init(renderer::State *renderer, SDL_Window *window) { - ImGui_State *state; - - switch (renderer->current_backend) { - case renderer::Backend::OpenGL: - state = reinterpret_cast(ImGui_ImplSdlGL3_Init(renderer, window, nullptr)); - break; - - case renderer::Backend::Vulkan: - state = reinterpret_cast(ImGui_ImplSdlVulkan_Init(renderer, window)); - break; - - default: - LOG_ERROR("Missing ImGui init for backend {}.", static_cast(renderer->current_backend)); - return nullptr; - } - - // Setup back-end capabilities flags - ImGuiIO &io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - - // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. - io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; - io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN; - io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP; - io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN; - io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; - io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; - io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; - io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE; - io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; - io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; - io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; - io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; - io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; - io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X; - io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; - io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; - - ImGuiPlatformIO &platform_io = ImGui::GetPlatformIO(); - platform_io.Platform_SetClipboardTextFn = ImGui_ImplSdl_SetClipboardText; - platform_io.Platform_GetClipboardTextFn = ImGui_ImplSdl_GetClipboardText; - platform_io.Platform_ClipboardUserData = nullptr; - platform_io.Platform_SetImeDataFn = nullptr; - - state->mouse_cursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - state->mouse_cursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); - state->mouse_cursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); - state->mouse_cursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); - state->mouse_cursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); - state->mouse_cursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); - state->mouse_cursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); - state->mouse_cursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); - state->mouse_cursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); - - // TODO: is this needed/useful ? - // Set platform dependent data in viewport - // Our mouse update function expect PlatformHandle to be filled for the main viewport - ImGuiViewport *main_viewport = ImGui::GetMainViewport(); - main_viewport->PlatformHandle = (void *)window; - main_viewport->PlatformHandleRaw = nullptr; - SDL_SysWMinfo info; - SDL_VERSION(&info.version); - if (SDL_GetWindowWMInfo(window, &info)) { -#if defined(SDL_VIDEO_DRIVER_WINDOWS) - main_viewport->PlatformHandleRaw = (void *)info.info.win.window; -#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) - main_viewport->PlatformHandleRaw = (void *)info.info.cocoa.window; -#endif - } - - return state; -} -IMGUI_API void ImGui_ImplSdl_Shutdown(ImGui_State *state) { - switch (state->renderer->current_backend) { - case renderer::Backend::OpenGL: - return ImGui_ImplSdlGL3_Shutdown(dynamic_cast(*state)); - - case renderer::Backend::Vulkan: - return ImGui_ImplSdlVulkan_Shutdown(dynamic_cast(*state)); - - default: - LOG_ERROR("Missing ImGui init for backend {}.", static_cast(state->renderer->current_backend)); - } - - // Destroy SDL mouse cursors - for (auto &mouse_cursor : state->mouse_cursors) - SDL_FreeCursor(mouse_cursor); - memset(state->mouse_cursors, 0, sizeof(state->mouse_cursors)); -} -IMGUI_API void ImGui_ImplSdl_NewFrame(ImGui_State *state) { - ImGuiIO &io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - SDL_GetWindowSize(state->window, &w, &h); - ImGui_ImplSdl_GetDrawableSize(state, display_w, display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) - static Uint64 frequency = SDL_GetPerformanceFrequency(); - Uint64 current_time = SDL_GetPerformanceCounter(); - io.DeltaTime = state->time > 0 ? (float)((double)(current_time - state->time) / frequency) : (1.0f / 60.0f); - state->time = current_time; - - // Setup mouse inputs (we already got mouse wheel, keyboard keys & characters from our event handler) - int mx, my; - Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - io.MouseDown[0] = state->mouse_pressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[1] = state->mouse_pressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - io.MouseDown[2] = state->mouse_pressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; - state->mouse_pressed[0] = state->mouse_pressed[1] = state->mouse_pressed[2] = false; - - if ((SDL_GetWindowFlags(state->window) & SDL_WINDOW_INPUT_FOCUS) != 0) - io.MousePos = ImVec2((float)mx, (float)my); - - // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0) { - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) { - SDL_ShowCursor(0); - } else { - SDL_SetCursor(state->mouse_cursors[cursor] ? state->mouse_cursors[cursor] : state->mouse_cursors[ImGuiMouseCursor_Arrow]); - SDL_ShowCursor(1); - } - } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); -} -IMGUI_API void ImGui_ImplSdl_RenderDrawData(ImGui_State *state) { - switch (state->renderer->current_backend) { - case renderer::Backend::OpenGL: - return ImGui_ImplSdlGL3_RenderDrawData(dynamic_cast(*state)); - - case renderer::Backend::Vulkan: - return ImGui_ImplSdlVulkan_RenderDrawData(dynamic_cast(*state)); - } -} - -// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. -// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. -// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. -// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -bool ImGui_ImplSdl_ProcessEvent(ImGui_State *state, SDL_Event *event) { - ImGuiIO &io = ImGui::GetIO(); - switch (event->type) { - case SDL_MOUSEWHEEL: { - if (event->wheel.x > 0) - io.MouseWheelH += 1; - if (event->wheel.x < 0) - io.MouseWheelH -= 1; - if (event->wheel.y > 0) - io.MouseWheel += 1; - if (event->wheel.y < 0) - io.MouseWheel -= 1; - return true; - } - case SDL_MOUSEBUTTONDOWN: { - if (event->button.button == SDL_BUTTON_LEFT) - state->mouse_pressed[0] = true; - if (event->button.button == SDL_BUTTON_RIGHT) - state->mouse_pressed[1] = true; - if (event->button.button == SDL_BUTTON_MIDDLE) - state->mouse_pressed[2] = true; - return true; - } - case SDL_TEXTINPUT: { - io.AddInputCharactersUTF8(event->text.text); - return true; - } - case SDL_KEYDOWN: - case SDL_KEYUP: { - int key = event->key.keysym.scancode; - IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)); - io.KeysDown[key] = (event->type == SDL_KEYDOWN); - io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); - io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); - io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); - io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); - return true; - } - } - return false; -} - -IMGUI_API void ImGui_ImplSdl_GetDrawableSize(ImGui_State *state, int &width, int &height) { - // Does this even matter? - switch (state->renderer->current_backend) { - case renderer::Backend::OpenGL: - SDL_GL_GetDrawableSize(state->window, &width, &height); - break; - - case renderer::Backend::Vulkan: - SDL_Vulkan_GetDrawableSize(state->window, &width, &height); - break; - - default: - LOG_ERROR("Missing ImGui init for backend {}.", static_cast(state->renderer->current_backend)); - } -} - -IMGUI_API ImTextureID ImGui_ImplSdl_CreateTexture(ImGui_State *state, void *data, int width, int height) { - switch (state->renderer->current_backend) { - case renderer::Backend::OpenGL: - return ImGui_ImplSdlGL3_CreateTexture(data, width, height); - - case renderer::Backend::Vulkan: - return ImGui_ImplSdlVulkan_CreateTexture(dynamic_cast(*state), data, width, height); - - default: - LOG_ERROR("Missing ImGui init for backend {}.", static_cast(state->renderer->current_backend)); - return {}; - } -} - -IMGUI_API void ImGui_ImplSdl_DeleteTexture(ImGui_State *state, ImTextureID texture) { - switch (state->renderer->current_backend) { - case renderer::Backend::OpenGL: - return ImGui_ImplSdlGL3_DeleteTexture(texture); - - case renderer::Backend::Vulkan: - return ImGui_ImplSdlVulkan_DeleteTexture(dynamic_cast(*state), texture); - - default: - LOG_ERROR("Missing ImGui init for backend {}.", static_cast(state->renderer->current_backend)); - } -} - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplSdl_InvalidateDeviceObjects(ImGui_State *state) { - switch (state->renderer->current_backend) { - case renderer::Backend::OpenGL: - return ImGui_ImplSdlGL3_InvalidateDeviceObjects(dynamic_cast(*state)); - - case renderer::Backend::Vulkan: - return ImGui_ImplSdlVulkan_InvalidateDeviceObjects(dynamic_cast(*state)); - - default: - LOG_ERROR("Missing ImGui init for backend {}.", static_cast(state->renderer->current_backend)); - } -} -IMGUI_API bool ImGui_ImplSdl_CreateDeviceObjects(ImGui_State *state) { - switch (state->renderer->current_backend) { - case renderer::Backend::OpenGL: - return ImGui_ImplSdlGL3_CreateDeviceObjects(dynamic_cast(*state)); - - case renderer::Backend::Vulkan: - return ImGui_ImplSdlVulkan_CreateDeviceObjects(dynamic_cast(*state)); - - default: - LOG_ERROR("Missing ImGui init for backend {}.", static_cast(state->renderer->current_backend)); - return false; - } -} - -void ImGui_Texture::init(ImGui_State *new_state, ImTextureID texture) { - assert(!texture_id); - - state = new_state; - texture_id = texture; -} - -void ImGui_Texture::init(ImGui_State *new_state, void *data, int width, int height) { - init(new_state, ImGui_ImplSdl_CreateTexture(new_state, data, width, height)); -} - -ImGui_Texture::operator bool() const { - return texture_id != nullptr; -} - -ImGui_Texture::operator ImTextureID() const { - return texture_id; -} - -bool ImGui_Texture::operator==(const ImGui_Texture &texture) { - return texture_id == texture.texture_id; -} - -ImGui_Texture &ImGui_Texture::operator=(ImGui_Texture &&texture) noexcept { - this->state = texture.state; - this->texture_id = texture.texture_id; - - texture.state = nullptr; - texture.texture_id = nullptr; - - return *this; -} - -ImGui_Texture::ImGui_Texture(ImGui_State *new_state, void *data, int width, int height) { - init(new_state, data, width, height); -} - -ImGui_Texture::ImGui_Texture(ImGui_Texture &&texture) noexcept - : state(texture.state) - , texture_id(texture.texture_id) { - texture.state = nullptr; - texture.texture_id = nullptr; -} - -ImGui_Texture::~ImGui_Texture() { - if (texture_id) - ImGui_ImplSdl_DeleteTexture(state, texture_id); -} diff --git a/vita3k/gui/src/imgui_impl_sdl_gl3.cpp b/vita3k/gui/src/imgui_impl_sdl_gl3.cpp deleted file mode 100644 index d68f68562f..0000000000 --- a/vita3k/gui/src/imgui_impl_sdl_gl3.cpp +++ /dev/null @@ -1,365 +0,0 @@ -// ImGui SDL2 binding with OpenGL3 -// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// Missing features: -// [ ] SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. -// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplSdlGL3_Init() so user can override the GLSL version e.g. "#version 150". -// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. -// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS). -// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes. -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. -// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. -// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. -// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include - -#include -#include - -// SDL,GL3W -#include -#include -#include - -// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -// If text or lines are blurry when integrating ImGui in your engine: in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) -void ImGui_ImplSdlGL3_RenderDrawData(ImGui_GLState &state) { - ImDrawData *draw_data = ImGui::GetDrawData(); - - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO &io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // Backup GL state - GLint last_framebuffer; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &last_framebuffer); - GLint last_active_texture; - glGetIntegerv(GL_ACTIVE_TEXTURE, &last_active_texture); - glActiveTexture(GL_TEXTURE0); - GLint last_program; - glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_sampler; - glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); - GLint last_array_buffer; - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - GLint last_element_array_buffer; - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); - GLint last_vertex_array; - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - GLint last_polygon_mode[2]; - glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; - glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; - glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - GLenum last_blend_src_rgb; - glGetIntegerv(GL_BLEND_SRC_RGB, (GLint *)&last_blend_src_rgb); - GLenum last_blend_dst_rgb; - glGetIntegerv(GL_BLEND_DST_RGB, (GLint *)&last_blend_dst_rgb); - GLenum last_blend_src_alpha; - glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint *)&last_blend_src_alpha); - GLenum last_blend_dst_alpha; - glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint *)&last_blend_dst_alpha); - GLenum last_blend_equation_rgb; - glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint *)&last_blend_equation_rgb); - GLenum last_blend_equation_alpha; - glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint *)&last_blend_equation_alpha); - GLboolean last_enable_blend = glIsEnabled(GL_BLEND); - GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); - GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); - GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - GLboolean last_color_mask[4]; - glGetBooleanv(GL_COLOR_WRITEMASK, last_color_mask); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - if (state.do_clear_screen) - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Setup viewport, orthographic projection matrix - glViewport(0, 0, fb_width, fb_height); - const float ortho_projection[4][4] = { - { 2.0f / io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f / -io.DisplaySize.y, 0.0f, 0.0f }, - { 0.0f, 0.0f, -1.0f, 0.0f }, - { -1.0f, 1.0f, 0.0f, 1.0f }, - }; - glUseProgram(state.shader_handle); - glUniform1i(state.attribute_location_tex, 0); - glUniformMatrix4fv(state.attribute_projection_mat, 1, GL_FALSE, &ortho_projection[0][0]); - glBindSampler(0, 0); // Rely on combined texture/sampler state. - - // Recreate the VAO every time - // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) - GLuint vao_handle = 0; - glGenVertexArrays(1, &vao_handle); - glBindVertexArray(vao_handle); - glBindBuffer(GL_ARRAY_BUFFER, state.vbo); - glEnableVertexAttribArray(state.attribute_position_location); - glEnableVertexAttribArray(state.attribute_uv_location); - glEnableVertexAttribArray(state.attribute_color_location); - glVertexAttribPointer(state.attribute_position_location, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid *)offsetof(ImDrawVert, pos)); - glVertexAttribPointer(state.attribute_uv_location, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid *)offsetof(ImDrawVert, uv)); - glVertexAttribPointer(state.attribute_color_location, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid *)offsetof(ImDrawVert, col)); - - // Draw - for (int n = 0; n < draw_data->CmdListsCount; n++) { - const ImDrawList *cmd_list = draw_data->CmdLists[n]; - const ImDrawIdx *idx_buffer_offset = 0; - - glBindBuffer(GL_ARRAY_BUFFER, state.vbo); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid *)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state.elements); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid *)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { - const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) { - pcmd->UserCallback(cmd_list, pcmd); - } else { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - // fix the offset issues as per instructed in release notes of imgui v1.86 - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset + pcmd->IdxOffset); - } - } - } - glDeleteVertexArrays(1, &vao_handle); - - // Restore modified GL state - glUseProgram(last_program); - glBindFramebuffer(GL_FRAMEBUFFER, last_framebuffer); - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindSampler(0, last_sampler); - glActiveTexture(last_active_texture); - glBindVertexArray(last_vertex_array); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); - glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); - glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); - if (last_enable_blend) - glEnable(GL_BLEND); - else - glDisable(GL_BLEND); - if (last_enable_cull_face) - glEnable(GL_CULL_FACE); - else - glDisable(GL_CULL_FACE); - if (last_enable_depth_test) - glEnable(GL_DEPTH_TEST); - else - glDisable(GL_DEPTH_TEST); - if (last_enable_scissor_test) - glEnable(GL_SCISSOR_TEST); - else - glDisable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); - glViewport(last_viewport[0], last_viewport[1], last_viewport[2], last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], last_scissor_box[2], last_scissor_box[3]); - glColorMask(last_color_mask[0], last_color_mask[1], last_color_mask[2], last_color_mask[3]); -} - -void ImGui_ImplSdlGL3_CreateFontsTexture(ImGui_GLState &state) { - // Build texture atlas - ImGuiIO &io = ImGui::GetIO(); - unsigned char *pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &state.font_texture); - glBindTexture(GL_TEXTURE_2D, state.font_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (ImTextureID)(intptr_t)state.font_texture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); -} - -bool ImGui_ImplSdlGL3_CreateDeviceObjects(ImGui_GLState &state) { - // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - - const GLchar *vertex_shader = "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 UV;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "void main()\n" - "{\n" - " Frag_UV = UV;\n" - " Frag_Color = Color;\n" - " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" - "}\n"; - - const GLchar *fragment_shader = "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main()\n" - "{\n" - " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n" - "}\n"; - - const GLchar *vertex_shader_with_version[2] = { state.glsl_version, vertex_shader }; - const GLchar *fragment_shader_with_version[2] = { state.glsl_version, fragment_shader }; - - state.shader_handle = glCreateProgram(); - state.vertex_handle = glCreateShader(GL_VERTEX_SHADER); - state.fragment_handle = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(state.vertex_handle, 2, vertex_shader_with_version, NULL); - glShaderSource(state.fragment_handle, 2, fragment_shader_with_version, NULL); - glCompileShader(state.vertex_handle); - glCompileShader(state.fragment_handle); - glAttachShader(state.shader_handle, state.vertex_handle); - glAttachShader(state.shader_handle, state.fragment_handle); - glLinkProgram(state.shader_handle); - - state.attribute_location_tex = glGetUniformLocation(state.shader_handle, "Texture"); - state.attribute_projection_mat = glGetUniformLocation(state.shader_handle, "ProjMtx"); - state.attribute_position_location = glGetAttribLocation(state.shader_handle, "Position"); - state.attribute_uv_location = glGetAttribLocation(state.shader_handle, "UV"); - state.attribute_color_location = glGetAttribLocation(state.shader_handle, "Color"); - - glGenBuffers(1, &state.vbo); - glGenBuffers(1, &state.elements); - - ImGui_ImplSdlGL3_CreateFontsTexture(state); - - // Restore modified GL state - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindVertexArray(last_vertex_array); - - state.init = true; - - return true; -} - -void ImGui_ImplSdlGL3_InvalidateDeviceObjects(ImGui_GLState &state) { - if (state.vbo) - glDeleteBuffers(1, &state.vbo); - if (state.elements) - glDeleteBuffers(1, &state.elements); - state.vbo = state.elements = 0; - - if (state.shader_handle && state.vertex_handle) - glDetachShader(state.shader_handle, state.vertex_handle); - if (state.vertex_handle) - glDeleteShader(state.vertex_handle); - state.vertex_handle = 0; - - if (state.shader_handle && state.fragment_handle) - glDetachShader(state.shader_handle, state.fragment_handle); - if (state.fragment_handle) - glDeleteShader(state.fragment_handle); - state.fragment_handle = 0; - - if (state.shader_handle) - glDeleteProgram(state.shader_handle); - state.shader_handle = 0; - - if (state.font_texture) { - glDeleteTextures(1, &state.font_texture); - ImGui::GetIO().Fonts->TexID = 0; - state.font_texture = 0; - } -} - -IMGUI_API ImGui_GLState *ImGui_ImplSdlGL3_Init(renderer::State *renderer, SDL_Window *window, const char *glsl_version) { - auto *state = new ImGui_GLState; - state->renderer = renderer; - state->window = window; - - // Store GL version string so we can refer to it later in case we recreate shaders. - if (glsl_version == NULL) - glsl_version = "#version 150"; - IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(state->glsl_version)); - strcpy(state->glsl_version, glsl_version); - strcat(state->glsl_version, "\n"); - - return state; -} - -void ImGui_ImplSdlGL3_Shutdown(ImGui_GLState &state) { - // Destroy OpenGL objects - ImGui_ImplSdlGL3_InvalidateDeviceObjects(state); -} - -IMGUI_API ImTextureID ImGui_ImplSdlGL3_CreateTexture(void *data, int width, int height) { - GLuint texture; - glGenTextures(1, &texture); - - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - return reinterpret_cast(static_cast(texture)); -} - -IMGUI_API void ImGui_ImplSdlGL3_DeleteTexture(ImTextureID texture) { - auto texture_name = static_cast(reinterpret_cast(texture)); - glDeleteTextures(1, &texture_name); -} diff --git a/vita3k/gui/src/imgui_impl_sdl_gl3_texture.cpp b/vita3k/gui/src/imgui_impl_sdl_gl3_texture.cpp new file mode 100644 index 0000000000..eaba630110 --- /dev/null +++ b/vita3k/gui/src/imgui_impl_sdl_gl3_texture.cpp @@ -0,0 +1,44 @@ +// Vita3K emulator project +// Copyright (C) 2024 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include +#include + +GLTextureData::GLTextureData(unsigned char *data, int width, int height) +{ + // Create a OpenGL texture identifier + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + // Setup filtering parameters for display + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Upload pixels into texture + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); +} + +GLTextureData::~GLTextureData() +{ + glDeleteTextures(1, &texture); +} + +ImTextureID GLTextureData::GetImTextureID() const +{ + return texture; +} diff --git a/vita3k/gui/src/imgui_impl_sdl_texture.cpp b/vita3k/gui/src/imgui_impl_sdl_texture.cpp new file mode 100644 index 0000000000..0a0246376f --- /dev/null +++ b/vita3k/gui/src/imgui_impl_sdl_texture.cpp @@ -0,0 +1,104 @@ +// Vita3K emulator project +// Copyright (C) 2024 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +ImGui_Texture::ImGui_Texture(renderer::State *state, FILE *f) { + this->loadTextureFromFile(state, f); +} + +ImGui_Texture::ImGui_Texture(renderer::State *state, unsigned char const *buffer, int len) { + this->loadTextureFromMemory(state, buffer, len); +} + +ImGui_Texture::operator bool() const { + if (!this->texture) { + return false; + } + return texture->GetImTextureID() != 0; +} + +ImGui_Texture::operator ImTextureID() const { + if (!this->texture) { + return 0; + } + return texture->GetImTextureID(); +} + +bool ImGui_Texture::operator==(const ImGui_Texture &texture) const { + return this->texture->GetImTextureID() == texture.texture->GetImTextureID(); +} + +ImGui_Texture &ImGui_Texture::operator=(ImGui_Texture &&texture) noexcept { + this->texture = std::move(texture.texture); + return *this; +} + +ImGui_Texture::ImGui_Texture(ImGui_Texture &&texture) noexcept + : texture(std::move(texture.texture)) { +} + +bool ImGui_Texture::loadTextureFromFile(renderer::State *state, FILE *f) { + int height = 0; + int width = 0; + + stbi_uc *data = stbi_load_from_file(f, &width, &height, nullptr, STBI_rgb_alpha); + if (!data) { + return false; + } + + bool result = this->loadTexture(state, data, height, width); + stbi_image_free(data); + return result; +} + +bool ImGui_Texture::loadTextureFromMemory(renderer::State *state, unsigned char const *buffer, int len) { + int height = 0; + int width = 0; + + stbi_uc *data = stbi_load_from_memory(buffer, len, &width, &height, nullptr, STBI_rgb_alpha); + if (!data) { + return false; + } + + bool result = this->loadTexture(state, data, height, width); + stbi_image_free(data); + return result; +} + +bool ImGui_Texture::loadTexture(renderer::State *state, unsigned char *data, int height, int width) { + switch (state->current_backend) { + case renderer::Backend::OpenGL: + this->height = height; + this->width = width; + this->texture = std::make_unique(data, width, height); + break; + case renderer::Backend::Vulkan: + this->height = height; + this->width = width; + this->texture = std::make_unique(dynamic_cast(state), data, width, height); + break; + } + + return this->texture != nullptr; +} diff --git a/vita3k/gui/src/imgui_impl_sdl_vulkan.cpp b/vita3k/gui/src/imgui_impl_sdl_vulkan.cpp deleted file mode 100644 index b32b7a4ffc..0000000000 --- a/vita3k/gui/src/imgui_impl_sdl_vulkan.cpp +++ /dev/null @@ -1,827 +0,0 @@ -// dear imgui: Renderer Backend for Vulkan -// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) - -// Implemented features: -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. - -// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. -// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. -// To build this on 32-bit systems and support texture changes: -// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files) -// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. -// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) -// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files) - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. -// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ - -// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. -// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. -// You will use those if you want to use this rendering backend in your engine/app. -// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by -// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. -// Read comments in imgui_impl_vulkan.h. - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. -// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). -// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. -// 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. -// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). -// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. -// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). -// 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. -// 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices. -// 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before. -// 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. -// 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. -// 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount(). -// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. -// 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like. -// 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int). -// 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display. -// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. -// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends. -// 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. -// 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. -// 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy. -// 2016-11-13: Vulkan: Fix validation layer warnings and errors and redeclare gl_PerVertex. -// 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources. -// 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. - -#include - -#include -#include -#include - -#include - -#include - -#include - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning(disable : 4127) // condition expression is constant -#endif - -//----------------------------------------------------------------------------- -// SHADERS -//----------------------------------------------------------------------------- - -// glsl_shader.vert, compiled with: -// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert -/* -#version 450 core -layout(location = 0) in vec2 aPos; -layout(location = 1) in vec2 aUV; -layout(location = 2) in vec4 aColor; -layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc; -out gl_PerVertex { vec4 gl_Position; }; -layout(location = 0) out struct { vec4 Color; vec2 UV; } Out; -void main() -{ - Out.Color = aColor; - Out.UV = aUV; - gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1); -} -*/ -static uint32_t __glsl_shader_vert_spv[] = { - 0x07230203, 0x00010000, 0x00080001, 0x0000002e, 0x00000000, 0x00020011, 0x00000001, 0x0006000b, - 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001, - 0x000a000f, 0x00000000, 0x00000004, 0x6e69616d, 0x00000000, 0x0000000b, 0x0000000f, 0x00000015, - 0x0000001b, 0x0000001c, 0x00030003, 0x00000002, 0x000001c2, 0x00040005, 0x00000004, 0x6e69616d, - 0x00000000, 0x00030005, 0x00000009, 0x00000000, 0x00050006, 0x00000009, 0x00000000, 0x6f6c6f43, - 0x00000072, 0x00040006, 0x00000009, 0x00000001, 0x00005655, 0x00030005, 0x0000000b, 0x0074754f, - 0x00040005, 0x0000000f, 0x6c6f4361, 0x0000726f, 0x00030005, 0x00000015, 0x00565561, 0x00060005, - 0x00000019, 0x505f6c67, 0x65567265, 0x78657472, 0x00000000, 0x00060006, 0x00000019, 0x00000000, - 0x505f6c67, 0x7469736f, 0x006e6f69, 0x00030005, 0x0000001b, 0x00000000, 0x00040005, 0x0000001c, - 0x736f5061, 0x00000000, 0x00060005, 0x0000001e, 0x73755075, 0x6e6f4368, 0x6e617473, 0x00000074, - 0x00050006, 0x0000001e, 0x00000000, 0x61635375, 0x0000656c, 0x00060006, 0x0000001e, 0x00000001, - 0x61725475, 0x616c736e, 0x00006574, 0x00030005, 0x00000020, 0x00006370, 0x00040047, 0x0000000b, - 0x0000001e, 0x00000000, 0x00040047, 0x0000000f, 0x0000001e, 0x00000002, 0x00040047, 0x00000015, - 0x0000001e, 0x00000001, 0x00050048, 0x00000019, 0x00000000, 0x0000000b, 0x00000000, 0x00030047, - 0x00000019, 0x00000002, 0x00040047, 0x0000001c, 0x0000001e, 0x00000000, 0x00050048, 0x0000001e, - 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x0000001e, 0x00000001, 0x00000023, 0x00000008, - 0x00030047, 0x0000001e, 0x00000002, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, - 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040017, - 0x00000008, 0x00000006, 0x00000002, 0x0004001e, 0x00000009, 0x00000007, 0x00000008, 0x00040020, - 0x0000000a, 0x00000003, 0x00000009, 0x0004003b, 0x0000000a, 0x0000000b, 0x00000003, 0x00040015, - 0x0000000c, 0x00000020, 0x00000001, 0x0004002b, 0x0000000c, 0x0000000d, 0x00000000, 0x00040020, - 0x0000000e, 0x00000001, 0x00000007, 0x0004003b, 0x0000000e, 0x0000000f, 0x00000001, 0x00040020, - 0x00000011, 0x00000003, 0x00000007, 0x0004002b, 0x0000000c, 0x00000013, 0x00000001, 0x00040020, - 0x00000014, 0x00000001, 0x00000008, 0x0004003b, 0x00000014, 0x00000015, 0x00000001, 0x00040020, - 0x00000017, 0x00000003, 0x00000008, 0x0003001e, 0x00000019, 0x00000007, 0x00040020, 0x0000001a, - 0x00000003, 0x00000019, 0x0004003b, 0x0000001a, 0x0000001b, 0x00000003, 0x0004003b, 0x00000014, - 0x0000001c, 0x00000001, 0x0004001e, 0x0000001e, 0x00000008, 0x00000008, 0x00040020, 0x0000001f, - 0x00000009, 0x0000001e, 0x0004003b, 0x0000001f, 0x00000020, 0x00000009, 0x00040020, 0x00000021, - 0x00000009, 0x00000008, 0x0004002b, 0x00000006, 0x00000028, 0x00000000, 0x0004002b, 0x00000006, - 0x00000029, 0x3f800000, 0x00050036, 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8, - 0x00000005, 0x0004003d, 0x00000007, 0x00000010, 0x0000000f, 0x00050041, 0x00000011, 0x00000012, - 0x0000000b, 0x0000000d, 0x0003003e, 0x00000012, 0x00000010, 0x0004003d, 0x00000008, 0x00000016, - 0x00000015, 0x00050041, 0x00000017, 0x00000018, 0x0000000b, 0x00000013, 0x0003003e, 0x00000018, - 0x00000016, 0x0004003d, 0x00000008, 0x0000001d, 0x0000001c, 0x00050041, 0x00000021, 0x00000022, - 0x00000020, 0x0000000d, 0x0004003d, 0x00000008, 0x00000023, 0x00000022, 0x00050085, 0x00000008, - 0x00000024, 0x0000001d, 0x00000023, 0x00050041, 0x00000021, 0x00000025, 0x00000020, 0x00000013, - 0x0004003d, 0x00000008, 0x00000026, 0x00000025, 0x00050081, 0x00000008, 0x00000027, 0x00000024, - 0x00000026, 0x00050051, 0x00000006, 0x0000002a, 0x00000027, 0x00000000, 0x00050051, 0x00000006, - 0x0000002b, 0x00000027, 0x00000001, 0x00070050, 0x00000007, 0x0000002c, 0x0000002a, 0x0000002b, - 0x00000028, 0x00000029, 0x00050041, 0x00000011, 0x0000002d, 0x0000001b, 0x0000000d, 0x0003003e, - 0x0000002d, 0x0000002c, 0x000100fd, 0x00010038 -}; - -// glsl_shader.frag, compiled with: -// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag -/* -#version 450 core -layout(location = 0) out vec4 fColor; -layout(set=0, binding=0) uniform sampler2D sTexture; -layout(location = 0) in struct { vec4 Color; vec2 UV; } In; -void main() -{ - fColor = In.Color * texture(sTexture, In.UV.st); -} -*/ -static uint32_t __glsl_shader_frag_spv[] = { - 0x07230203, 0x00010000, 0x00080001, 0x0000001e, 0x00000000, 0x00020011, 0x00000001, 0x0006000b, - 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001, - 0x0007000f, 0x00000004, 0x00000004, 0x6e69616d, 0x00000000, 0x00000009, 0x0000000d, 0x00030010, - 0x00000004, 0x00000007, 0x00030003, 0x00000002, 0x000001c2, 0x00040005, 0x00000004, 0x6e69616d, - 0x00000000, 0x00040005, 0x00000009, 0x6c6f4366, 0x0000726f, 0x00030005, 0x0000000b, 0x00000000, - 0x00050006, 0x0000000b, 0x00000000, 0x6f6c6f43, 0x00000072, 0x00040006, 0x0000000b, 0x00000001, - 0x00005655, 0x00030005, 0x0000000d, 0x00006e49, 0x00050005, 0x00000016, 0x78655473, 0x65727574, - 0x00000000, 0x00040047, 0x00000009, 0x0000001e, 0x00000000, 0x00040047, 0x0000000d, 0x0000001e, - 0x00000000, 0x00040047, 0x00000016, 0x00000022, 0x00000000, 0x00040047, 0x00000016, 0x00000021, - 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, 0x00030016, 0x00000006, - 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000004, 0x00040020, 0x00000008, 0x00000003, - 0x00000007, 0x0004003b, 0x00000008, 0x00000009, 0x00000003, 0x00040017, 0x0000000a, 0x00000006, - 0x00000002, 0x0004001e, 0x0000000b, 0x00000007, 0x0000000a, 0x00040020, 0x0000000c, 0x00000001, - 0x0000000b, 0x0004003b, 0x0000000c, 0x0000000d, 0x00000001, 0x00040015, 0x0000000e, 0x00000020, - 0x00000001, 0x0004002b, 0x0000000e, 0x0000000f, 0x00000000, 0x00040020, 0x00000010, 0x00000001, - 0x00000007, 0x00090019, 0x00000013, 0x00000006, 0x00000001, 0x00000000, 0x00000000, 0x00000000, - 0x00000001, 0x00000000, 0x0003001b, 0x00000014, 0x00000013, 0x00040020, 0x00000015, 0x00000000, - 0x00000014, 0x0004003b, 0x00000015, 0x00000016, 0x00000000, 0x0004002b, 0x0000000e, 0x00000018, - 0x00000001, 0x00040020, 0x00000019, 0x00000001, 0x0000000a, 0x00050036, 0x00000002, 0x00000004, - 0x00000000, 0x00000003, 0x000200f8, 0x00000005, 0x00050041, 0x00000010, 0x00000011, 0x0000000d, - 0x0000000f, 0x0004003d, 0x00000007, 0x00000012, 0x00000011, 0x0004003d, 0x00000014, 0x00000017, - 0x00000016, 0x00050041, 0x00000019, 0x0000001a, 0x0000000d, 0x00000018, 0x0004003d, 0x0000000a, - 0x0000001b, 0x0000001a, 0x00050057, 0x00000007, 0x0000001c, 0x00000017, 0x0000001b, 0x00050085, - 0x00000007, 0x0000001d, 0x00000012, 0x0000001c, 0x0003003e, 0x00000009, 0x0000001d, 0x000100fd, - 0x00010038 -}; - -//----------------------------------------------------------------------------- -// FUNCTIONS -//----------------------------------------------------------------------------- - -inline renderer::vulkan::VKState &get_renderer(ImGui_VulkanState &state) { - return dynamic_cast(*state.renderer); -} - -static void TextureUpdateDescriptorSet(ImGui_VulkanState &state, TextureState *texture) { - auto &vk_state = get_renderer(state); - // always has the same descriptor set - if (texture == state.Font) - return; - - if (texture->last_frame_used == state.frame_timestamp) - return; - texture->last_frame_used = state.frame_timestamp; - - texture->descriptor_set = state.DescriptorSets[state.DescriptorIdx]; - state.DescriptorIdx = (state.DescriptorIdx + 1) % TextureState::nb_descriptor_sets; - - // update descriptor set - vk::DescriptorImageInfo descr_image_info{ - .imageView = texture->image_view, - .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - }; - vk::WriteDescriptorSet write_descr{ - .dstSet = texture->descriptor_set, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - }; - write_descr.setImageInfo(descr_image_info); - vk_state.device.updateDescriptorSets(write_descr, {}); -} - -static void CreateOrResizeBuffer(ImGui_VulkanState &state, vk::Buffer &buffer, vma::Allocation &buffer_allocation, vk::DeviceSize &p_buffer_size, size_t new_size, vk::BufferUsageFlagBits usage) { - auto &vk_state = get_renderer(state); - if (buffer) - vk_state.allocator.destroyBuffer(buffer, buffer_allocation); - - vk::DeviceSize vertex_buffer_size_aligned = ((new_size - 1) / state.BufferMemoryAlignment + 1) * state.BufferMemoryAlignment; - vk::BufferCreateInfo buffer_info{ - .size = vertex_buffer_size_aligned, - .usage = usage, - .sharingMode = vk::SharingMode::eExclusive - }; - vma::AllocationCreateInfo alloc_info{ - .flags = vma::AllocationCreateFlagBits::eHostAccessSequentialWrite, - .usage = vma::MemoryUsage::eAuto - }; - std::tie(buffer, buffer_allocation) = vk_state.allocator.createBuffer(buffer_info, alloc_info); - p_buffer_size = vertex_buffer_size_aligned; -} - -static void ImGui_ImplVulkan_SetupRenderState(ImGui_VulkanState &state, ImDrawData *draw_data, vk::Pipeline pipeline, vk::CommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers *rb, int fb_width, int fb_height) { - // Bind pipeline: - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - - // Bind Vertex And Index Buffer: - if (draw_data->TotalVtxCount > 0) { - vk::DeviceSize vertex_offset = 0; - command_buffer.bindVertexBuffers(0, rb->VertexBuffer, vertex_offset); - command_buffer.bindIndexBuffer(rb->IndexBuffer, 0, sizeof(ImDrawIdx) == 2 ? vk::IndexType::eUint16 : vk::IndexType::eUint32); - } - - // Setup viewport: - { - vk::Viewport viewport{ - .x = 0, - .y = 0, - .width = static_cast(fb_width), - .height = static_cast(fb_height), - .minDepth = 0.f, - .maxDepth = 1.f - }; - command_buffer.setViewport(0, viewport); - } - - // Setup scale and translation: - // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - { - float scale[2]; - scale[0] = 2.0f / draw_data->DisplaySize.x; - scale[1] = 2.0f / draw_data->DisplaySize.y; - float translate[2]; - translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; - translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; - command_buffer.pushConstants(state.PipelineLayout, vk::ShaderStageFlagBits::eVertex, sizeof(float) * 0, sizeof(float) * 2, scale); - command_buffer.pushConstants(state.PipelineLayout, vk::ShaderStageFlagBits::eVertex, sizeof(float) * 2, sizeof(float) * 2, translate); - } -} - -// constexpr vk::IndexType imgui_index_type = sizeof(ImDrawIdx) == 2 ? vk::IndexType::eUint16 : vk::IndexType::eUint32; - -IMGUI_API ImGui_VulkanState *ImGui_ImplSdlVulkan_Init(renderer::State *renderer, SDL_Window *window) { - auto *state = new ImGui_VulkanState; - state->renderer = renderer; - state->window = window; - auto &vk_state = get_renderer(*state); - - vk::ShaderModuleCreateInfo vert_info{ - .codeSize = sizeof(__glsl_shader_vert_spv), - .pCode = reinterpret_cast(__glsl_shader_vert_spv) - }; - state->ShaderModuleVert = vk_state.device.createShaderModule(vert_info); - - vk::ShaderModuleCreateInfo frag_info{ - .codeSize = sizeof(__glsl_shader_frag_spv), - .pCode = reinterpret_cast(__glsl_shader_frag_spv) - }; - state->ShaderModuleFrag = vk_state.device.createShaderModule(frag_info); - - return state; -} - -IMGUI_API void ImGui_ImplSdlVulkan_Shutdown(ImGui_VulkanState &state) { - ImGui_ImplSdlVulkan_InvalidateDeviceObjects(state); - - get_renderer(state).device.destroy(state.ShaderModuleVert); - get_renderer(state).device.destroy(state.ShaderModuleFrag); -} - -static void ImGui_ImplSdlVulkan_DeletePipeline(ImGui_VulkanState &state) { - get_renderer(state).device.destroy(state.Pipeline); -} - -static bool ImGui_ImplSdlVulkan_CreatePipeline(ImGui_VulkanState &state) { - auto &vk_state = get_renderer(state); - - std::vector shader_stage_infos = { - vk::PipelineShaderStageCreateInfo{ - .stage = vk::ShaderStageFlagBits::eVertex, - .module = state.ShaderModuleVert, - .pName = "main" }, - vk::PipelineShaderStageCreateInfo{ - .stage = vk::ShaderStageFlagBits::eFragment, - .module = state.ShaderModuleFrag, - .pName = "main" }, - }; - - std::vector gui_pipeline_bindings = { - vk::VertexInputBindingDescription{ - .binding = 0, - .stride = sizeof(ImDrawVert) }, - }; - - std::vector gui_pipeline_attributes = { - vk::VertexInputAttributeDescription{ - .location = 0, - .binding = 0, - .format = vk::Format::eR32G32Sfloat, - .offset = 0 }, - vk::VertexInputAttributeDescription{ - .location = 1, - .binding = 0, - .format = vk::Format::eR32G32Sfloat, - .offset = sizeof(ImVec2) }, - vk::VertexInputAttributeDescription{ - .location = 2, - .binding = 0, - .format = vk::Format::eR8G8B8A8Unorm, - .offset = sizeof(ImVec2) * 2 }, - }; - - vk::PipelineVertexInputStateCreateInfo gui_pipeline_vertex_info{ - .vertexBindingDescriptionCount = static_cast(gui_pipeline_bindings.size()), - .pVertexBindingDescriptions = gui_pipeline_bindings.data(), // Bindings - .vertexAttributeDescriptionCount = static_cast(gui_pipeline_attributes.size()), - .pVertexAttributeDescriptions = gui_pipeline_attributes.data() // Attributes - }; - - vk::PipelineInputAssemblyStateCreateInfo gui_pipeline_assembly_info{ - .topology = vk::PrimitiveTopology::eTriangleList, - .primitiveRestartEnable = false - }; - - vk::PipelineViewportStateCreateInfo gui_pipeline_viewport_info{ - .viewportCount = 1, - .scissorCount = 1, - }; - - vk::PipelineRasterizationStateCreateInfo gui_pipeline_rasterization_info{ - .polygonMode = vk::PolygonMode::eFill, // Fill Polygons - .cullMode = vk::CullModeFlagBits::eNone, - .frontFace = vk::FrontFace::eCounterClockwise, // Counter Clockwise Face Forwards - .lineWidth = 1.0f // Line Width - }; - - vk::PipelineMultisampleStateCreateInfo gui_pipeline_multisample_info{ - .rasterizationSamples = vk::SampleCountFlagBits::e1, // No Multisampling - }; - - vk::PipelineDepthStencilStateCreateInfo gui_pipeline_depth_stencil_info{ - .depthCompareOp = vk::CompareOp::eAlways, - .minDepthBounds = 0.0f, - .maxDepthBounds = 1.0f - }; - - vk::PipelineColorBlendAttachmentState attachment_blending{ - true, // Enable Blending - vk::BlendFactor::eSrcAlpha, // Src Color - vk::BlendFactor::eOneMinusSrcAlpha, // Dst Color - vk::BlendOp::eAdd, // Color Blend Op - vk::BlendFactor::eOne, // Src Alpha - vk::BlendFactor::eOneMinusSrcAlpha, // Dst Alpha - vk::BlendOp::eAdd, // Alpha Blend Op - vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA - }; - - vk::PipelineColorBlendStateCreateInfo gui_pipeline_blend_info{ - .attachmentCount = 1, - .pAttachments = &attachment_blending - }; - - std::array dynamic_states = { - vk::DynamicState::eScissor, - vk::DynamicState::eViewport, - }; - - vk::PipelineDynamicStateCreateInfo gui_pipeline_dynamic_info{}; - gui_pipeline_dynamic_info.setDynamicStates(dynamic_states); - - vk::GraphicsPipelineCreateInfo gui_pipeline_info{ - .pVertexInputState = &gui_pipeline_vertex_info, - .pInputAssemblyState = &gui_pipeline_assembly_info, - .pViewportState = &gui_pipeline_viewport_info, - .pRasterizationState = &gui_pipeline_rasterization_info, - .pMultisampleState = &gui_pipeline_multisample_info, - .pDepthStencilState = &gui_pipeline_depth_stencil_info, - .pColorBlendState = &gui_pipeline_blend_info, - .pDynamicState = &gui_pipeline_dynamic_info, - .layout = state.PipelineLayout, - .renderPass = vk_state.screen_renderer.default_render_pass, - }; - gui_pipeline_info.setStages(shader_stage_infos); - - vk::ResultValue pipelineResultValue = vk_state.device.createGraphicsPipeline(vk::PipelineCache(), gui_pipeline_info, nullptr); - if (pipelineResultValue.result != vk::Result::eSuccess) { - LOG_ERROR("Failed to create Vulkan gui pipeline."); - return false; - } - - state.Pipeline = pipelineResultValue.value; - - return true; -} - -IMGUI_API void ImGui_ImplSdlVulkan_RenderDrawData(ImGui_VulkanState &state) { - ImDrawData *draw_data = ImGui::GetDrawData(); - auto &vk_state = get_renderer(state); - - if (vk_state.screen_renderer.swapchain_image_idx == ~0) { - // this happen in the game selection screen - if (!vk_state.screen_renderer.acquire_swapchain_image(true)) - return; - } else if (!vk_state.screen_renderer.current_cmd_buffer) { - // Can happen while resizing the window - return; - } - - state.CommandBuffer = vk_state.screen_renderer.current_cmd_buffer; - // uint32_t image_index = vk_state.screen_renderer.swapchain_image_idx; - - if (vk_state.screen_renderer.need_rebuild) { - ImGui_ImplSdlVulkan_DeletePipeline(state); - ImGui_ImplSdlVulkan_CreatePipeline(state); - vk_state.screen_renderer.need_rebuild = false; - } - - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); - if (fb_width <= 0 || fb_height <= 0) - return; - - // Allocate array to store enough vertex/index buffers - ImGui_ImplVulkanH_WindowRenderBuffers *wrb = &state.MainWindowRenderBuffers; - if (wrb->FrameRenderBuffers == NULL) { - wrb->Index = 0; - wrb->Count = vk_state.screen_renderer.swapchain_size; - wrb->FrameRenderBuffers = (ImGui_ImplVulkanH_FrameRenderBuffers *)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameRenderBuffers) * wrb->Count); - memset(wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkanH_FrameRenderBuffers) * wrb->Count); - } - IM_ASSERT(wrb->Count == vk_state.screen_renderer.swapchain_size); - wrb->Index = (wrb->Index + 1) % wrb->Count; - ImGui_ImplVulkanH_FrameRenderBuffers *rb = &wrb->FrameRenderBuffers[wrb->Index]; - - if (draw_data->TotalVtxCount > 0) { - // Create or resize the vertex/index buffers - size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); - size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); - if (!rb->VertexBuffer || rb->VertexBufferSize < vertex_size) - CreateOrResizeBuffer(state, rb->VertexBuffer, rb->VertexBufferAllocation, rb->VertexBufferSize, vertex_size, vk::BufferUsageFlagBits::eVertexBuffer); - if (!rb->IndexBuffer || rb->IndexBufferSize < index_size) - CreateOrResizeBuffer(state, rb->IndexBuffer, rb->IndexBufferAllocation, rb->IndexBufferSize, index_size, vk::BufferUsageFlagBits::eIndexBuffer); - - // Upload vertex/index data into a single contiguous GPU buffer - ImDrawVert *vtx_dst = static_cast(vk_state.allocator.mapMemory(rb->VertexBufferAllocation)); - ImDrawIdx *idx_dst = static_cast(vk_state.allocator.mapMemory(rb->IndexBufferAllocation)); - - for (int n = 0; n < draw_data->CmdListsCount; n++) { - const ImDrawList *cmd_list = draw_data->CmdLists[n]; - memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); - vtx_dst += cmd_list->VtxBuffer.Size; - idx_dst += cmd_list->IdxBuffer.Size; - } - - vk_state.allocator.flushAllocation(rb->VertexBufferAllocation, 0, rb->VertexBufferSize); - vk_state.allocator.flushAllocation(rb->IndexBufferAllocation, 0, rb->IndexBufferSize); - - vk_state.allocator.unmapMemory(rb->VertexBufferAllocation); - vk_state.allocator.unmapMemory(rb->IndexBufferAllocation); - } - - // Setup desired Vulkan state - ImGui_ImplVulkan_SetupRenderState(state, draw_data, state.Pipeline, state.CommandBuffer, rb, fb_width, fb_height); - - // Will project scissor/clipping rectangles into framebuffer space - ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports - ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) - - // Render command lists - // (Because we merged all buffers into a single one, we maintain our own offset into them) - int global_vtx_offset = 0; - int global_idx_offset = 0; - for (int n = 0; n < draw_data->CmdListsCount; n++) { - const ImDrawList *cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { - const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback != NULL) { - // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplVulkan_SetupRenderState(state, draw_data, state.Pipeline, state.CommandBuffer, rb, fb_width, fb_height); - else - pcmd->UserCallback(cmd_list, pcmd); - } else { - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); - ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - - // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds - if (clip_min.x < 0.0f) { - clip_min.x = 0.0f; - } - if (clip_min.y < 0.0f) { - clip_min.y = 0.0f; - } - if (clip_max.x > fb_width) { - clip_max.x = (float)fb_width; - } - if (clip_max.y > fb_height) { - clip_max.y = (float)fb_height; - } - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // Apply scissor/clipping rectangle - vk::Rect2D scissor{ - .offset = { static_cast(clip_min.x), static_cast(clip_min.y) }, - .extent = { static_cast(clip_max.x - clip_min.x), static_cast(clip_max.y - clip_min.y) } - }; - state.CommandBuffer.setScissor(0, scissor); - - // Bind DescriptorSet with font or user texture - TextureState *texture; - if (pcmd->TextureId) - texture = static_cast(pcmd->TextureId); - else - texture = state.Font; - - TextureUpdateDescriptorSet(state, texture); - - state.CommandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, state.PipelineLayout, 0, texture->descriptor_set, {}); - - // Draw - state.CommandBuffer.drawIndexed(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); - } - } - global_idx_offset += cmd_list->IdxBuffer.Size; - global_vtx_offset += cmd_list->VtxBuffer.Size; - } - - // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. - // Our last values will leak into user/application rendering IF: - // - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state - // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitely set that state. - // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering. - // In theory we should aim to backup/restore those values but I am not sure this is possible. - // We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644) - - // not necessary, we are not using a dynamic viewport - // VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } }; - // vkCmdSetScissor(command_buffer, 0, 1, &scissor); - - state.frame_timestamp++; -} - -IMGUI_API ImTextureID ImGui_ImplSdlVulkan_CreateTexture(ImGui_VulkanState &state, void *pixels, int width, int height, bool is_alpha) { - auto *texture = new TextureState; - - const size_t buffer_size = width * height * (is_alpha ? 1 : 4); - - vk::BufferCreateInfo buffer_info{ - .size = buffer_size, - .usage = vk::BufferUsageFlagBits::eTransferSrc, - .sharingMode = vk::SharingMode::eExclusive, - }; - - auto &vk_state = get_renderer(state); - - vma::AllocationInfo alloc_info; - auto [temp_buffer, temp_allocation] = vk_state.allocator.createBuffer(buffer_info, vkutil::vma_mapped_alloc, alloc_info); - - vk_state.allocator.invalidateAllocation(temp_allocation, 0, buffer_size); - - std::memcpy(alloc_info.pMappedData, pixels, buffer_size); - - vk_state.allocator.flushAllocation(temp_allocation, 0, buffer_size); - - const vk::Format format = is_alpha ? vk::Format::eR8Unorm : vk::Format::eR8G8B8A8Unorm; - - vk::ImageCreateInfo image_info{ - .imageType = vk::ImageType::e2D, - .format = format, - .extent = vk::Extent3D{ static_cast(width), static_cast(height), 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 - }; - - std::tie(texture->image, texture->allocation) = vk_state.allocator.createImage(image_info, vkutil::vma_auto_alloc); - - vk::CommandBuffer transfer_buffer = vkutil::create_single_time_command(vk_state.device, - vk_state.transfer_command_pool); - - vk::ImageMemoryBarrier image_transfer_optimal_barrier{ - .srcAccessMask = vk::AccessFlagBits(), - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, // Will be written by a transfer operation. - .oldLayout = vk::ImageLayout::eUndefined, // Old Layout - .newLayout = vk::ImageLayout::eTransferDstOptimal, // New Layout - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, // No Queue Family Transition - .image = texture->image, - .subresourceRange = vkutil::color_subresource_range // Subresource Range - }; - - transfer_buffer.pipelineBarrier( - vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, // Top Of Pipe -> Transfer Stage - vk::DependencyFlags(), // No Dependency Flags - 0, nullptr, // No Memory Barriers - 0, nullptr, // No Buffer Memory Barriers - 1, &image_transfer_optimal_barrier // 1 Image Memory Barrier - ); - - vk::BufferImageCopy region{ - 0, // Buffer Offset - static_cast(width), // Buffer Row Length - static_cast(height), // Buffer Height - vk::ImageSubresourceLayers{ - vk::ImageAspectFlagBits::eColor, // Aspects - 0, 0, 1 // First Layer/Level - }, - vk::Offset3D{ 0, 0, 0 }, // Image Offset - vk::Extent3D{ static_cast(width), static_cast(height), 1 } // Image Extent - }; - - transfer_buffer.copyBufferToImage( - temp_buffer, // Buffer - texture->image, // Image - vk::ImageLayout::eTransferDstOptimal, // Image Layout - 1, ®ion // Regions - ); - - vk::ImageMemoryBarrier image_shader_read_only_barrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits(), - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = texture->image, - .subresourceRange = vkutil::color_subresource_range - }; - - transfer_buffer.pipelineBarrier( - vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eBottomOfPipe, // Transfer -> Bottom of Pipe Stage - vk::DependencyFlags(), // No Dependency Flags - 0, nullptr, // No Memory Barriers - 0, nullptr, // No Buffer Barriers - 1, &image_shader_read_only_barrier // Image Memory Barriers - ); - - vkutil::end_single_time_command(vk_state.device, vk_state.transfer_queue, vk_state.transfer_command_pool, transfer_buffer); - vk_state.allocator.destroyBuffer(temp_buffer, temp_allocation); - - const vk::ComponentMapping mapping = is_alpha ? vk::ComponentMapping{ vk::ComponentSwizzle::eOne, vk::ComponentSwizzle::eOne, vk::ComponentSwizzle::eOne, vk::ComponentSwizzle::eR } - : vkutil::default_comp_mapping; - - vk::ImageViewCreateInfo font_view_info{ - .image = texture->image, - .viewType = vk::ImageViewType::e2D, - .format = format, - .components = mapping, - .subresourceRange = vkutil::color_subresource_range - }; - - texture->image_view = vk_state.device.createImageView(font_view_info); - - return texture; -} - -IMGUI_API void ImGui_ImplSdlVulkan_DeleteTexture(ImGui_VulkanState &state, ImTextureID texture) { - auto texture_ptr = static_cast(texture); - auto &vk_state = get_renderer(state); - - vk_state.device.waitIdle(); - vk_state.device.destroy(texture_ptr->image_view); - vk_state.allocator.destroyImage(texture_ptr->image, texture_ptr->allocation); - - delete texture_ptr; -} - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplSdlVulkan_InvalidateDeviceObjects(ImGui_VulkanState &state) { - auto &vk_state = get_renderer(state); - - ImGui_ImplSdlVulkan_DeleteTexture(state, state.Font); - ImGui_ImplSdlVulkan_DeletePipeline(state); - - vk_state.device.destroy(state.PipelineLayout); - vk_state.device.destroy(state.DescriptorSetLayout); - - vk_state.device.destroy(state.FontSampler); - - vk_state.device.destroy(state.DescriptorPool); -} - -IMGUI_API bool ImGui_ImplSdlVulkan_CreateDeviceObjects(ImGui_VulkanState &state) { - auto &vk_state = get_renderer(state); - - if (!state.FontSampler) { - vk::SamplerCreateInfo info{ - .magFilter = vk::Filter::eLinear, - .minFilter = vk::Filter::eLinear, - .mipmapMode = vk::SamplerMipmapMode::eLinear, - .addressModeU = vk::SamplerAddressMode::eRepeat, - .addressModeV = vk::SamplerAddressMode::eRepeat, - .addressModeW = vk::SamplerAddressMode::eRepeat, - .minLod = -1000, - .maxLod = 1000, - }; - state.FontSampler = vk_state.device.createSampler(info); - } - - if (!state.DescriptorSetLayout) { - vk::DescriptorSetLayoutBinding binding{ - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eFragment, - }; - binding.setImmutableSamplers(state.FontSampler); - vk::DescriptorSetLayoutCreateInfo info{}; - info.setBindings(binding); - state.DescriptorSetLayout = vk_state.device.createDescriptorSetLayout(info); - } - - if (!state.PipelineLayout) { - // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix - vk::PushConstantRange push_constants{ - .stageFlags = vk::ShaderStageFlagBits::eVertex, - .offset = sizeof(float) * 0, - .size = sizeof(float) * 4 - }; - vk::PipelineLayoutCreateInfo layout_info{}; - layout_info.setSetLayouts(state.DescriptorSetLayout); - layout_info.setPushConstantRanges(push_constants); - state.PipelineLayout = vk_state.device.createPipelineLayout(layout_info); - } - - ImGui_ImplSdlVulkan_CreatePipeline(state); - - { - // Hopefully imgui doesn't use more than 2048 different images - // within k frames (where k is the number of images in the swapchain) - vk::DescriptorPoolSize pool_size{ - .type = vk::DescriptorType::eCombinedImageSampler, - .descriptorCount = TextureState::nb_descriptor_sets + 1 - }; - vk::DescriptorPoolCreateInfo pool_info{ - .maxSets = TextureState::nb_descriptor_sets + 1, - }; - pool_info.setPoolSizes(pool_size); - state.DescriptorPool = vk_state.device.createDescriptorPool(pool_info); - vk::DescriptorSetAllocateInfo descr_set_info{ - .descriptorPool = state.DescriptorPool, - }; - std::vector descr_set_layouts(TextureState::nb_descriptor_sets + 1, state.DescriptorSetLayout); - descr_set_info.setSetLayouts(descr_set_layouts); - state.DescriptorSets = vk_state.device.allocateDescriptorSets(descr_set_info); - } - - // Create ImGui Texture - { - ImGuiIO &io = ImGui::GetIO(); - - uint8_t *pixels; - int width, height; - io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height); - - io.Fonts->TexID = ImGui_ImplSdlVulkan_CreateTexture(state, pixels, width, height, true); - state.Font = static_cast(io.Fonts->TexID); - - // reserve and remove the last descriptor set for the font - state.Font->descriptor_set = state.DescriptorSets[TextureState::nb_descriptor_sets]; - state.DescriptorSets.resize(TextureState::nb_descriptor_sets); - - // update this descriptor setonce and for all - vk::DescriptorImageInfo descr_image_info{ - .imageView = state.Font->image_view, - .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - }; - vk::WriteDescriptorSet write_descr{ - .dstSet = state.Font->descriptor_set, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - }; - write_descr.setImageInfo(descr_image_info); - get_renderer(state).device.updateDescriptorSets(write_descr, {}); - } - - state.init = true; - - return true; -} diff --git a/vita3k/gui/src/imgui_impl_sdl_vulkan_texture.cpp b/vita3k/gui/src/imgui_impl_sdl_vulkan_texture.cpp new file mode 100644 index 0000000000..a7eee6e92a --- /dev/null +++ b/vita3k/gui/src/imgui_impl_sdl_vulkan_texture.cpp @@ -0,0 +1,273 @@ +// Vita3K emulator project +// Copyright (C) 2024 Vita3K team +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include +#include + +#include + +VkDescriptorPool imgui_descriptor_pool; + +// Helper function to find Vulkan memory type bits. See ImGui_ImplVulkan_MemoryType() in imgui_impl_vulkan.cpp +uint32_t findMemoryType(vk::PhysicalDevice physicalDevice, uint32_t type_filter, vk::MemoryPropertyFlags properties) { + vk::PhysicalDeviceMemoryProperties mem_properties = physicalDevice.getMemoryProperties(); + + for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) + if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) + return i; + + return 0xFFFFFFFF; // Unable to find memoryType +} + +VKTextureData::VKTextureData(renderer::vulkan::VKState *vk_state, unsigned char *data, int width, int height) { + this->state = vk_state; + // Calculate allocation size (in number of bytes) + size_t image_size = width * height * 4; + + // Create the Vulkan image. + { + vk::ImageCreateInfo info = {}; + info.setImageType(vk::ImageType::e2D) + .setFormat(vk::Format::eR8G8B8A8Unorm) + .setExtent(vk::Extent3D{ static_cast(width), static_cast(height), 1 }) + .setMipLevels(1) + .setArrayLayers(1) + .setSamples(vk::SampleCountFlagBits::e1) + .setTiling(vk::ImageTiling::eOptimal) + .setUsage(vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst) + .setSharingMode(vk::SharingMode::eExclusive) + .setInitialLayout(vk::ImageLayout::eUndefined); + Image = vk_state->device.createImage(info); + + vk::MemoryRequirements req = vk_state->device.getImageMemoryRequirements(Image); + + vk::MemoryAllocateInfo alloc_info{}; + alloc_info.setAllocationSize(req.size) + .setMemoryTypeIndex(findMemoryType(vk_state->physical_device, req.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal)); + ImageMemory = vk_state->device.allocateMemory(alloc_info); + + vk_state->device.bindImageMemory(Image, ImageMemory, 0); + } + + // Create the Image View + { + vk::ImageViewCreateInfo info{}; + info.setImage(Image) + .setViewType(vk::ImageViewType::e2D) + .setFormat(vk::Format::eR8G8B8A8Unorm) + .setSubresourceRange(vk::ImageSubresourceRange{ + vk::ImageAspectFlagBits::eColor, // aspectMask + 0, // baseMipLevel + 1, // levelCount + 0, // baseArrayLayer + 1 // layerCount + }); + ImageView = vk_state->device.createImageView(info); + } + + // Create Sampler + { + // Create the sampler create info structure + vk::SamplerCreateInfo sampler_info{}; + sampler_info.setMagFilter(vk::Filter::eLinear) + .setMinFilter(vk::Filter::eLinear) + .setMipmapMode(vk::SamplerMipmapMode::eLinear) + .setAddressModeU(vk::SamplerAddressMode::eRepeat) + .setAddressModeV(vk::SamplerAddressMode::eRepeat) + .setAddressModeW(vk::SamplerAddressMode::eRepeat) + .setMinLod(-1000.0f) + .setMaxLod(1000.0f) + .setMaxAnisotropy(1.0f); + + // Create the sampler + Sampler = vk_state->device.createSampler(sampler_info); + } + + // Create Descriptor Set using ImGUI's implementation + DS = ImGui_ImplVulkan_AddTexture(Sampler, ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // Create Upload Buffer + { + // Create the buffer create info structure + vk::BufferCreateInfo buffer_info{}; + buffer_info.setSize(image_size) + .setUsage(vk::BufferUsageFlagBits::eTransferSrc) + .setSharingMode(vk::SharingMode::eExclusive); + + // Create the buffer + UploadBuffer = vk_state->device.createBuffer(buffer_info); + + // Get memory requirements + vk::MemoryRequirements req = vk_state->device.getBufferMemoryRequirements(UploadBuffer); + + // Allocate memory + vk::MemoryAllocateInfo alloc_info{}; + alloc_info.setAllocationSize(req.size) + .setMemoryTypeIndex(findMemoryType(vk_state->physical_device, req.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible)); + + UploadBufferMemory = vk_state->device.allocateMemory(alloc_info); + + // Bind buffer memory + vk_state->device.bindBufferMemory(UploadBuffer, UploadBufferMemory, 0); + } + + // Upload to Buffer: + { + void *map = nullptr; + vk::Result result = vk_state->device.mapMemory(UploadBufferMemory, 0, image_size, {}, &map); + + // Copy data to the mapped memory + memcpy(map, data, image_size); + + // Create a mapped memory range + vk::MappedMemoryRange range{}; + range.setMemory(UploadBufferMemory) + .setSize(image_size); + + // Flush the mapped memory range + result = vk_state->device.flushMappedMemoryRanges(1, &range); + + // Unmap the memory + vk_state->device.unmapMemory(UploadBufferMemory); + } + + // Create a command buffer that will perform following steps when hit in the command queue. + // TODO: this works in the example, but may need input if this is an acceptable way to access the pool/create the command buffer. + vk::CommandPool command_pool = vk_state->frames[vk_state->current_frame_idx].render_pool; + vk::CommandBuffer command_buffer; + { + vk::CommandBufferAllocateInfo alloc_info{}; + alloc_info.setCommandPool(command_pool) + .setLevel(vk::CommandBufferLevel::ePrimary) + .setCommandBufferCount(1); + + vk_state->device.allocateCommandBuffers(&alloc_info, &command_buffer); + + vk::CommandBufferBeginInfo begin_info{}; + begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + command_buffer.begin(begin_info); + } + + // Copy to Image + { + // Create the image memory barrier for the copy operation + vk::ImageMemoryBarrier copy_barrier{}; + copy_barrier.setSrcAccessMask({}) + .setDstAccessMask(vk::AccessFlagBits::eTransferWrite) + .setOldLayout(vk::ImageLayout::eUndefined) + .setNewLayout(vk::ImageLayout::eTransferDstOptimal) + .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setImage(Image) + .setSubresourceRange(vk::ImageSubresourceRange{ + vk::ImageAspectFlagBits::eColor, // aspectMask + 0, // baseMipLevel + 1, // levelCount + 0, // baseArrayLayer + 1 // layerCount + }); + + // Pipeline barrier for the copy operation + command_buffer.pipelineBarrier( + vk::PipelineStageFlagBits::eHost, + vk::PipelineStageFlagBits::eTransfer, + {}, // dependency flags + 0, nullptr, // memory barriers + 0, nullptr, // buffer memory barriers + 1, ©_barrier // image memory barriers + ); + + // Define the buffer to image copy region + vk::BufferImageCopy region{}; + region.setBufferOffset(0) + .setBufferRowLength(0) + .setBufferImageHeight(0) + .setImageSubresource(vk::ImageSubresourceLayers{ + vk::ImageAspectFlagBits::eColor, // aspectMask + 0, // mipLevel + 0, // baseArrayLayer + 1 // layerCount + }) + .setImageOffset({ 0, 0, 0 }) + .setImageExtent({ static_cast(width), static_cast(height), 1 }); + + // Copy buffer to image + command_buffer.copyBufferToImage( + UploadBuffer, + Image, + vk::ImageLayout::eTransferDstOptimal, + 1, ®ion); + + // Create the image memory barrier for the use operation + vk::ImageMemoryBarrier use_barrier{}; + use_barrier.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite) + .setDstAccessMask(vk::AccessFlagBits::eShaderRead) + .setOldLayout(vk::ImageLayout::eTransferDstOptimal) + .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) + .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setImage(Image) + .setSubresourceRange(vk::ImageSubresourceRange{ + vk::ImageAspectFlagBits::eColor, // aspectMask + 0, // baseMipLevel + 1, // levelCount + 0, // baseArrayLayer + 1 // layerCount + }); + + // Pipeline barrier for the use operation + command_buffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eFragmentShader, + {}, // dependency flags + 0, nullptr, // memory barriers + 0, nullptr, // buffer memory barriers + 1, &use_barrier // image memory barriers + ); + } + + // End command buffer + { + // Create the submit info structure + vk::SubmitInfo end_info{}; + end_info.setCommandBufferCount(1) + .setPCommandBuffers(&command_buffer); + + // End the command buffer + command_buffer.end(); + + // Submit the command buffer + vk_state->general_queue.submit(1, &end_info, nullptr); + vk_state->device.waitIdle(); + } +} + +VKTextureData::~VKTextureData() { + this->state->device.waitIdle(); + this->state->device.freeMemory(UploadBufferMemory); + this->state->device.destroyBuffer(UploadBuffer); + this->state->device.destroySampler(Sampler); + this->state->device.destroyImageView(ImageView); + this->state->device.destroyImage(Image); + this->state->device.freeMemory(ImageMemory); + + ImGui_ImplVulkan_RemoveTexture(DS); +} + +ImTextureID VKTextureData::GetImTextureID() const { + return (ImTextureID)this->DS; +} diff --git a/vita3k/gui/src/information_bar.cpp b/vita3k/gui/src/information_bar.cpp index 3ba3907d60..1ef870379b 100644 --- a/vita3k/gui/src/information_bar.cpp +++ b/vita3k/gui/src/information_bar.cpp @@ -33,7 +33,6 @@ #include #include -#include namespace gui { @@ -91,8 +90,6 @@ void erase_app_notice(GuiState &gui, const std::string &title_id) { static bool init_notice_icon(GuiState &gui, EmuEnvState &emuenv, const fs::path &content_path, const NoticeList &info) { gui.notice_info_icon[info.time] = {}; - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; if (!vfs::read_file(VitaIoDevice::ux0, buffer, emuenv.pref_path, content_path)) { @@ -109,15 +106,10 @@ static bool init_notice_icon(GuiState &gui, EmuEnvState &emuenv, const fs::path } } } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid icon for notice id: {} in path {}.", info.id, content_path); - return false; - } - gui.notice_info_icon[info.time].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); - return gui.notice_info_icon.contains(info.time); + gui.notice_info_icon[info.time].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); + + return true; } static bool set_notice_info(GuiState &gui, EmuEnvState &emuenv, const NoticeList &info) { @@ -514,8 +506,6 @@ static void draw_notice_info(GuiState &gui, EmuEnvState &emuenv) { ImGui::SameLine(0.f, 20.f); if (ImGui::Button(common["ok"].c_str(), BUTTON_SIZE) || ImGui::IsKeyPressed(static_cast(emuenv.cfg.keyboard_button_cross))) { notice_info.clear(); - for (auto ¬ice : gui.notice_info_icon) - notice.second = {}; gui.notice_info_icon.clear(); notice_list["global"].clear(); notice_list[emuenv.io.user_id].clear(); diff --git a/vita3k/gui/src/live_area.cpp b/vita3k/gui/src/live_area.cpp index 85f9cf7db4..b49c9ab6b1 100644 --- a/vita3k/gui/src/live_area.cpp +++ b/vita3k/gui/src/live_area.cpp @@ -33,7 +33,6 @@ #include #include -#include namespace gui { @@ -242,8 +241,6 @@ void init_live_area(GuiState &gui, EmuEnvState &emuenv, const std::string &app_p name["livearea-background"].erase(remove_if(name["livearea-background"].begin(), name["livearea-background"].end(), isspace), name["livearea-background"].end()); for (const auto &contents : name) { - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; if (contents.second.empty()) { @@ -263,14 +260,8 @@ void init_live_area(GuiState &gui, EmuEnvState &emuenv, const std::string &app_p LOG_WARN("Contents {} '{}' Not found for title {} [{}].", contents.first, contents.second, app_path, APP_INDEX->title); continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid Live Area Contents for title {} [{}].", app_path, APP_INDEX->title); - continue; - } - gui.live_area_contents[app_path][contents.first].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.live_area_contents[app_path][contents.first].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } std::map>> items_name; @@ -437,8 +428,6 @@ void init_live_area(GuiState &gui, EmuEnvState &emuenv, const std::string &app_p bg_name.erase(remove(bg_name.begin(), bg_name.end(), '\n'), bg_name.end()); bg_name.erase(remove_if(bg_name.begin(), bg_name.end(), isspace), bg_name.end()); - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; if (app_device == VitaIoDevice::vs0) @@ -451,15 +440,10 @@ void init_live_area(GuiState &gui, EmuEnvState &emuenv, const std::string &app_p LOG_WARN("background, Id: {}, Name: '{}', Not found for title: {} [{}].", item.first, bg_name, app_path, APP_INDEX->title); continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Frame: {}, Invalid Live Area Contents for title: {} [{}].", item.first, app_path, APP_INDEX->title); - continue; - } - items_size[app_path][item.first]["background"] = ImVec2(float(width), float(height)); - gui.live_items[app_path][item.first]["background"].emplace_back(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + auto &live_item_background = gui.live_items[app_path][item.first]["background"].emplace_back(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); + + items_size[app_path][item.first]["background"] = ImVec2(float( live_item_background.width), float(live_item_background.height)); } } @@ -469,8 +453,6 @@ void init_live_area(GuiState &gui, EmuEnvState &emuenv, const std::string &app_p img_name.erase(remove(img_name.begin(), img_name.end(), '\n'), img_name.end()); img_name.erase(remove_if(img_name.begin(), img_name.end(), isspace), img_name.end()); - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; if (app_device == VitaIoDevice::vs0) @@ -483,15 +465,10 @@ void init_live_area(GuiState &gui, EmuEnvState &emuenv, const std::string &app_p LOG_WARN("Image, Id: {} Name: '{}', Not found for title {} [{}].", item.first, img_name, app_path, APP_INDEX->title); continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Frame: {}, Invalid Live Area Contents for title: {} [{}].", item.first, app_path, APP_INDEX->title); - continue; - } - items_size[app_path][item.first]["image"] = ImVec2(float(width), float(height)); - gui.live_items[app_path][item.first]["image"].emplace_back(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + auto &live_item_image = gui.live_items[app_path][item.first]["image"].emplace_back(emuenv.renderer.get(), &buffer[0], static_cast(buffer.size())); + + items_size[app_path][item.first]["image"] = ImVec2(float(live_item_image.width), float(live_item_image.height)); } } } diff --git a/vita3k/gui/src/manual.cpp b/vita3k/gui/src/manual.cpp index 21665bc020..bf0413c6f0 100644 --- a/vita3k/gui/src/manual.cpp +++ b/vita3k/gui/src/manual.cpp @@ -26,8 +26,6 @@ #include -#include - namespace gui { void open_manual(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path) { @@ -90,7 +88,6 @@ bool init_manual(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path manual_path /= lang; if (fs::exists(APP_PATH / manual_path) && !fs::is_empty(APP_PATH / manual_path)) { - int32_t width, height; for (const auto &manual : fs::directory_iterator(APP_PATH / manual_path)) { if (manual.path().extension() == ".png") { const auto page_path = manual_path / manual.path().filename(); @@ -103,23 +100,13 @@ bool init_manual(GuiState &gui, EmuEnvState &emuenv, const std::string &app_path return false; } - // Load image data from memory buffer and check if it's valid - stbi_uc *data = stbi_load_from_memory(buffer.data(), static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid manual image for title: {} [{}] in path: {}.", app_path, APP_INDEX->title, page_path); - return false; - } - // Add manual image to vector - gui.manuals.emplace_back(gui.imgui_state.get(), data, width, height); + auto &manual_image = gui.manuals.emplace_back(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); // Calculate height of the page - const auto ratio = static_cast(width) / static_cast(height); - height = static_cast(960.f / ratio); + const auto ratio = static_cast(manual_image.width) / static_cast(manual_image.height); + int32_t height = static_cast(960.f / ratio); height_manual_pages.push_back(height); - - // Free image data - stbi_image_free(data); } } } diff --git a/vita3k/gui/src/settings.cpp b/vita3k/gui/src/settings.cpp index 8654458be7..88a30e05bc 100644 --- a/vita3k/gui/src/settings.cpp +++ b/vita3k/gui/src/settings.cpp @@ -31,7 +31,6 @@ #include #include -#include #undef ERROR @@ -164,8 +163,6 @@ static void get_themes_list(GuiState &gui, EmuEnvState &emuenv) { for (const auto &[content_id, theme] : themes_info) { for (const auto &[preview_type, theme_name] : theme_preview_name[content_id]) { if (!theme_name.empty()) { - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; if (content_id == "default") @@ -177,14 +174,8 @@ static void get_themes_list(GuiState &gui, EmuEnvState &emuenv) { LOG_WARN("Background, Name: '{}', Not found for title: {} [{}].", theme_name, content_id, theme.title); continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid Background for title: {} [{}].", content_id, theme.title); - continue; - } - gui.themes_preview[content_id][preview_type].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.themes_preview[content_id][preview_type].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } } } @@ -617,7 +608,7 @@ void draw_settings(GuiState &gui, EmuEnvState &emuenv) { std::filesystem::path image_path = ""; host::dialog::filesystem::Result result = host::dialog::filesystem::open_file(image_path, { { "Image file", { "bmp", "gif", "jpg", "png", "tif" } } }); - if ((result == host::dialog::filesystem::Result::SUCCESS) && init_user_start_background(gui, fs_utils::path_to_utf8(image_path.native()))) { + if ((result == host::dialog::filesystem::Result::SUCCESS) && init_user_start_background(gui, emuenv, fs_utils::path_to_utf8(image_path.native()))) { gui.users[emuenv.io.user_id].start_path = fs_utils::path_to_utf8(image_path.native()); gui.users[emuenv.io.user_id].start_type = "image"; save_user(gui, emuenv, emuenv.io.user_id); diff --git a/vita3k/gui/src/settings_dialog.cpp b/vita3k/gui/src/settings_dialog.cpp index 158b5fb087..791b980d6f 100644 --- a/vita3k/gui/src/settings_dialog.cpp +++ b/vita3k/gui/src/settings_dialog.cpp @@ -1035,7 +1035,6 @@ void draw_settings_dialog(GuiState &gui, EmuEnvState &emuenv) { if (!gui.user_backgrounds.empty()) { ImGui::Spacing(); if (ImGui::Button(lang.gui["clean_user_backgrounds"].c_str())) { - gui.user_backgrounds[gui.users[emuenv.io.user_id].backgrounds[gui.current_user_bg]] = {}; gui.user_backgrounds.clear(); if (!gui.theme_backgrounds.empty()) gui.users[emuenv.io.user_id].use_theme_bg = true; diff --git a/vita3k/gui/src/themes.cpp b/vita3k/gui/src/themes.cpp index 34f0d05a11..08d06d14a9 100644 --- a/vita3k/gui/src/themes.cpp +++ b/vita3k/gui/src/themes.cpp @@ -25,7 +25,6 @@ #include #include -#include namespace gui { @@ -39,28 +38,21 @@ std::string get_theme_title_from_buffer(const vfs::FileBuffer &buffer) { static constexpr ImVec2 background_size(960.f, 512.f), background_preview_size(226.f, 128.f); bool init_user_background(GuiState &gui, EmuEnvState &emuenv, const std::string &background_path) { + gui.user_backgrounds[background_path] = {}; + auto background_path_path = fs_utils::utf8_to_path(background_path); if (!fs::exists(background_path_path)) { LOG_WARN("Background doesn't exist: {}.", background_path_path); return false; } - int32_t width = 0; - int32_t height = 0; - FILE *f = FOPEN(background_path_path.c_str(), "rb"); - stbi_uc *data = stbi_load_from_file(f, &width, &height, nullptr, STBI_rgb_alpha); - - if (!data) { - LOG_ERROR("Invalid or corrupted background: {}.", background_path_path); - return false; - } - - gui.user_backgrounds[background_path] = {}; - gui.user_backgrounds[background_path].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.user_backgrounds[background_path].loadTextureFromFile(emuenv.renderer.get(), f); fclose(f); + int32_t width = gui.user_backgrounds[background_path].width; + int32_t height = gui.user_backgrounds[background_path].height; + // Resize background to fit screen size if needed (keep aspect ratio) auto &user_background = gui.user_backgrounds_infos[background_path]; @@ -76,7 +68,7 @@ bool init_user_background(GuiState &gui, EmuEnvState &emuenv, const std::string user_background.prev_pos = ImVec2((background_preview_size.x / 2.f) - (user_background.prev_size.x / 2.f), (background_preview_size.y / 2.f) - (user_background.prev_size.y / 2.f)); user_background.pos = ImVec2((background_size.x / 2.f) - (user_background.size.x / 2.f), (background_size.y / 2.f) - (user_background.size.y / 2.f)); - return gui.user_backgrounds.contains(background_path); + return true; } bool init_user_backgrounds(GuiState &gui, EmuEnvState &emuenv) { @@ -154,8 +146,6 @@ void init_theme_start_background(GuiState &gui, EmuEnvState &emuenv, const std:: break; } - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; if (theme_start_name.empty()) { @@ -173,17 +163,11 @@ void init_theme_start_background(GuiState &gui, EmuEnvState &emuenv, const std:: LOG_WARN("Background not found: '{}', for content id: {}.", theme_start_name, content_id); return; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid Background: '{}' for content id: {}.", theme_start_name, content_id); - return; - } - gui.start_background.init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.start_background.loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } -bool init_user_start_background(GuiState &gui, const std::string &image_path) { +bool init_user_start_background(GuiState &gui, EmuEnvState &emuenv, const std::string &image_path) { const fs::path image_path_path = fs_utils::utf8_to_path(image_path); if (!fs::exists(image_path_path)) { LOG_WARN("Image doesn't exist: {}.", image_path); @@ -193,22 +177,11 @@ bool init_user_start_background(GuiState &gui, const std::string &image_path) { start_param = {}; gui.start_background = {}; - int32_t width = 0; - int32_t height = 0; - FILE *f = FOPEN(image_path_path.c_str(), "rb"); - stbi_uc *data = stbi_load_from_file(f, &width, &height, nullptr, STBI_rgb_alpha); - - if (!data) { - LOG_ERROR("Invalid or corrupted image: {}.", image_path); - return false; - } - - gui.start_background.init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.start_background.loadTextureFromFile(emuenv.renderer.get(), f); fclose(f); - return gui.start_background; + return true; } bool init_theme(GuiState &gui, EmuEnvState &emuenv, const std::string &content_id) { @@ -283,8 +256,6 @@ bool init_theme(GuiState &gui, EmuEnvState &emuenv, const std::string &content_i notice_name[NoticeIcon::NEW] = info_bar_prop.child("m_newNoticeFilePath").text().as_string(); for (const auto ¬ice : notice_name) { - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; const auto type = notice.first; @@ -296,13 +267,8 @@ bool init_theme(GuiState &gui, EmuEnvState &emuenv, const std::string &content_i LOG_WARN("Notice icon, Name: '{}', Not found for content id: {}.", name, content_id); continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid notice icon for content id: {}.", content_id); - continue; - } - gui.theme_information_bar_notice[type].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + + gui.theme_information_bar_notice[type].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } } } else @@ -322,8 +288,6 @@ bool init_theme(GuiState &gui, EmuEnvState &emuenv, const std::string &content_i } for (const auto &icon : theme_icon_name) { - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; const auto &title_id = icon.first; @@ -341,19 +305,11 @@ bool init_theme(GuiState &gui, EmuEnvState &emuenv, const std::string &content_i } else LOG_INFO("Default icon found for system App {}.", title_id); } - stbi_uc *data = stbi_load_from_memory(buffer.data(), static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Name: '{}', Invalid icon for content id: {}.", name, content_id); - continue; - } - gui.app_selector.sys_apps_icon[title_id].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.app_selector.sys_apps_icon[title_id].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } for (const auto &bg : theme_bg_name) { - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; if (content_id == "default") @@ -365,14 +321,8 @@ bool init_theme(GuiState &gui, EmuEnvState &emuenv, const std::string &content_i LOG_WARN("Background not found: '{}', for content id: {}.", bg, content_id); continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid Background: '{}', for content id: {}.", bg, content_id); - continue; - } - gui.theme_backgrounds.emplace_back(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.theme_backgrounds.emplace_back().loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } return !gui.theme_backgrounds.empty(); diff --git a/vita3k/gui/src/trophy_collection.cpp b/vita3k/gui/src/trophy_collection.cpp index 3a90ef3c71..dc72bbc14f 100644 --- a/vita3k/gui/src/trophy_collection.cpp +++ b/vita3k/gui/src/trophy_collection.cpp @@ -29,7 +29,6 @@ #include #include -#include namespace gui { using namespace np::trophy; @@ -235,8 +234,6 @@ void init_trophy_collection(GuiState &gui, EmuEnvState &emuenv) { np_com_id_list.push_back({ np_com_id, np_com_id_info[np_com_id].name["000"], progress, updated }); for (const auto &group : np_com_list_name_icons) { - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; vfs::read_file(VitaIoDevice::ux0, buffer, emuenv.pref_path, "user/" + emuenv.io.user_id + "/trophy/conf/" + np_com_id + "/" + group.second); @@ -246,14 +243,7 @@ void init_trophy_collection(GuiState &gui, EmuEnvState &emuenv) { continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid icon: '{}' for NPComId: {}.", group.second, np_com_id); - continue; - } - - gui.trophy_np_com_id_list_icons[np_com_id][group.first].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.trophy_np_com_id_list_icons[np_com_id][group.first].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); } } } @@ -317,8 +307,6 @@ static void get_trophy_list(GuiState &gui, EmuEnvState &emuenv, const std::strin } for (const auto &[trophy_id, _] : trophy_info) { - int32_t width = 0; - int32_t height = 0; vfs::FileBuffer buffer; const std::string icon_name = fmt::format("TROP{}.PNG", trophy_id); @@ -327,14 +315,8 @@ static void get_trophy_list(GuiState &gui, EmuEnvState &emuenv, const std::strin LOG_WARN("Trophy icon, Name: '{}', Not found for trophy id: {}.", icon_name, trophy_id); continue; } - stbi_uc *data = stbi_load_from_memory(&buffer[0], static_cast(buffer.size()), &width, &height, nullptr, STBI_rgb_alpha); - if (!data) { - LOG_ERROR("Invalid trophy icon for trophy id {} [{}].", icon_name, trophy_id); - continue; - } - gui.trophy_list[trophy_id].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.trophy_list[trophy_id].loadTextureFromMemory(emuenv.renderer.get(), buffer.data(), static_cast(buffer.size())); auto &common = gui.lang.common.main; const auto trophy_type = np_com_id_info[np_com_id].context.trophy_kinds[string_utils::stoi_def(trophy_id, 0, "trophy id")]; @@ -506,7 +488,6 @@ void draw_trophy_collection(GuiState &gui, EmuEnvState &emuenv) { }); np_com_id_list.erase(np_com_id_index); np_com_id_info.erase(delete_np_com_id); - gui.trophy_np_com_id_list_icons[delete_np_com_id]["000"] = {}; gui.trophy_np_com_id_list_icons.erase(delete_np_com_id); delete_np_com_id.clear(); } diff --git a/vita3k/gui/src/trophy_unlocked.cpp b/vita3k/gui/src/trophy_unlocked.cpp index 4d15f99a56..a7ac9278f5 100644 --- a/vita3k/gui/src/trophy_unlocked.cpp +++ b/vita3k/gui/src/trophy_unlocked.cpp @@ -18,7 +18,6 @@ #include "private.h" #include -#include #include #include @@ -53,7 +52,7 @@ static void draw_trophy_unlocked(GuiState &gui, EmuEnvState &emuenv, NpTrophyUnl gui.trophy_window_pos = ImVec2(ImGui::GetIO().DisplaySize.x + TROPHY_WINDOW_MARGIN_PADDING, TROPHY_WINDOW_Y_POS); // Load icon - gui.trophy_window_icon = load_image(gui, callback_data.icon_buf.data(), static_cast(callback_data.icon_buf.size())); + gui.trophy_window_icon.loadTextureFromMemory(emuenv.renderer.get(), callback_data.icon_buf.data(), static_cast(callback_data.icon_buf.size())); } else if (gui.trophy_window_frame_stage == TrophyAnimationStage::SLIDE_IN && gui.trophy_window_pos.x > target_window_pos.x) { gui.trophy_window_pos.x -= TROPHY_MOVE_DELTA; } else if (gui.trophy_window_frame_stage == TrophyAnimationStage::SLIDE_OUT && gui.trophy_window_pos.x < target_window_pos.x) { @@ -137,7 +136,7 @@ void draw_trophies_unlocked(GuiState &gui, EmuEnvState &emuenv) { // Destroy the texture if (gui.trophy_window_frame_count != 0xFFFFFFFF) - ImGui_ImplSdl_DeleteTexture(gui.imgui_state.get(), gui.trophy_window_icon); + gui.trophy_window_icon = {}; gui.trophy_window_frame_stage = TrophyAnimationStage::SLIDE_IN; gui.trophy_window_frame_count = 0; diff --git a/vita3k/gui/src/user_management.cpp b/vita3k/gui/src/user_management.cpp index 43d8abb60c..0135685b89 100644 --- a/vita3k/gui/src/user_management.cpp +++ b/vita3k/gui/src/user_management.cpp @@ -31,7 +31,6 @@ #include #include -#include #undef ERROR @@ -55,29 +54,19 @@ struct AvatarInfo { static std::map> users_avatar_infos; static bool init_avatar(GuiState &gui, EmuEnvState &emuenv, const std::string &user_id, const std::string &avatar_path) { const auto avatar_path_path = avatar_path == "default" ? emuenv.static_assets_path / "data/image/icon.png" : fs_utils::utf8_to_path(avatar_path); - if (!fs::exists(avatar_path_path)) { LOG_WARN("Avatar image doesn't exist: {}.", avatar_path_path); return false; } - int32_t width = 0; - int32_t height = 0; - FILE *f = FOPEN(avatar_path_path.c_str(), "rb"); - - stbi_uc *data = stbi_load_from_file(f, &width, &height, nullptr, STBI_rgb_alpha); - - if (!data) { - LOG_ERROR("Invalid or corrupted image: {}.", avatar_path_path); - return false; - } - gui.users_avatar[user_id] = {}; - gui.users_avatar[user_id].init(gui.imgui_state.get(), data, width, height); - stbi_image_free(data); + gui.users_avatar[user_id].loadTextureFromFile(emuenv.renderer.get(), f); fclose(f); + int32_t width = gui.users_avatar[user_id].width; + int32_t height = gui.users_avatar[user_id].height; + // Calculate avatar size and position based of aspect ratio // Resize for all size of avatar constexpr std::array sizes = { SMALL, MEDIUM, LARGE }; @@ -89,7 +78,7 @@ static bool init_avatar(GuiState &gui, EmuEnvState &emuenv, const std::string &u avatar.pos = ImVec2((avatar_size.x / 2.f) - (avatar.size.x / 2.f), (avatar_size.y / 2.f) - (avatar.size.y / 2.f)); } - return gui.users_avatar.contains(user_id); + return true; } void get_users_list(GuiState &gui, EmuEnvState &emuenv) { @@ -229,7 +218,7 @@ void open_user(GuiState &gui, EmuEnvState &emuenv) { gui.vita_area.user_management = false; if (gui.users[emuenv.io.user_id].start_type == "image") - init_user_start_background(gui, gui.users[emuenv.io.user_id].start_path); + init_user_start_background(gui, emuenv, gui.users[emuenv.io.user_id].start_path); else init_theme_start_background(gui, emuenv, gui.users[emuenv.io.user_id].theme_id); @@ -286,7 +275,6 @@ static void create_temp_user(GuiState &gui, EmuEnvState &emuenv) { static void clear_user_temp(GuiState &gui) { temp = {}; - gui.users_avatar["temp"] = {}; gui.users_avatar.erase("temp"); user_id_selected.clear(); } diff --git a/vita3k/interface.cpp b/vita3k/interface.cpp index 6cb83c9ee5..3eb81e0f64 100644 --- a/vita3k/interface.cpp +++ b/vita3k/interface.cpp @@ -43,7 +43,7 @@ #include #include -#include +#include #include @@ -155,7 +155,7 @@ bool install_archive_content(EmuEnvState &emuenv, GuiState *gui, const ZipPtr &z gui::draw_begin(*gui, emuenv); gui::draw_ui(*gui, emuenv); gui::draw_reinstall_dialog(&status, *gui, emuenv); - gui::draw_end(*gui); + gui::draw_end(*gui, emuenv); emuenv.renderer->swap_window(emuenv.window.get()); } switch (status) { @@ -649,7 +649,7 @@ bool handle_events(EmuEnvState &emuenv, GuiState &gui) { SDL_Event event; while (SDL_PollEvent(&event)) { - ImGui_ImplSdl_ProcessEvent(gui.imgui_state.get(), &event); + ImGui_ImplSDL2_ProcessEvent(&event); switch (event.type) { case SDL_QUIT: if (!emuenv.io.app_path.empty()) diff --git a/vita3k/main.cpp b/vita3k/main.cpp index d615afb892..4b296d10d5 100644 --- a/vita3k/main.cpp +++ b/vita3k/main.cpp @@ -185,10 +185,16 @@ int main(int argc, char *argv[]) { SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); - if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) < 0) { app::error_dialog("SDL initialisation failed."); return SDLInitFailed; } + + // From 2.0.18: Enable native IME. +#ifdef SDL_HINT_IME_SHOW_UI + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); +#endif + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); } @@ -219,7 +225,7 @@ int main(int argc, char *argv[]) { if (handle_events(emuenv, gui)) { gui::draw_begin(gui, emuenv); gui::draw_initial_setup(gui, emuenv); - gui::draw_end(gui); + gui::draw_end(gui, emuenv); emuenv.renderer->swap_window(emuenv.window.get()); } else return QuitRequested; @@ -277,24 +283,8 @@ int main(int argc, char *argv[]) { auto discord_rich_presence_old = emuenv.cfg.discord_rich_presence; #endif - std::chrono::system_clock::time_point present = std::chrono::system_clock::now(); - std::chrono::system_clock::time_point later = std::chrono::system_clock::now(); - const double frame_time = 1000.0 / 60.0; // Application not provided via argument, show app selector while (run_type == app::AppRunType::Unknown) { - // get the current time & get the time we worked for - present = std::chrono::system_clock::now(); - std::chrono::duration work_time = present - later; - // check if we are running faster than ~60fps (16.67ms) - if (work_time.count() < frame_time) { - // sleep for delta time. - std::chrono::duration delta_ms(frame_time - work_time.count()); - auto delta_ms_duration = std::chrono::duration_cast(delta_ms); - std::this_thread::sleep_for(std::chrono::milliseconds(delta_ms_duration.count())); - } - // save the later time - later = std::chrono::system_clock::now(); - if (handle_events(emuenv, gui)) { ZoneScopedN("UI rendering"); // Tracy - Track UI rendering loop scope gui::draw_begin(gui, emuenv); @@ -305,10 +295,12 @@ int main(int argc, char *argv[]) { gui::draw_vita_area(gui, emuenv); gui::draw_ui(gui, emuenv); - gui::draw_end(gui); + gui::draw_end(gui, emuenv); emuenv.renderer->swap_window(emuenv.window.get()); FrameMark; // Tracy - Frame end mark for UI rendering loop } else { + emuenv.renderer->preclose_action(); + app::destroy(emuenv, gui); return QuitRequested; } @@ -350,8 +342,6 @@ int main(int argc, char *argv[]) { return main_thread->status == ThreadStatus::dormant; }); return Success; - } else { - gui.imgui_state->do_clear_screen = false; } gui::init_app_background(gui, emuenv, emuenv.io.app_path); @@ -394,7 +384,7 @@ int main(int argc, char *argv[]) { emuenv.renderer->precompile_shader(hash); gui::draw_pre_compiling_shaders_progress(gui, emuenv, uint32_t(emuenv.renderer->shaders_cache_hashs.size())); - gui::draw_end(gui); + gui::draw_end(gui, emuenv); emuenv.renderer->swap_window(emuenv.window.get()); } } @@ -418,7 +408,7 @@ int main(int argc, char *argv[]) { gui::draw_common_dialog(gui, emuenv); draw_app_background(gui, emuenv); - gui::draw_end(gui); + gui::draw_end(gui, emuenv); emuenv.renderer->swap_window(emuenv.window.get()); FrameMark; // Tracy - Frame end mark for game loading loop } @@ -455,7 +445,7 @@ int main(int argc, char *argv[]) { gui::draw_ui(gui, emuenv); } - gui::draw_end(gui); + gui::draw_end(gui, emuenv); emuenv.renderer->swap_window(emuenv.window.get()); FrameMark; // Tracy - Frame end mark for game rendering loop } @@ -465,7 +455,7 @@ int main(int argc, char *argv[]) { #endif emuenv.renderer->preclose_action(); - app::destroy(emuenv, gui.imgui_state.get()); + app::destroy(emuenv, gui); if (emuenv.load_exec) run_execv(argv, emuenv); diff --git a/vita3k/renderer/CMakeLists.txt b/vita3k/renderer/CMakeLists.txt index a728caf7d9..1222736ef5 100644 --- a/vita3k/renderer/CMakeLists.txt +++ b/vita3k/renderer/CMakeLists.txt @@ -46,8 +46,8 @@ add_library( ) target_include_directories(renderer PUBLIC include) -target_link_libraries(renderer PUBLIC display mem stb shader glutil threads config util vkutil) -target_link_libraries(renderer PRIVATE ddspp sdl2 stb ffmpeg xxHash::xxhash concurrentqueue) +target_link_libraries(renderer PUBLIC concurrentqueue display mem stb shader glutil threads config util vkutil) +target_link_libraries(renderer PRIVATE ddspp sdl2 stb ffmpeg xxHash::xxhash) # Marshmallow Tracy linking if(TRACY_ENABLE_ON_CORE_COMPONENTS) diff --git a/vita3k/renderer/include/renderer/vulkan/gxm_to_vulkan.h b/vita3k/renderer/include/renderer/vulkan/gxm_to_vulkan.h index 0a1a254741..261c2c5e90 100644 --- a/vita3k/renderer/include/renderer/vulkan/gxm_to_vulkan.h +++ b/vita3k/renderer/include/renderer/vulkan/gxm_to_vulkan.h @@ -17,10 +17,6 @@ #pragma once -#define VK_NO_PROTOTYPES -#define VULKAN_HPP_NO_CONSTRUCTORS -#define VULKAN_HPP_NO_SPACESHIP_OPERATOR -#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #include #include diff --git a/vita3k/vkutil/include/vkutil/vkutil.h b/vita3k/vkutil/include/vkutil/vkutil.h index 95b25874fa..9cb7ebf884 100644 --- a/vita3k/vkutil/include/vkutil/vkutil.h +++ b/vita3k/vkutil/include/vkutil/vkutil.h @@ -17,10 +17,6 @@ #pragma once -#define VK_NO_PROTOTYPES -#define VULKAN_HPP_NO_CONSTRUCTORS -#define VULKAN_HPP_NO_SPACESHIP_OPERATOR -#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #include #define VMA_STATIC_VULKAN_FUNCTIONS 0 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1