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);