ADD: Window mode system
This commit is contained in:
@@ -188,6 +188,23 @@ void VulkanEngine::init()
|
||||
window_flags
|
||||
);
|
||||
|
||||
_windowMode = WindowMode::Windowed;
|
||||
_windowDisplayIndex = SDL_GetWindowDisplayIndex(_window);
|
||||
if (_windowDisplayIndex < 0) _windowDisplayIndex = 0;
|
||||
{
|
||||
int wx = 0, wy = 0, ww = 0, wh = 0;
|
||||
SDL_GetWindowPosition(_window, &wx, &wy);
|
||||
SDL_GetWindowSize(_window, &ww, &wh);
|
||||
if (ww > 0 && wh > 0)
|
||||
{
|
||||
_windowedRect.x = wx;
|
||||
_windowedRect.y = wy;
|
||||
_windowedRect.w = ww;
|
||||
_windowedRect.h = wh;
|
||||
_windowedRect.valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
_deviceManager = std::make_shared<DeviceManager>();
|
||||
_deviceManager->init_vulkan(_window);
|
||||
|
||||
@@ -332,6 +349,125 @@ void VulkanEngine::init()
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
void VulkanEngine::set_window_mode(WindowMode mode, int display_index)
|
||||
{
|
||||
if (!_window) return;
|
||||
|
||||
const int num_displays = SDL_GetNumVideoDisplays();
|
||||
int current_display = SDL_GetWindowDisplayIndex(_window);
|
||||
if (current_display < 0) current_display = 0;
|
||||
|
||||
if (num_displays <= 0)
|
||||
{
|
||||
display_index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (display_index < 0) display_index = current_display;
|
||||
display_index = std::clamp(display_index, 0, num_displays - 1);
|
||||
}
|
||||
|
||||
const WindowMode prev_mode = _windowMode;
|
||||
const bool entering_fullscreen = (prev_mode == WindowMode::Windowed) && (mode != WindowMode::Windowed);
|
||||
|
||||
if (entering_fullscreen)
|
||||
{
|
||||
int wx = 0, wy = 0, ww = 0, wh = 0;
|
||||
SDL_GetWindowPosition(_window, &wx, &wy);
|
||||
SDL_GetWindowSize(_window, &ww, &wh);
|
||||
if (ww > 0 && wh > 0)
|
||||
{
|
||||
_windowedRect.x = wx;
|
||||
_windowedRect.y = wy;
|
||||
_windowedRect.w = ww;
|
||||
_windowedRect.h = wh;
|
||||
_windowedRect.valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are currently in any fullscreen mode, leave fullscreen first so we can safely
|
||||
// move the window to the target display before re-entering fullscreen.
|
||||
if (prev_mode != WindowMode::Windowed)
|
||||
{
|
||||
if (SDL_SetWindowFullscreen(_window, 0) != 0)
|
||||
{
|
||||
fmt::println("[Window] SDL_SetWindowFullscreen(0) failed: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
// Move the window to the selected display (applies to both windowed and fullscreen).
|
||||
SDL_SetWindowPosition(_window,
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display_index),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display_index));
|
||||
|
||||
if (mode == WindowMode::Windowed)
|
||||
{
|
||||
SDL_SetWindowBordered(_window, SDL_TRUE);
|
||||
SDL_SetWindowResizable(_window, SDL_TRUE);
|
||||
|
||||
int target_w = 0, target_h = 0;
|
||||
if (_windowedRect.valid)
|
||||
{
|
||||
target_w = _windowedRect.w;
|
||||
target_h = _windowedRect.h;
|
||||
}
|
||||
if (target_w <= 0 || target_h <= 0)
|
||||
{
|
||||
SDL_GetWindowSize(_window, &target_w, &target_h);
|
||||
}
|
||||
if (target_w > 0 && target_h > 0)
|
||||
{
|
||||
SDL_SetWindowSize(_window, target_w, target_h);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_SetWindowBordered(_window, SDL_FALSE);
|
||||
|
||||
const Uint32 fullscreen_flag =
|
||||
(mode == WindowMode::FullscreenDesktop) ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
|
||||
|
||||
if (mode == WindowMode::FullscreenExclusive)
|
||||
{
|
||||
SDL_DisplayMode desktop{};
|
||||
if (SDL_GetDesktopDisplayMode(display_index, &desktop) == 0)
|
||||
{
|
||||
// Request desktop mode by default. Future UI can add explicit mode selection.
|
||||
SDL_SetWindowDisplayMode(_window, &desktop);
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_SetWindowFullscreen(_window, fullscreen_flag) != 0)
|
||||
{
|
||||
fmt::println("[Window] SDL_SetWindowFullscreen({}) failed: {}",
|
||||
static_cast<unsigned>(fullscreen_flag),
|
||||
SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
_windowMode = mode;
|
||||
_windowDisplayIndex = SDL_GetWindowDisplayIndex(_window);
|
||||
if (_windowDisplayIndex < 0) _windowDisplayIndex = display_index;
|
||||
|
||||
// Make sure SDL has processed the fullscreen/move requests before we query drawable size for swapchain recreation.
|
||||
SDL_PumpEvents();
|
||||
|
||||
// Recreate swapchain immediately so the very next draw uses correct extent.
|
||||
if (_swapchainManager)
|
||||
{
|
||||
_swapchainManager->resize_swapchain(_window);
|
||||
if (_swapchainManager->resize_requested)
|
||||
{
|
||||
resize_requested = true;
|
||||
_last_resize_event_ms = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
resize_requested = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanEngine::set_logical_render_extent(VkExtent2D extent)
|
||||
{
|
||||
extent = clamp_nonzero_extent(extent);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <cstdint>
|
||||
#include "vk_mem_alloc.h"
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
@@ -62,6 +63,13 @@ struct MeshNode : public Node
|
||||
class VulkanEngine
|
||||
{
|
||||
public:
|
||||
enum class WindowMode : uint8_t
|
||||
{
|
||||
Windowed = 0,
|
||||
FullscreenDesktop = 1, // borderless fullscreen ("fullscreen windowed")
|
||||
FullscreenExclusive = 2 // exclusive fullscreen (may change display mode)
|
||||
};
|
||||
|
||||
bool _isInitialized{false};
|
||||
int _frameNumber{0};
|
||||
|
||||
@@ -80,6 +88,9 @@ public:
|
||||
|
||||
struct SDL_Window *_window{nullptr};
|
||||
|
||||
WindowMode _windowMode{WindowMode::Windowed};
|
||||
int _windowDisplayIndex{0};
|
||||
|
||||
FrameResources _frames[FRAME_OVERLAP];
|
||||
|
||||
FrameResources &get_current_frame() { return _frames[_frameNumber % FRAME_OVERLAP]; };
|
||||
@@ -204,6 +215,9 @@ public:
|
||||
//run main loop
|
||||
void run();
|
||||
|
||||
// Window controls (runtime)
|
||||
void set_window_mode(WindowMode mode, int display_index);
|
||||
|
||||
// Rendering resolution controls:
|
||||
// - logicalRenderExtent controls camera aspect and picking (letterboxed view).
|
||||
// - renderScale controls the internal render target pixel count (logical * scale).
|
||||
@@ -258,6 +272,15 @@ public:
|
||||
bool freeze_rendering{false};
|
||||
|
||||
private:
|
||||
struct WindowedRect
|
||||
{
|
||||
int x{0};
|
||||
int y{0};
|
||||
int w{0};
|
||||
int h{0};
|
||||
bool valid{false};
|
||||
} _windowedRect;
|
||||
|
||||
void init_frame_resources();
|
||||
|
||||
void init_pipelines();
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
#include "SDL2/SDL.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ImGuizmo.h"
|
||||
|
||||
@@ -23,12 +25,93 @@
|
||||
#include "device/images.h"
|
||||
#include "context.h"
|
||||
#include <core/types.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "mesh_bvh.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
static void ui_window(VulkanEngine *eng)
|
||||
{
|
||||
if (!eng || !eng->_window) return;
|
||||
|
||||
int num_displays = SDL_GetNumVideoDisplays();
|
||||
if (num_displays <= 0)
|
||||
{
|
||||
ImGui::Text("No displays reported by SDL (%s)", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
int current_display = SDL_GetWindowDisplayIndex(eng->_window);
|
||||
if (current_display < 0) current_display = eng->_windowDisplayIndex;
|
||||
current_display = std::clamp(current_display, 0, num_displays - 1);
|
||||
|
||||
const char *cur_display_name = SDL_GetDisplayName(current_display);
|
||||
if (!cur_display_name) cur_display_name = "Unknown";
|
||||
|
||||
ImGui::Text("Current: %s on display %d (%s)",
|
||||
(eng->_windowMode == VulkanEngine::WindowMode::Windowed)
|
||||
? "Windowed"
|
||||
: (eng->_windowMode == VulkanEngine::WindowMode::FullscreenDesktop)
|
||||
? "Borderless Fullscreen"
|
||||
: "Exclusive Fullscreen",
|
||||
current_display,
|
||||
cur_display_name);
|
||||
|
||||
static int pending_display = -1;
|
||||
static int pending_mode = -1; // 0 windowed, 1 borderless, 2 exclusive
|
||||
if (pending_display < 0) pending_display = current_display;
|
||||
if (pending_mode < 0) pending_mode = static_cast<int>(eng->_windowMode);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginCombo("Monitor", cur_display_name))
|
||||
{
|
||||
for (int i = 0; i < num_displays; ++i)
|
||||
{
|
||||
const char *name = SDL_GetDisplayName(i);
|
||||
if (!name) name = "Unknown";
|
||||
const bool selected = (pending_display == i);
|
||||
if (ImGui::Selectable(name, selected))
|
||||
{
|
||||
pending_display = i;
|
||||
}
|
||||
if (selected)
|
||||
{
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
const char *mode_labels[] = {
|
||||
"Windowed",
|
||||
"Borderless (Fullscreen Desktop)",
|
||||
"Exclusive Fullscreen"
|
||||
};
|
||||
ImGui::Combo("Mode", &pending_mode, mode_labels, 3);
|
||||
|
||||
ImGui::TextUnformatted("Apply triggers immediate swapchain recreation.");
|
||||
if (ImGui::Button("Apply"))
|
||||
{
|
||||
auto mode = static_cast<VulkanEngine::WindowMode>(std::clamp(pending_mode, 0, 2));
|
||||
eng->set_window_mode(mode, pending_display);
|
||||
|
||||
// Re-sync pending selections with what SDL actually applied.
|
||||
pending_display = SDL_GetWindowDisplayIndex(eng->_window);
|
||||
if (pending_display < 0) pending_display = eng->_windowDisplayIndex;
|
||||
pending_display = std::clamp(pending_display, 0, num_displays - 1);
|
||||
pending_mode = static_cast<int>(eng->_windowMode);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Use Current"))
|
||||
{
|
||||
pending_display = current_display;
|
||||
pending_mode = static_cast<int>(eng->_windowMode);
|
||||
}
|
||||
}
|
||||
|
||||
// Background / compute playground
|
||||
static void ui_background(VulkanEngine *eng)
|
||||
{
|
||||
@@ -1364,6 +1447,11 @@ void vk_engine_draw_debug_ui(VulkanEngine *eng)
|
||||
ui_overview(eng);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Window"))
|
||||
{
|
||||
ui_window(eng);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Background"))
|
||||
{
|
||||
ui_background(eng);
|
||||
|
||||
Reference in New Issue
Block a user