|
| 1 | +/* |
| 2 | +=========================================================================== |
| 3 | +Copyright (C) 2026 Gopex LLC. All rights reserved. |
| 4 | +
|
| 5 | +Bridge Dear ImGui to the engine's Vulkan loader (qvk* via ImGui_ImplVulkan_LoadFunctions). |
| 6 | +SDL2 mouse position when the inspector wants input. |
| 7 | +=========================================================================== |
| 8 | +*/ |
| 9 | + |
| 10 | +#ifdef USE_IMGUI |
| 11 | + |
| 12 | +#define VK_NO_PROTOTYPES |
| 13 | + |
| 14 | +#include <imgui.h> |
| 15 | +#include <imgui_impl_vulkan.h> |
| 16 | + |
| 17 | +extern "C" { |
| 18 | +#include "../../qcommon/q_shared.h" |
| 19 | +#include "../vk_instance.h" |
| 20 | +#define USE_VK_PBR |
| 21 | +#include "../vk.h" |
| 22 | +#include "../vk_render_pass.h" |
| 23 | +} |
| 24 | + |
| 25 | +#if defined( USE_SDL ) && !defined( ANDROID ) |
| 26 | +# if defined( USE_LOCAL_HEADERS ) |
| 27 | +# include "SDL.h" |
| 28 | +# else |
| 29 | +# include <SDL2/SDL.h> |
| 30 | +# endif |
| 31 | +extern "C" struct SDL_Window *SDL_window; |
| 32 | +#endif |
| 33 | + |
| 34 | +#include "vk_imgui.h" |
| 35 | + |
| 36 | +static qboolean g_vkImguiBackendReady = qfalse; |
| 37 | + |
| 38 | +extern "C" void VkImgui_SetVulkanBackendReady( qboolean ready ) |
| 39 | +{ |
| 40 | + g_vkImguiBackendReady = ready; |
| 41 | +} |
| 42 | + |
| 43 | +extern "C" qboolean VkImgui_IsVulkanBackendReady( void ) |
| 44 | +{ |
| 45 | + return g_vkImguiBackendReady; |
| 46 | +} |
| 47 | + |
| 48 | +static PFN_vkVoidFunction VkImgui_ResolveVulkanProc( const char *name, void *userData ) |
| 49 | +{ |
| 50 | + PFN_vkVoidFunction p = nullptr; |
| 51 | + (void)userData; |
| 52 | + |
| 53 | + if ( ri.VK_GetInstanceProcAddr && vk_instance != VK_NULL_HANDLE ) { |
| 54 | + p = (PFN_vkVoidFunction)ri.VK_GetInstanceProcAddr( vk_instance, name ); |
| 55 | + } |
| 56 | + if ( p == nullptr && qvkGetDeviceProcAddr != nullptr && vk.device != VK_NULL_HANDLE ) { |
| 57 | + p = (PFN_vkVoidFunction)qvkGetDeviceProcAddr( vk.device, name ); |
| 58 | + } |
| 59 | + return p; |
| 60 | +} |
| 61 | + |
| 62 | +extern "C" bool VkImgui_InitVulkanBackend( ImGui_ImplVulkan_InitInfo *outInfo, char *errBuf, size_t errBufSize ) |
| 63 | +{ |
| 64 | + ImGui_ImplVulkan_InitInfo info; |
| 65 | + |
| 66 | + if ( errBuf && errBufSize > 0 ) { |
| 67 | + errBuf[0] = '\0'; |
| 68 | + } |
| 69 | + |
| 70 | + if ( vk_instance == VK_NULL_HANDLE || vk.device == VK_NULL_HANDLE || vk.queue == VK_NULL_HANDLE || |
| 71 | + vk.physical_device == VK_NULL_HANDLE ) { |
| 72 | + if ( errBuf && errBufSize > 0 ) { |
| 73 | + Q_strncpyz( errBuf, "Vulkan device not ready", errBufSize ); |
| 74 | + } |
| 75 | + return false; |
| 76 | + } |
| 77 | + |
| 78 | + if ( !ImGui_ImplVulkan_LoadFunctions( 0, VkImgui_ResolveVulkanProc, nullptr ) ) { |
| 79 | + if ( errBuf && errBufSize > 0 ) { |
| 80 | + Q_strncpyz( errBuf, "ImGui_ImplVulkan_LoadFunctions failed", errBufSize ); |
| 81 | + } |
| 82 | + return false; |
| 83 | + } |
| 84 | + |
| 85 | + Com_Memset( &info, 0, sizeof( info ) ); |
| 86 | +#ifdef VK_API_VERSION_1_4 |
| 87 | + info.ApiVersion = VK_API_VERSION_1_4; |
| 88 | +#elif defined( VK_API_VERSION_1_3 ) |
| 89 | + info.ApiVersion = VK_API_VERSION_1_3; |
| 90 | +#else |
| 91 | + info.ApiVersion = VK_API_VERSION_1_2; |
| 92 | +#endif |
| 93 | + info.Instance = vk_instance; |
| 94 | + info.PhysicalDevice = vk.physical_device; |
| 95 | + info.Device = vk.device; |
| 96 | + info.QueueFamily = vk.queue_family_index; |
| 97 | + info.Queue = vk.queue; |
| 98 | + info.DescriptorPool = VK_NULL_HANDLE; |
| 99 | + info.DescriptorPoolSize = 64; |
| 100 | + info.MinImageCount = 2; |
| 101 | + info.ImageCount = vk.swapchain_image_count > 0 ? vk.swapchain_image_count : 2; |
| 102 | + if ( info.ImageCount < info.MinImageCount ) { |
| 103 | + info.ImageCount = info.MinImageCount; |
| 104 | + } |
| 105 | + info.PipelineCache = VK_NULL_HANDLE; |
| 106 | + info.PipelineInfoMain.RenderPass = vk.render_pass.overlay_compose; |
| 107 | + info.PipelineInfoMain.Subpass = 0; |
| 108 | + info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; |
| 109 | + info.UseDynamicRendering = false; |
| 110 | + |
| 111 | + if ( ImGui_ImplVulkan_Init( &info ) ) { |
| 112 | + if ( outInfo ) { |
| 113 | + *outInfo = info; |
| 114 | + } |
| 115 | + return true; |
| 116 | + } |
| 117 | + |
| 118 | + if ( errBuf && errBufSize > 0 ) { |
| 119 | + Q_strncpyz( errBuf, "ImGui_ImplVulkan_Init failed", errBufSize ); |
| 120 | + } |
| 121 | + return false; |
| 122 | +} |
| 123 | + |
| 124 | +extern "C" void VkImgui_ShutdownVulkanBackend( void ) |
| 125 | +{ |
| 126 | + ImGui_ImplVulkan_Shutdown(); |
| 127 | +} |
| 128 | + |
| 129 | +extern "C" void VkImgui_NewFrameVulkan( void ) |
| 130 | +{ |
| 131 | + ImGui_ImplVulkan_NewFrame(); |
| 132 | +} |
| 133 | + |
| 134 | +extern "C" void VkImgui_RenderDrawDataVulkan( ImDrawData *drawData, VkCommandBuffer cmd ) |
| 135 | +{ |
| 136 | + if ( drawData != nullptr && cmd != VK_NULL_HANDLE ) { |
| 137 | + ImGui_ImplVulkan_RenderDrawData( drawData, cmd ); |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +extern "C" void VkImgui_UpdateMouseFromSDL( ImGuiIO *io, qboolean inspectorWantsInput ) |
| 142 | +{ |
| 143 | +#if defined( USE_SDL ) && !defined( ANDROID ) |
| 144 | + if ( !inspectorWantsInput || io == nullptr || SDL_window == nullptr ) { |
| 145 | + return; |
| 146 | + } |
| 147 | + { |
| 148 | + int mx = 0; |
| 149 | + int my = 0; |
| 150 | + const Uint32 buttons = SDL_GetMouseState( &mx, &my ); |
| 151 | + io->MousePos = ImVec2( (float)mx, (float)my ); |
| 152 | + io->MouseDown[0] = ( buttons & SDL_BUTTON( SDL_BUTTON_LEFT ) ) != 0; |
| 153 | + io->MouseDown[1] = ( buttons & SDL_BUTTON( SDL_BUTTON_RIGHT ) ) != 0; |
| 154 | + io->MouseDown[2] = ( buttons & SDL_BUTTON( SDL_BUTTON_MIDDLE ) ) != 0; |
| 155 | + } |
| 156 | +#else |
| 157 | + (void)io; |
| 158 | + (void)inspectorWantsInput; |
| 159 | +#endif |
| 160 | +} |
| 161 | + |
| 162 | +extern "C" void VkImgui_NotifySwapchainRestart( void ) |
| 163 | +{ |
| 164 | + if ( vk.swapchain_image_count >= 2 ) { |
| 165 | + ImGui_ImplVulkan_SetMinImageCount( vk.swapchain_image_count ); |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +extern "C" void VkImgui_RecordOverlayPass( void ) |
| 170 | +{ |
| 171 | + extern cvar_t *r_imgui; |
| 172 | + |
| 173 | + if ( !g_vkImguiBackendReady || !vkImguiState.active ) { |
| 174 | + return; |
| 175 | + } |
| 176 | + if ( !r_imgui || !r_imgui->integer ) { |
| 177 | + return; |
| 178 | + } |
| 179 | + if ( ImGui::GetCurrentContext() == nullptr ) { |
| 180 | + return; |
| 181 | + } |
| 182 | + if ( vk.cmd == nullptr || vk.cmd->command_buffer == VK_NULL_HANDLE ) { |
| 183 | + return; |
| 184 | + } |
| 185 | + if ( vk.render_pass.overlay_compose == VK_NULL_HANDLE || |
| 186 | + vk.cmd->swapchain_image_index >= MAX_SWAPCHAIN_IMAGES || |
| 187 | + vk.framebuffers.overlay_compose[ vk.cmd->swapchain_image_index ] == VK_NULL_HANDLE || |
| 188 | + vk.renderWidth == 0 || vk.renderHeight == 0 ) { |
| 189 | + return; |
| 190 | + } |
| 191 | + |
| 192 | + ImDrawData *dd = ImGui::GetDrawData(); |
| 193 | + if ( dd == nullptr || !dd->Valid ) { |
| 194 | + return; |
| 195 | + } |
| 196 | + |
| 197 | + vk_begin_render_pass_tracked( vk.render_pass.overlay_compose, |
| 198 | + vk.framebuffers.overlay_compose[ vk.cmd->swapchain_image_index ], |
| 199 | + qfalse, vk.renderWidth, vk.renderHeight ); |
| 200 | + ImGui_ImplVulkan_RenderDrawData( dd, vk.cmd->command_buffer ); |
| 201 | + vk_end_render_pass_tracked(); |
| 202 | +} |
| 203 | + |
| 204 | +#endif /* USE_IMGUI */ |
0 commit comments