From 1756e8d8521d9eb0ff1f079642b7cc63c454cc47 Mon Sep 17 00:00:00 2001 From: Nicolas Adenis-Lamarre Date: Sun, 28 Nov 2021 21:30:21 +0000 Subject: [PATCH] options: image / image_max_width (int) / image_background (path) image : to display an image component (only one component of type image can be rendered) note that the texture is never cleared, nor reloaded (in case of configuration modification). image_max_width: by default, the width of the image is the one of the pannel (value is 0). with this option, you can reduce it. image_background: global background image to display Signed-off-by: Nicolas Adenis-Lamarre --- README.md | 3 + bin/MangoHud.conf | 7 ++ src/gl/gl_renderer.cpp | 2 +- src/gl/inject_egl.cpp | 5 + src/hud_elements.cpp | 69 +++++++++++++ src/hud_elements.h | 22 ++++ src/{loadTextures.cpp => load_textures.cpp} | 27 +++-- src/load_textures.h | 4 + src/meson.build | 1 + src/overlay.h | 1 + src/overlay_params.cpp | 4 + src/overlay_params.h | 6 ++ src/vulkan.cpp | 106 ++++++++++++++++++-- 13 files changed, 237 insertions(+), 20 deletions(-) rename src/{loadTextures.cpp => load_textures.cpp} (74%) create mode 100644 src/load_textures.h diff --git a/README.md b/README.md index 6f45902..08543bc 100644 --- a/README.md +++ b/README.md @@ -346,6 +346,9 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu | `show_fps_limit` | Display the current fps limit | | `custom_text_center` | Display a custom text centered useful for a header e.g `custom_text_center=FlightLessMango Benchmarks` | | `custom_text` | Display a custom text e.g `custom_text=Fsync enabled` | +| `background_image` | Display a background image from argument path on the whole screen (eventually transparent) | +| `image` | Display an image from argument path | +| `image_max_width` | Maximize the image by a max number of pixel for the width | | `exec` | Display output of bash command in next column, e.g `custom_text=/home` , `exec=df -h /home \| tail -n 1`. Only works with legacy_layout=false | | `round_corners` | Change the amount of roundness of the corners have e.g `round_corners=10.0` | | `vkbasalt` | Shows if vkbasalt is on | diff --git a/bin/MangoHud.conf b/bin/MangoHud.conf index 349ef9e..061a260 100644 --- a/bin/MangoHud.conf +++ b/bin/MangoHud.conf @@ -117,6 +117,13 @@ frame_timing ### Display output of Bash command in next column # exec= +### Display a background image from argument path on the whole screen (eventually transparent) +# background_image= +### Display an image from argument path +# image= +### Maximize the image by a max number of pixel for the width +# image_max_width= + ### Display media player metadata # media_player # media_player_name=spotify diff --git a/src/gl/gl_renderer.cpp b/src/gl/gl_renderer.cpp index e715739..6b60dd0 100644 --- a/src/gl/gl_renderer.cpp +++ b/src/gl/gl_renderer.cpp @@ -251,7 +251,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects() "varying vec4 Frag_Color;\n" "void main()\n" "{\n" - " gl_FragColor = Frag_Color * vec4(1, 1, 1, texture2D(Texture, Frag_UV.st).r);\n" + " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" "}\n"; const GLchar* fragment_shader_glsl_130 = diff --git a/src/gl/inject_egl.cpp b/src/gl/inject_egl.cpp index d12b023..71530f4 100644 --- a/src/gl/inject_egl.cpp +++ b/src/gl/inject_egl.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "real_dlsym.h" #include "mesa/util/macros.h" #include "mesa/util/os_time.h" @@ -25,6 +26,10 @@ void* get_egl_proc_address(const char* name) { SPDLOG_ERROR("Failed to open " MANGOHUD_ARCH " libEGL.so.1: {}", dlerror()); } else { pfn_eglGetProcAddress = reinterpret_cast(real_dlsym(handle, "eglGetProcAddress")); + + if(gladLoadGLES2Loader((GLADloadproc)pfn_eglGetProcAddress) == 0) { + pfn_eglGetProcAddress = nullptr; + } } } diff --git a/src/hud_elements.cpp b/src/hud_elements.cpp index 1b7353a..68d9bbb 100644 --- a/src/hud_elements.cpp +++ b/src/hud_elements.cpp @@ -17,6 +17,7 @@ #include "string_utils.h" #include "app/mangoapp.h" #include +#include "load_textures.h" #define CHAR_CELSIUS "\xe2\x84\x83" #define CHAR_FAHRENHEIT "\xe2\x84\x89" @@ -672,6 +673,72 @@ void HudElements::custom_text_center(){ ImGui::PopFont(); } +void HudElements::image(){ + const std::string& value = HUDElements.ordered_functions[HUDElements.place].second; + + // load the image if needed + if (HUDElements.image_infos.loaded == false) { + unsigned maxwidth = HUDElements.params->width; + if (HUDElements.params->image_max_width != 0 && HUDElements.params->image_max_width < maxwidth) { + maxwidth = HUDElements.params->image_max_width; + } + + HUDElements.image_infos.loaded = true; + if (HUDElements.is_vulkan) { + if ((HUDElements.image_infos.texture = add_texture(HUDElements.sw_stats, value, &(HUDElements.image_infos.width), &(HUDElements.image_infos.height), maxwidth))) + HUDElements.image_infos.valid = true; + } else { + HUDElements.image_infos.valid = GL_LoadTextureFromFile(value.c_str(), + reinterpret_cast(&(HUDElements.image_infos.texture)), + &(HUDElements.image_infos.width), + &(HUDElements.image_infos.height), + maxwidth); + } + + if (HUDElements.image_infos.valid) + SPDLOG_INFO("Image {} loaded ({}x{})", value, HUDElements.image_infos.width, HUDElements.image_infos.height); + else + SPDLOG_WARN("Failed to load image: {}", value); + } + + // render the image + if (HUDElements.image_infos.valid) { + ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Image(HUDElements.image_infos.texture, ImVec2(HUDElements.image_infos.width, HUDElements.image_infos.height)); + } +} + +void HudElements::background_image(){ + const std::string& value = HUDElements.params->background_image; + + // load the image if needed + if (HUDElements.background_image_infos.loaded == false) { + HUDElements.background_image_infos.loaded = true; + if (HUDElements.is_vulkan) { + if ((HUDElements.background_image_infos.texture = add_texture(HUDElements.sw_stats, value, &(HUDElements.background_image_infos.width), &(HUDElements.background_image_infos.height), 0))) + HUDElements.background_image_infos.valid = true; + } else { + HUDElements.background_image_infos.valid = GL_LoadTextureFromFile(value.c_str(), + reinterpret_cast(&(HUDElements.background_image_infos.texture)), + &(HUDElements.background_image_infos.width), + &(HUDElements.background_image_infos.height), + 0); + } + + if (HUDElements.background_image_infos.valid) + SPDLOG_INFO("Image {} loaded ({}x{})", value, HUDElements.background_image_infos.width, HUDElements.background_image_infos.height); + else + SPDLOG_WARN("Failed to load image: {}", value); + } + + // render the image + if (HUDElements.background_image_infos.valid) { + ImGui::GetBackgroundDrawList()->AddImage(HUDElements.background_image_infos.texture, + ImVec2(0, 0), + ImVec2(HUDElements.background_image_infos.width, HUDElements.background_image_infos.height)); + } +} + void HudElements::custom_text(){ ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushFont(HUDElements.sw_stats->font1); @@ -1109,6 +1176,8 @@ void HudElements::sort_elements(const std::pair& optio if (param == "frame_timing") { ordered_functions.push_back({frame_timing, value}); } if (param == "media_player") { ordered_functions.push_back({media_player, value}); } if (param == "custom_text") { ordered_functions.push_back({custom_text, value}); } + if (param == "background_image") { ordered_functions.push_back({background_image, value}); } + if (param == "image") { ordered_functions.push_back({image, value}); } if (param == "custom_text_center") { ordered_functions.push_back({custom_text_center, value}); } if (param == "exec") { ordered_functions.push_back({_exec, value}); exec_list.push_back({int(ordered_functions.size() - 1), value}); } diff --git a/src/hud_elements.h b/src/hud_elements.h index 93c6b8c..4e11557 100644 --- a/src/hud_elements.h +++ b/src/hud_elements.h @@ -5,9 +5,29 @@ #include #include "timing.hpp" +struct image_infos { + std::string path; + int width; + int height; + bool loaded; + bool valid; + ImTextureID texture; + + image_infos() { + loaded = false; + valid = false; + } + + ~image_infos() { + } +}; + + struct overlay_params; class HudElements{ public: + struct image_infos image_infos; + struct image_infos background_image_infos; struct swapchain_stats *sw_stats; struct overlay_params *params; struct exec_entry { @@ -54,6 +74,8 @@ class HudElements{ static void show_fps_limit(); static void custom_text_center(); static void custom_text(); + static void image(); + static void background_image(); static void vkbasalt(); static void gamemode(); static void graphs(); diff --git a/src/loadTextures.cpp b/src/load_textures.cpp similarity index 74% rename from src/loadTextures.cpp rename to src/load_textures.cpp index a36d517..7680146 100644 --- a/src/loadTextures.cpp +++ b/src/load_textures.cpp @@ -1,4 +1,4 @@ -#include +#include "load_textures.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_RESIZE_IMPLEMENTATION @@ -22,17 +22,22 @@ bool GL_LoadTextureFromFile(const char* filename, GLuint* out_texture, int* out_ int image_width_resized = image_width * ratio; int image_height_resized = image_height * ratio; - unsigned char* image_data_resized = (unsigned char*)malloc(image_width_resized * image_height_resized * 4); - if (!image_data_resized) { + + if (ratio != 1) + { + unsigned char* image_data_resized = (unsigned char*)stbi__malloc(image_width_resized * image_height_resized * 4); + if (!image_data_resized) { + stbi_image_free(image_data); + return false; + } + + stbir_resize_uint8(image_data, image_width, image_height, 0, + image_data_resized, image_width_resized, image_height_resized, 0, + 4); stbi_image_free(image_data); - return false; + image_data = image_data_resized; } - stbir_resize_uint8(image_data, image_width, image_height, 0, - image_data_resized, image_width_resized, image_height_resized, 0, - 4); - stbi_image_free(image_data); - // Create a OpenGL texture identifier GLuint image_texture; glGenTextures(1, &image_texture); @@ -48,8 +53,8 @@ bool GL_LoadTextureFromFile(const char* filename, GLuint* out_texture, int* out_ #if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width_resized, image_height_resized, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data_resized); - stbi_image_free(image_data_resized); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width_resized, image_height_resized, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); + stbi_image_free(image_data); *out_texture = image_texture; *out_width = image_width_resized; diff --git a/src/load_textures.h b/src/load_textures.h new file mode 100644 index 0000000..c12ca07 --- /dev/null +++ b/src/load_textures.h @@ -0,0 +1,4 @@ +#pragma once +#include + +bool GL_LoadTextureFromFile(const char* filename, GLuint* out_texture, int* out_width, int* out_height, int maxwidth); diff --git a/src/meson.build b/src/meson.build index 6841c38..9ff8761 100644 --- a/src/meson.build +++ b/src/meson.build @@ -55,6 +55,7 @@ vklayer_files = files( 'blacklist.cpp', 'file_utils.cpp', 'amdgpu.cpp', + 'load_textures.cpp', ) opengl_files = [] if ['windows', 'mingw'].contains(host_machine.system()) diff --git a/src/overlay.h b/src/overlay.h index 25bab83..37db3db 100644 --- a/src/overlay.h +++ b/src/overlay.h @@ -114,6 +114,7 @@ float get_time_stat(void *_data, int _idx); void stop_hw_updater(); extern void control_client_check(int control, int& control_client, const std::string& deviceName); extern void process_control_socket(int& control_client, overlay_params ¶ms); +ImTextureID add_texture(swapchain_stats* stats, const std::string& filename, int* width, int* height, int maxwidth); #ifdef HAVE_DBUS void render_mpris_metadata(const overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing); #endif diff --git a/src/overlay_params.cpp b/src/overlay_params.cpp index 9d4bad1..2fd370b 100644 --- a/src/overlay_params.cpp +++ b/src/overlay_params.cpp @@ -429,6 +429,9 @@ parse_gl_size_query(const char *str) #define parse_blacklist(s) parse_str_tokenize(s) #define parse_custom_text_center(s) parse_str(s) #define parse_custom_text(s) parse_str(s) +#define parse_background_image(s) parse_str(s) +#define parse_image(s) parse_str(s) +#define parse_image_max_width(s) parse_unsigned(s) #define parse_fps_value(s) parse_load_value(s) #define parse_fps_color(s) parse_load_color(s) #define parse_battery_color(s) parse_color(s) @@ -629,6 +632,7 @@ parse_overlay_config(struct overlay_params *params, params->round_corners = 0; params->battery_color =0xff9078; params->fsr_steam_sharpness = -1; + params->image_max_width = 0; #ifdef HAVE_X11 params->toggle_hud = { XK_Shift_R, XK_F12 }; diff --git a/src/overlay_params.h b/src/overlay_params.h index 7977edf..33eb55b 100644 --- a/src/overlay_params.h +++ b/src/overlay_params.h @@ -69,6 +69,9 @@ typedef unsigned long KeySym; OVERLAY_PARAM_BOOL(fps_color_change) \ OVERLAY_PARAM_BOOL(custom_text_center) \ OVERLAY_PARAM_BOOL(custom_text) \ + OVERLAY_PARAM_CUSTOM(background_image) \ + OVERLAY_PARAM_CUSTOM(image) \ + OVERLAY_PARAM_CUSTOM(image_max_width) \ OVERLAY_PARAM_BOOL(exec) \ OVERLAY_PARAM_BOOL(vkbasalt) \ OVERLAY_PARAM_BOOL(gamemode) \ @@ -251,6 +254,9 @@ struct overlay_params { uint32_t font_glyph_ranges; std::string custom_text_center; std::string custom_text; + std::string background_image; + std::string image; + unsigned image_max_width; std::string config_file_path; std::unordered_map options; int permit_upload; diff --git a/src/vulkan.cpp b/src/vulkan.cpp index 0ef97e6..98ac1b4 100644 --- a/src/vulkan.cpp +++ b/src/vulkan.cpp @@ -50,6 +50,9 @@ #include "blacklist.h" #include "pci_ids.h" +#include "stb_image.h" +#include "stb_image_resize.h" + using namespace std; float offset_x, offset_y, hudSpacing; @@ -71,6 +74,13 @@ struct vk_image { bool uploaded; }; +struct texture_info { + vk_image image; + VkDescriptorSet descset; + std::string filename; + int maxwidth; +}; + /* Mapped from VkInstace/VkPhysicalDevice */ struct instance_data { struct vk_instance_dispatch_table vtable; @@ -113,6 +123,8 @@ struct device_data { ImFont *font_alt, *font_text; struct vk_image font_img; size_t font_params_hash = 0; + + std::vector textures; }; /* Mapped from VkCommandBuffer */ @@ -344,10 +356,10 @@ static void setup_device_pipeline(struct device_data *device_data) /* Descriptor pool */ VkDescriptorPoolSize sampler_pool_size = {}; sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - sampler_pool_size.descriptorCount = 1; + sampler_pool_size.descriptorCount = 3; VkDescriptorPoolCreateInfo desc_pool_info = {}; desc_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - desc_pool_info.maxSets = 1; + desc_pool_info.maxSets = 3; desc_pool_info.poolSizeCount = 1; desc_pool_info.pPoolSizes = &sampler_pool_size; VK_CHECK(device_data->vtable.CreateDescriptorPool(device_data->device, @@ -859,6 +871,36 @@ static void check_fonts(struct swapchain_data* data) } } +ImTextureID add_texture(swapchain_stats *stats, const std::string& filename, int* width, int* height, int maxwidth) { + // FIXME hacks + struct swapchain_data *data = reinterpret_cast((char*)stats - ((char*)&((swapchain_data*)0)->sw_stats)); + struct texture_info ti {}; + int original_width, original_height, channels; + + // load + int ret = stbi_info(filename.c_str(), &original_width, &original_height, &channels); + if (!ret) + return nullptr; + + // reduce the image + float ratio = 1.0; + if (original_width > maxwidth && maxwidth != 0) { + ratio = maxwidth / static_cast (original_width); + } + *width = original_width * ratio; + *height = original_height * ratio; + + ti.descset = alloc_descriptor_set(data->device); + create_image(data->device, *width, *height, VK_FORMAT_R8G8B8A8_UNORM, ti.image); + update_image_descriptor(data->device, ti.image.image_view, ti.descset); + ti.filename = filename; + ti.maxwidth = maxwidth; + + data->device->textures.push_back(ti); + + return (ImTextureID) ti.descset; +} + static void CreateOrResizeBuffer(struct device_data *data, VkBuffer *buffer, VkDeviceMemory *buffer_memory, @@ -929,6 +971,39 @@ static struct overlay_draw *render_swapchain_display(struct swapchain_data *data // ensure_swapchain_fonts(data->device, draw->command_buffer); + /* ensure_textures */ + for (auto& tex : data->device->textures) { + if (!tex.descset) + continue; + + if (!tex.image.uploaded) { +// tex.image.uploaded = true; + + // load + int width, height, channels; + unsigned char* pixels = stbi_load(tex.filename.c_str(), &width, &height, &channels, STBI_rgb_alpha); + if (!pixels) + { + SPDLOG_ERROR("Failed to load image: {}", tex.filename); + continue; + } + + // reduce the image + if (width > tex.maxwidth && tex.maxwidth != 0) { + unsigned char* pixels_resized = (unsigned char*)malloc(width * height * STBI_rgb_alpha); + stbir_resize_uint8(pixels, width, height, 0, pixels_resized, tex.image.width, tex.image.height, 0, STBI_rgb_alpha); + stbi_image_free(pixels); + pixels = pixels_resized; + } + + SPDLOG_DEBUG("Uploading '{}' ({}x{})", tex.filename, tex.image.width, tex.image.height); + size_t upload_size = tex.image.width * tex.image.height * STBI_rgb_alpha; + + submit_image_upload_cmd(device_data, &tex.image, pixels, upload_size); + stbi_image_free(pixels); + } + } + /* Bounce the image to display back to color attachment layout for * rendering on top of it. */ @@ -1474,15 +1549,29 @@ static void setup_swapchain_data(struct swapchain_data *data, NULL, &data->command_pool)); } +static void destroy_vk_image(struct device_data *device_data, vk_image& image) +{ + device_data->vtable.DestroyImageView(device_data->device, image.image_view, NULL); + device_data->vtable.DestroyImage(device_data->device, image.image, NULL); + device_data->vtable.FreeMemory(device_data->device, image.mem, NULL); + image = {}; +} + static void shutdown_device_font(struct device_data *device_data) { - device_data->vtable.DestroyImageView(device_data->device, device_data->font_img.image_view, NULL); - device_data->vtable.DestroyImage(device_data->device, device_data->font_img.image, NULL); - device_data->vtable.FreeMemory(device_data->device, device_data->font_img.mem, NULL); + destroy_vk_image(device_data, device_data->font_img); +} + +static void shutdown_textures(struct device_data *device_data) +{ + for (auto& tex : device_data->textures) + { + device_data->vtable.FreeDescriptorSets(device_data->device, device_data->descriptor_pool, 1, &tex.descset); + destroy_vk_image(device_data, tex.image); + } -// device_data->vtable.DestroyBuffer(device_data->device, device_data->font_img.buffer, NULL); -// device_data->vtable.FreeMemory(device_data->device, device_data->font_img.buffer_mem, NULL); - device_data->font_img = {}; + HUDElements.image_infos = {}; + HUDElements.background_image_infos = {}; } static void shutdown_swapchain_data(struct swapchain_data *data) @@ -1879,6 +1968,7 @@ static void overlay_DestroyDevice( struct device_data *device_data = FIND(struct device_data, device); if (!is_blacklisted()) { shutdown_device_font(device_data); + shutdown_textures(device_data); IM_FREE(device_data->font_atlas); device_unmap_queues(device_data); }