diff --git a/README.md b/README.md index a01087c8..8baaa996 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu | `alpha` | Set the opacity of all text and frametime graph `0.0`-`1.0` | | `arch` | Show if the application is 32- or 64-bit | | `background_alpha` | Set the opacity of the background `0.0`-`1.0` | +| `background_image` | Display a background image from argument path on the whole screen (eventually transparent) | | `battery_color` | Change the battery text color | | `battery_icon` | Display battery icon instead of percent | | `battery` | Display current battery percent and energy consumption | @@ -351,6 +352,8 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu | `horizontal` | Display Mangohud in a horizontal position | | `hud_compact` | Display compact version of MangoHud | | `hud_no_margin` | Remove margins around MangoHud | +| `image` | Display an image from argument path | +| `image_max_width` | Maximize the image by a max number of pixel for the width | | `io_read`
`io_write` | Show non-cached IO read/write, in MiB/s | | `log_duration` | Set amount of time the logging will run for (in seconds) | | `log_interval` | Change the default log interval. Default is `100` | diff --git a/data/MangoHud.conf b/data/MangoHud.conf index fb9a9f89..9feff959 100644 --- a/data/MangoHud.conf +++ b/data/MangoHud.conf @@ -143,6 +143,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 b5e1050c..9ff67bba 100644 --- a/src/gl/gl_renderer.cpp +++ b/src/gl/gl_renderer.cpp @@ -259,7 +259,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 9760a103..12fd57a3 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 @@ static 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 499ed660..1200295d 100644 --- a/src/hud_elements.cpp +++ b/src/hud_elements.cpp @@ -19,6 +19,7 @@ #include #include "version.h" #include "blacklist.h" +#include "load_textures.h" #define CHAR_CELSIUS "\xe2\x84\x83" #define CHAR_FAHRENHEIT "\xe2\x84\x89" @@ -716,6 +717,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(){ ImguiNextColumnFirstItem(); ImGui::PushFont(HUDElements.sw_stats->font1); @@ -1179,6 +1246,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 39eb696c..2fef8da0 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 { @@ -59,6 +79,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 a36d517e..76801466 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 00000000..c12ca075 --- /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 e1cdbea3..e08a2ae2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -44,7 +44,8 @@ vklayer_files = files( 'vulkan.cpp', 'blacklist.cpp', 'file_utils.cpp', - 'intel.cpp' + 'intel.cpp', + 'load_textures.cpp', ) opengl_files = [] if ['windows', 'mingw'].contains(host_machine.system()) diff --git a/src/overlay.h b/src/overlay.h index e37b0b67..79840e8d 100644 --- a/src/overlay.h +++ b/src/overlay.h @@ -114,6 +114,7 @@ extern void control_client_check(int control, int& control_client, const std::st extern void process_control_socket(int& control_client, overlay_params ¶ms); extern void control_send(int control_client, const char *cmd, unsigned cmdlen, const char *param, unsigned paramlen); extern int global_control_client; +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 a0998f5a..249086f9 100644 --- a/src/overlay_params.cpp +++ b/src/overlay_params.cpp @@ -444,6 +444,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) @@ -658,6 +661,7 @@ static void set_param_defaults(struct overlay_params *params){ params->preset = -1; params->font_size = 24; params->table_columns = 3; + params->image_max_width = 0; } void diff --git a/src/overlay_params.h b/src/overlay_params.h index 0878dca5..0826f61e 100644 --- a/src/overlay_params.h +++ b/src/overlay_params.h @@ -71,6 +71,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) \ @@ -281,6 +284,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/stb_image.h b/src/stb_image.h index d60371b9..c1fbc3c1 100644 --- a/src/stb_image.h +++ b/src/stb_image.h @@ -4955,6 +4955,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; diff --git a/src/vulkan.cpp b/src/vulkan.cpp index b3ace7f7..eb9ceaf0 100644 --- a/src/vulkan.cpp +++ b/src/vulkan.cpp @@ -51,6 +51,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; @@ -63,6 +66,22 @@ namespace MangoHud { namespace GL { }} #endif +struct vk_image { + VkImage image; + VkImageView view; + VkDeviceMemory mem; + uint32_t width, height; + size_t size; + 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; @@ -158,12 +177,16 @@ struct swapchain_data { std::list draws; /* List of struct overlay_draw */ bool font_uploaded; - VkImage font_image; - VkImageView font_image_view; - VkDeviceMemory font_mem; + //VkImage font_image; + //VkImageView font_image_view; + //VkDeviceMemory font_mem; VkBuffer upload_font_buffer; VkDeviceMemory upload_font_buffer_mem; + struct vk_image font_image; + size_t font_params_hash = 0; + + std::vector textures; /**/ ImGuiContext* imgui_context; ImFontAtlas* font_atlas; @@ -214,6 +237,7 @@ static void unmap_object(uint64_t obj) /**/ static void shutdown_swapchain_font(struct swapchain_data*); +ImTextureID add_texture(swapchain_stats* stats, const std::string& filename, int* width, int* height, int maxwidth); static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo, VkLayerFunction func) @@ -291,6 +315,22 @@ static struct device_data *new_device_data(VkDevice device, struct instance_data return data; } +static VkDescriptorSet alloc_descriptor_set(const struct swapchain_data *data) +{ + VkDescriptorSet descriptor_set {}; + struct device_data *device_data = data->device; + + VkDescriptorSetAllocateInfo alloc_info {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = data->descriptor_pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &data->descriptor_layout; + VK_CHECK(device_data->vtable.AllocateDescriptorSets(device_data->device, + &alloc_info, + &descriptor_set)); + return descriptor_set; +} + static struct queue_data *new_queue_data(VkQueue queue, const VkQueueFamilyProperties *family_props, uint32_t family_index, @@ -623,9 +663,7 @@ static void create_image(struct swapchain_data *data, uint32_t width, uint32_t height, VkFormat format, - VkImage& image, - VkDeviceMemory& image_mem, - VkImageView& image_view) + vk_image& image) { struct device_data *device_data = data->device; @@ -644,10 +682,10 @@ static void create_image(struct swapchain_data *data, image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK(device_data->vtable.CreateImage(device_data->device, &image_info, - NULL, &image)); + NULL, &image.image)); VkMemoryRequirements font_image_req; device_data->vtable.GetImageMemoryRequirements(device_data->device, - image, &font_image_req); + image.image, &font_image_req); VkMemoryAllocateInfo image_alloc_info = {}; image_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; image_alloc_info.allocationSize = font_image_req.size; @@ -655,33 +693,31 @@ static void create_image(struct swapchain_data *data, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, font_image_req.memoryTypeBits); VK_CHECK(device_data->vtable.AllocateMemory(device_data->device, &image_alloc_info, - NULL, &image_mem)); + NULL, &image.mem)); VK_CHECK(device_data->vtable.BindImageMemory(device_data->device, - image, - image_mem, 0)); + image.image, + image.mem, 0)); /* Font image view */ VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_info.image = image; + view_info.image = image.image; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_info.format = format; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.layerCount = 1; VK_CHECK(device_data->vtable.CreateImageView(device_data->device, &view_info, - NULL, &image_view)); + NULL, &image.view)); - update_image_descriptor(data, image_view, descriptor_set); + update_image_descriptor(data, image.view, descriptor_set); } static VkDescriptorSet create_image_with_desc(struct swapchain_data *data, uint32_t width, uint32_t height, VkFormat format, - VkImage& image, - VkDeviceMemory& image_mem, - VkImageView& image_view) + vk_image& image) { struct device_data *device_data = data->device; @@ -696,10 +732,71 @@ static VkDescriptorSet create_image_with_desc(struct swapchain_data *data, &alloc_info, &descriptor_set)); - create_image(data, descriptor_set, width, height, format, image, image_mem, image_view); + create_image(data, descriptor_set, width, height, format, image); return descriptor_set; } +// TODO Synchronous image data upload, make it async +static void submit_image_upload_cmd(struct swapchain_data *data, vk_image *img, void *pixels, size_t upload_size) +{ + if (img->uploaded) + return; + + struct device_data *device_data = data->device; + auto start = Clock::now(); + VkCommandBuffer cmd_buffer; + VkCommandBufferAllocateInfo cmd_buffer_info = {}; + cmd_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd_buffer_info.commandPool = data->command_pool; + cmd_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_buffer_info.commandBufferCount = 1; + VK_CHECK(device_data->vtable.AllocateCommandBuffers(device_data->device, + &cmd_buffer_info, + &cmd_buffer)); + VK_CHECK(device_data->set_device_loader_data(device_data->device, + cmd_buffer)); + + VkCommandBufferBeginInfo buffer_begin_info = {}; + buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VK_CHECK(device_data->vtable.BeginCommandBuffer(cmd_buffer, &buffer_begin_info)); + + VkBuffer buffer; + VkDeviceMemory buffer_mem; + upload_image_data(device_data, cmd_buffer, pixels, upload_size, img->width, img->height, + buffer, buffer_mem, img->image); + + device_data->vtable.EndCommandBuffer(cmd_buffer); + + VkFence fence; + VkFenceCreateInfo fence_info = {}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VK_CHECK(device_data->vtable.CreateFence(device_data->device, + &fence_info, + NULL, + &fence)); + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &cmd_buffer; + + VkResult r; + VK_CHECK(r = device_data->vtable.QueueSubmit(device_data->graphic_queue->queue, 1, &submit_info, fence)); + + if (r == VK_SUCCESS) + VK_CHECK(device_data->vtable.WaitForFences(device_data->device, 1, &fence, VK_TRUE, UINT64_MAX)); + device_data->vtable.FreeCommandBuffers(device_data->device, data->command_pool, 1, &cmd_buffer); + device_data->vtable.DestroyFence(device_data->device, fence, NULL); + + device_data->vtable.DestroyBuffer(device_data->device, buffer, NULL); + device_data->vtable.FreeMemory(device_data->device, buffer_mem, NULL); + + img->uploaded = true; + auto dur = Clock::now() - start; + auto dur_us = std::chrono::duration_cast(dur).count(); + SPDLOG_DEBUG("upload duration: {} us, {} bytes, {:0.02f} MiB/s", dur_us, upload_size, upload_size/(dur_us/1e6f)/(1024*1024)); +} + static void check_fonts(struct swapchain_data* data) { struct device_data *device_data = data->device; @@ -720,9 +817,9 @@ static void check_fonts(struct swapchain_data* data) shutdown_swapchain_font(data); if (desc_set) - create_image(data, desc_set, width, height, VK_FORMAT_R8G8B8A8_UNORM, data->font_image, data->font_mem, data->font_image_view); + create_image(data, desc_set, width, height, VK_FORMAT_R8G8B8A8_UNORM, data->font_image); else - desc_set = create_image_with_desc(data, width, height, VK_FORMAT_R8G8B8A8_UNORM, data->font_image, data->font_mem, data->font_image_view); + desc_set = create_image_with_desc(data, width, height, VK_FORMAT_R8G8B8A8_UNORM, data->font_image); data->font_atlas->SetTexID((ImTextureID)desc_set); data->font_uploaded = false; @@ -746,7 +843,37 @@ static void ensure_swapchain_fonts(struct swapchain_data *data, int width, height; data->font_atlas->GetTexDataAsRGBA32(&pixels, &width, &height); size_t upload_size = width * height * 4 * sizeof(char); - upload_image_data(device_data, command_buffer, pixels, upload_size, width, height, data->upload_font_buffer, data->upload_font_buffer_mem, data->font_image); + upload_image_data(device_data, command_buffer, pixels, upload_size, width, height, data->upload_font_buffer, data->upload_font_buffer_mem, data->font_image.image); +} + +ImTextureID add_texture(swapchain_stats* stats, const std::string& filename, int* width, int* height, int maxwidth) { + struct texture_info ti {}; + int original_width, original_height, channels; + // FIXME hacks + struct swapchain_data *data = reinterpret_cast((char*)stats - ((char*)&((swapchain_data*)0)->sw_stats)); + + // 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); + create_image(data, ti.descset, *width, *height, VK_FORMAT_R8G8B8A8_UNORM, ti.image); + update_image_descriptor(data, ti.image.view, ti.descset); + ti.filename = filename; + ti.maxwidth = maxwidth; + + data->textures.push_back(ti); + + return (ImTextureID) ti.descset; } static void CreateOrResizeBuffer(struct device_data *data, @@ -816,6 +943,39 @@ static struct overlay_draw *render_swapchain_display(struct swapchain_data *data ensure_swapchain_fonts(data, draw->command_buffer); + /* ensure_textures */ + for (auto& tex : data->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(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. */ @@ -1419,16 +1579,31 @@ 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.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_swapchain_font(struct swapchain_data *data) { struct device_data *device_data = data->device; + destroy_vk_image(device_data, data->font_image); +} - device_data->vtable.DestroyImageView(device_data->device, data->font_image_view, NULL); - device_data->vtable.DestroyImage(device_data->device, data->font_image, NULL); - device_data->vtable.FreeMemory(device_data->device, data->font_mem, NULL); +static void shutdown_textures(struct swapchain_data *data) +{ + struct device_data *device_data = data->device; + for (auto& tex : data->textures) + { + device_data->vtable.FreeDescriptorSets(device_data->device, data->descriptor_pool, 1, &tex.descset); + destroy_vk_image(device_data, tex.image); + } - device_data->vtable.DestroyBuffer(device_data->device, data->upload_font_buffer, NULL); - device_data->vtable.FreeMemory(device_data->device, data->upload_font_buffer_mem, NULL); + HUDElements.image_infos = {}; + HUDElements.background_image_infos = {}; } static void shutdown_swapchain_data(struct swapchain_data *data) @@ -1465,6 +1640,7 @@ static void shutdown_swapchain_data(struct swapchain_data *data) device_data->vtable.DestroySampler(device_data->device, data->font_sampler, NULL); shutdown_swapchain_font(data); + shutdown_textures(data); IM_FREE(data->font_atlas); ImGui::DestroyContext(data->imgui_context);