Skip to content

feat: improve HiDPI behavior across SDL, Vulkan, and ImGui#37

Merged
K1ngst0m merged 1 commit intomainfrom
dev/refine-imgui
Jan 8, 2026
Merged

feat: improve HiDPI behavior across SDL, Vulkan, and ImGui#37
K1ngst0m merged 1 commit intomainfrom
dev/refine-imgui

Conversation

@K1ngst0m
Copy link
Copy Markdown
Collaborator

@K1ngst0m K1ngst0m commented Jan 8, 2026

User description

  • Create SDL window with high pixel density enabled
  • Drive swapchain dimensions from pixel size (not logical window size)
  • Handle DPI/scale change events as resize triggers
  • Ship Roboto Mono and rebuild font atlas when display scale changes
  • Include SIL Open Font License alongside the font asset

PR Type

Enhancement


Description

  • Handle DPI/scale change events as window resize triggers

  • Use pixel-based dimensions for swapchain instead of logical sizes

  • Rebuild ImGui font atlas dynamically when display scale changes

  • Add Roboto Mono font with SIL Open Font License

  • Enable SDL high pixel density mode for HiDPI support


Diagram Walkthrough

flowchart LR
  SDL["SDL Window<br/>HIGH_PIXEL_DENSITY"] -->|pixel dimensions| Vulkan["Vulkan Swapchain<br/>SDL_GetWindowSizeInPixels"]
  DPIEvent["DPI/Scale Change<br/>Events"] -->|triggers rebuild| ImGui["ImGui Font Atlas<br/>Roboto Mono"]
  ImGui -->|display_scale| Fonts["Font Rasterization<br/>RasterizerDensity"]
Loading

File Walkthrough

Relevant files
Enhancement
application.cpp
Handle DPI and pixel size change events                                   

src/app/application.cpp

  • Added event handlers for SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED and
    SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED
  • Both events now trigger window resize flag to ensure proper swapchain
    recreation
+5/-0     
sdl_platform.cpp
Enable high pixel density SDL window                                         

src/app/sdl_platform.cpp

  • Added SDL_WINDOW_HIGH_PIXEL_DENSITY flag when creating SDL window
  • Reformatted window flags initialization for clarity
+3/-3     
vulkan_backend.cpp
Use pixel dimensions for swapchain sizing                               

src/render/backend/vulkan_backend.cpp

  • Replaced SDL_GetWindowSize with SDL_GetWindowSizeInPixels for
    pixel-accurate dimensions
  • Added error handling for SDL_GetWindowSizeInPixels calls in three
    locations
  • Added #include header
+18/-4   
imgui_layer.cpp
Dynamic font atlas rebuild on DPI changes                               

src/ui/imgui_layer.cpp

  • Added get_display_scale() helper function to safely retrieve window
    display scale
  • Implemented rebuild_fonts() function to dynamically rebuild font atlas
    with proper scaling
  • Added DPI change detection in begin_frame() to trigger font rebuild
    when scale changes
  • Call ImGui_ImplVulkan_CreateFontsTexture() after initialization and
    DPI changes
  • Store window pointer, font path, and last display scale as member
    variables
  • Added includes for SDL video, cmath, and filesystem
+76/-1   
imgui_layer.hpp
Add HiDPI state tracking members                                                 

src/ui/imgui_layer.hpp

  • Changed SDL include from full to forward declarations
  • Added member variables: m_font_path, m_font_size_pixels, m_window,
    m_last_display_scale
  • Added includes for array, string, and vector headers
+11/-2   
Documentation
OFL.txt
Add SIL Open Font License                                                               

assets/fonts/OFL.txt

  • Added complete SIL Open Font License text (91 lines)
  • Covers licensing terms for Roboto Mono font asset
+91/-0   

Summary by CodeRabbit

  • New Features

    • High-DPI display support with automatic font scaling and dynamic font rebuilds
  • Improvements

    • Better detection of pixel-size and display-scale changes for windows
    • More reliable rendering by using pixel-accurate window sizing
    • Window creation now targets high pixel density displays by default
  • Documentation

    • Added SIL Open Font License file for bundled fonts

✏️ Tip: You can customize this high-level summary in your review settings.

- Create SDL window with high pixel density enabled
- Drive swapchain dimensions from pixel size (not logical window size)
- Handle DPI/scale change events as resize triggers
- Ship Roboto Mono and rebuild font atlas when display scale changes
- Include SIL Open Font License alongside the font asset
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Adds HiDPI support: window created with high-pixel-density flag, SDL pixel-size and display-scale events treated as resizes, Vulkan backend queries window size in pixels with error handling, and ImGuiLayer detects display-scale changes to rebuild and recreate HiDPI fonts dynamically.

Changes

Cohort / File(s) Summary
Font License
assets/fonts/OFL.txt
Adds SIL Open Font License v1.1 text for bundled fonts.
Platform Configuration
src/app/sdl_platform.cpp
Adds SDL_WINDOW_HIGH_PIXEL_DENSITY to window creation flags.
Event Handling
src/app/application.cpp
Treats WINDOW_PIXEL_SIZE_CHANGED and WINDOW_DISPLAY_SCALE_CHANGED as resize events by setting m_window_resized.
Graphics Rendering
src/render/backend/vulkan_backend.cpp
Replaces SDL_GetWindowSize with SDL_GetWindowSizeInPixels, adds error handling and loops handling pixel-size query failures; includes <array>.
UI Font Scaling & State
src/ui/imgui_layer.cpp, <br>src/ui/imgui_layer.hpp`
Adds per-window display scale detection, rebuild_fonts implementation, font path/size and m_last_display_scale members; detects DPI changes in begin_frame, rebuilds fonts and recreates ImGui Vulkan font texture.

Sequence Diagram

sequenceDiagram
    participant SDL as SDL (Window / Events)
    participant App as Application
    participant ImGui as ImGuiLayer
    participant Vulkan as Vulkan Backend

    SDL->>App: WINDOW_DISPLAY_SCALE_CHANGED / WINDOW_PIXEL_SIZE_CHANGED
    App->>App: set m_window_resized = true
    App->>ImGui: begin_frame()
    ImGui->>SDL: query display scale (get_display_scale)
    alt display scale changed
        ImGui->>ImGui: compute new font size
        ImGui->>ImGui: rebuild_fonts(new_scale)
        ImGui->>Vulkan: destroy and recreate font texture
    end
    ImGui->>Vulkan: render frame with scaled fonts
    SDL->>App: WINDOW_RESIZED
    App->>Vulkan: recreate_swapchain()
    Vulkan->>SDL: SDL_GetWindowSizeInPixels()
    Vulkan->>Vulkan: create framebuffer with pixel dimensions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I nibble pixels, tiny and bright,

Fonts grow bold with every light,
Scales shift soft across the screen,
Crisp lines wake where blurs had been,
A rabbit cheers this HiDPI scene.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main objective of the changeset: improving HiDPI support across three key components (SDL, Vulkan, and ImGui).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@K1ngst0m K1ngst0m marked this pull request as ready for review January 8, 2026 12:47
@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Jan 8, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Log format unclear: The new logging calls do not show whether logs are emitted in a structured format (e.g.,
JSON) as required, which cannot be confirmed from the diff alone.

Referred Code
    if (!ImGui_ImplVulkan_CreateFontsTexture()) {
        GOGGLES_LOG_WARN("ImGui_ImplVulkan_CreateFontsTexture failed (UI may look wrong on HiDPI)");
    }

    GOGGLES_LOG_INFO("ImGui layer initialized");
    return make_result_ptr(std::move(layer));
}

ImGuiLayer::~ImGuiLayer() {
    shutdown();
}

void ImGuiLayer::shutdown() {
    if (m_device) {
        auto wait_result = m_device.waitIdle();
        if (wait_result != vk::Result::eSuccess) {
            GOGGLES_LOG_WARN("waitIdle failed in ImGui shutdown: {}", vk::to_string(wait_result));
        }
        ImGui_ImplVulkan_Shutdown();
        ImGui_ImplSDL3_Shutdown();
        ImGui::DestroyContext();


 ... (clipped 32 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Jan 8, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Return early if GPU synchronization fails

In begin_frame, return early if m_device.waitIdle() fails to prevent modifying
GPU resources in an unsafe state during font rebuilding.

src/ui/imgui_layer.cpp [182-213]

 void ImGuiLayer::begin_frame() {
     if (m_window != nullptr) {
         float display_scale = get_display_scale(m_window);
         if (std::fabs(display_scale - m_last_display_scale) > 0.01F) {
             auto wait_result = m_device.waitIdle();
             if (wait_result != vk::Result::eSuccess) {
                 GOGGLES_LOG_WARN("waitIdle failed during ImGui DPI font rebuild: {}",
                                  vk::to_string(wait_result));
+                // Do not proceed with font texture recreation if we can't sync with the GPU.
+                return;
             }
 
             auto& io = ImGui::GetIO();
             rebuild_fonts(io, m_font_path, m_font_size_pixels, display_scale);
 
             ImGui_ImplVulkan_DestroyFontsTexture();
             if (!ImGui_ImplVulkan_CreateFontsTexture()) {
                 GOGGLES_LOG_WARN(
                     "ImGui_ImplVulkan_CreateFontsTexture failed after DPI change (scale={})",
                     display_scale);
             }
 
             m_last_display_scale = display_scale;
         }
     }
 
     ImGui_ImplVulkan_NewFrame();
     ImGui_ImplSDL3_NewFrame();
     ImGui::NewFrame();
 
     if (m_visible) {
         draw_shader_controls();
     }
 }

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a critical error handling flaw where failure to synchronize with the GPU is not handled, potentially leading to crashes or undefined behavior.

Medium
General
Avoid repeated filesystem existence checks
Suggestion Impact:rebuild_fonts was converted to an ImGuiLayer member function using cached members (m_font_exists, m_font_path, m_font_size_pixels) and removed the per-call std::filesystem::exists check and error_code logic.

code diff:

-void rebuild_fonts(ImGuiIO& io, const std::filesystem::path& font_path, float size_pixels,
-                   float display_scale) {
+void ImGuiLayer::rebuild_fonts(ImGuiIO& io, float display_scale) {
     io.Fonts->Clear();
 
     ImFontConfig cfg{};
     cfg.RasterizerDensity = 1.0F;
 
     ImFont* font = nullptr;
-    const float rasterized_size_pixels = size_pixels * display_scale;
-    std::error_code ec;
-    if (!font_path.empty() && std::filesystem::exists(font_path, ec) && !ec) {
+    const float rasterized_size_pixels = m_font_size_pixels * display_scale;
+    if (m_font_exists) {
         font =
-            io.Fonts->AddFontFromFileTTF(font_path.string().c_str(), rasterized_size_pixels, &cfg);
+            io.Fonts->AddFontFromFileTTF(m_font_path.string().c_str(), rasterized_size_pixels, &cfg);
         if (font == nullptr) {
             GOGGLES_LOG_WARN("Failed to load ImGui font from '{}', falling back to default",
-                             font_path.string());
+                             m_font_path.string());
         }

Avoid redundant filesystem checks in rebuild_fonts by checking for the font
file's existence only once at startup and caching the result.

src/ui/imgui_layer.cpp [38-65]

-void rebuild_fonts(ImGuiIO& io, const std::filesystem::path& font_path, float size_pixels,
-                   float display_scale) {
+void ImGuiLayer::rebuild_fonts(ImGuiIO& io, float display_scale) {
     io.Fonts->Clear();
 
     ImFontConfig cfg{};
     cfg.RasterizerDensity = 1.0F;
 
     ImFont* font = nullptr;
-    const float rasterized_size_pixels = size_pixels * display_scale;
-    std::error_code ec;
-    if (!font_path.empty() && std::filesystem::exists(font_path, ec) && !ec) {
+    const float rasterized_size_pixels = m_font_size_pixels * display_scale;
+    if (m_font_exists) {
         font =
-            io.Fonts->AddFontFromFileTTF(font_path.string().c_str(), rasterized_size_pixels, &cfg);
+            io.Fonts->AddFontFromFileTTF(m_font_path.string().c_str(), rasterized_size_pixels, &cfg);
         if (font == nullptr) {
             GOGGLES_LOG_WARN("Failed to load ImGui font from '{}', falling back to default",
-                             font_path.string());
+                             m_font_path.string());
         }
     }
 
     if (font == nullptr) {
         ImFontConfig default_cfg = cfg;
         default_cfg.SizePixels = rasterized_size_pixels;
         font = io.Fonts->AddFontDefault(&default_cfg);
     }
 
     io.FontDefault = font;
     io.FontGlobalScale = 1.0F / display_scale;
 }

[Suggestion processed]

Suggestion importance[1-10]: 4

__

Why: The suggestion correctly identifies a redundant filesystem check and proposes a valid optimization by caching the result, which is good practice even if the performance impact is minor.

Low
  • Update

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ui/imgui_layer.cpp (1)

1-416: Pipeline failure: Code requires formatting.

The CI pipeline reports that the code needs formatting. Please run pixi run format locally before merging.

🤖 Fix all issues with AI agents
In @src/ui/imgui_layer.cpp:
- Around line 82-84: The call to rebuild_fonts is incorrect:
ImGuiLayer::rebuild_fonts is a member that takes (ImGuiIO&, float) but the code
calls a free function with four args; change the call to use the member on the
layer instance and pass only (io, display_scale), i.e. replace rebuild_fonts(io,
layer->m_font_path, layer->m_font_size_pixels, display_scale) with
layer->rebuild_fonts(io, display_scale) so the member function reads
m_font_path, m_font_size_pixels, and m_font_exists directly and
m_last_display_scale remains updated.
- Around line 38-63: The ImGuiLayer::rebuild_fonts implementation must be moved
out of the anonymous namespace and its signature must match call sites; change
the definition to the class member signature that accepts the font path and font
size (e.g., ImGuiLayer::rebuild_fonts(ImGuiIO& io, const std::filesystem::path&
font_path, float font_size_pixels, float display_scale)) and update the body to
use the passed font_path and font_size_pixels instead of
m_font_path/m_font_size_pixels; also add a member bool m_font_exists to the
ImGuiLayer class (declare in the header and initialize in the constructor by
checking if the font file exists) or otherwise compute existence from the passed
font_path before attempting AddFontFromFileTTF, and ensure the function is
placed outside any anonymous namespace.
🧹 Nitpick comments (1)
src/ui/imgui_layer.cpp (1)

180-204: Consider moving GPU synchronization and font rebuilds out of the per-frame hot path.

The begin_frame() method performs waitIdle() (line 184) and dynamic memory allocations (via rebuild_fonts) when DPI changes are detected. Based on coding guidelines, per-frame code paths should not use blocking synchronization primitives or perform dynamic allocations.

While DPI changes are rare events, this still executes within the frame rendering loop and could cause frame drops or stuttering when the user moves the window between monitors.

Consider an alternative approach:

  1. Set a flag when DPI change is detected in begin_frame()
  2. Handle the rebuild at the start of the next frame loop iteration (before begin_frame() is called)
  3. Or defer rendering for one frame while the rebuild completes asynchronously

This would maintain the strict separation of per-frame hot paths from blocking/allocating operations.

Based on learnings, per-frame code paths must not exceed 8ms CPU budget or perform dynamic allocations.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df8eba3 and 835596e.

📒 Files selected for processing (1)
  • src/ui/imgui_layer.cpp
🧰 Additional context used
🧠 Learnings (15)
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/render/**/*.{cpp,hpp} : All vulkan-hpp calls returning `vk::Result` must be explicitly checked. Prohibited pattern: `static_cast<void>(device.waitIdle())`. Use macro `VK_TRY(call, ErrorCode, "message")` for vk::Result early return or manual pattern with error handling.

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/render/**/*.{cpp,hpp} : Vulkan resource management: Follow RAII guidelines. Use `vk::Unique*` only for appropriate resource types. Call `device.waitIdle()` or wait on fences before destroying GPU-async resources. Store creation info with resources for debugging/recreation. Never leak Vulkan objects. Member ordering: declare in reverse destruction order (device before instance).

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2025-12-29T15:44:30.520Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: openspec/project.md:0-0
Timestamp: 2025-12-29T15:44:30.520Z
Learning: Applies to openspec/src/render/**/*.{cpp,hpp} : Use `vk::Unique*` for long-lived Vulkan resources; use plain handles for per-frame/GPU-async resources

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/render/**/*.cpp : Main thread owns: Vulkan instance, device, swapchain lifecycle; queue submission; window events and user input; job coordination. Main thread MUST NOT: block on I/O, perform heavy computation (>1ms), allocate memory in per-frame code paths.

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/**/*.cpp : Error handling macros: Use `VK_TRY` for vk::Result early return in Vulkan code. Use `GOGGLES_TRY` for Result<T> propagation (both as statement and expression). Use `GOGGLES_MUST` for internal invariants that should never fail. Use manual pattern for destructors/cleanup functions where propagation is impossible.

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2025-12-29T15:44:30.520Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: openspec/project.md:0-0
Timestamp: 2025-12-29T15:44:30.520Z
Learning: Applies to openspec/src/capture/vk_layer/**/*.{cpp,hpp} : Do not perform blocking operations or file I/O in capture layer performance-critical paths (e.g., vkQueuePresentKHR)

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/render/**/*.{cpp,hpp} : RAII handle guidelines for Vulkan resources: Use `vk::Unique*` only for appropriate resource types: Instance, Device, Surface (long-lived singletons); Swapchain, Pipelines, Layouts (created once). Use plain `vk::` handles for command buffers (pooled lifetime), per-frame sync primitives (reused), and imported external images (requires explicit sync).

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/render/**/*.cpp : Per-frame code paths on main thread MUST NOT: perform dynamic memory allocations (`new`, `malloc`, `std::make_shared`); use blocking synchronization primitives (`std::mutex`, `std::condition_variable`); exceed 8ms CPU time budget (excluding GPU sync).

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan capture layer threading: Hooks execute in calling thread (usually render thread). No blocking in hooks, especially `vkQueuePresentKHR` hot path. Use atomics or locks for thread-safe layer state.

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/render/**/*.{cpp,hpp} : Application code MUST use vulkan-hpp (C++ bindings), NOT raw Vulkan C API. Use `vk::` types (e.g., `vk::Instance`, `vk::Device`). Do not use raw C handles like `VkInstance`, `VkDevice`. Required configuration: `#define VULKAN_HPP_NO_EXCEPTIONS`, `#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1`, `#include <vulkan/vulkan.hpp>`.

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2025-12-29T15:44:30.520Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: openspec/project.md:0-0
Timestamp: 2025-12-29T15:44:30.520Z
Learning: Applies to openspec/src/capture/vk_layer/**/*.{cpp,hpp} : Capture layer (vk_layer/) MUST use raw Vulkan C API, not vulkan-hpp

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.054Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.054Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Capture layer exception: The Vulkan capture layer (`src/capture/vk_layer/`) is exempt and MUST use the raw Vulkan C API because layer dispatch tables require C function pointers and the layer must intercept C API calls.

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2025-12-29T15:44:30.520Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: openspec/project.md:0-0
Timestamp: 2025-12-29T15:44:30.520Z
Learning: Vulkan capture layer is the primary frame capture method, intercepting rendered frames before presentation for zero-copy access

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2026-01-07T07:20:57.053Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-07T07:20:57.053Z
Learning: Applies to docs/**/*.{cpp,hpp} : Use top-level namespace `goggles`. Use module namespaces `goggles::<module>` (e.g., `goggles::capture`, `goggles::render`). Use sub-module namespaces `goggles::<module>::<submodule>`. Never use `using namespace` in headers. Prefer explicit namespace qualification or scoped `using` declarations.

Applied to files:

  • src/ui/imgui_layer.cpp
📚 Learning: 2025-12-29T15:44:30.520Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: openspec/project.md:0-0
Timestamp: 2025-12-29T15:44:30.520Z
Learning: Applies to openspec/**/*.hpp : Organize namespaces as `goggles` with sub-modules like `goggles::capture`, `goggles::render`; never use `using namespace` in headers

Applied to files:

  • src/ui/imgui_layer.cpp
🧬 Code graph analysis (1)
src/ui/imgui_layer.cpp (1)
src/ui/imgui_layer.hpp (3)
  • window (56-57)
  • path (74-74)
  • path (95-95)
🪛 Cppcheck (2.19.0)
src/ui/imgui_layer.cpp

[error] 189-189: Unmatched ')'. Configuration

(syntaxError)

🪛 GitHub Actions: CI
src/ui/imgui_layer.cpp

[error] 1-1: Code needs formatting. Run 'pixi run format' locally.

🔇 Additional comments (3)
src/ui/imgui_layer.cpp (3)

27-36: LGTM! Well-designed helper function.

The get_display_scale function correctly handles null window and invalid scale values with appropriate fallbacks.


146-148: Good error handling for font texture creation.

Properly checks the return value of ImGui_ImplVulkan_CreateFontsTexture() and logs a warning on failure, which helps diagnose HiDPI issues.


46-53: This review comment is based on incorrect code. The snippet shown does not match the current state of the file. The actual code at lines 46-53 uses a parameter font_path with an inline std::filesystem::exists() check, not a m_font_exists member variable. No such member variable exists in the codebase.

The current implementation already properly validates file existence:

if (!font_path.empty() && std::filesystem::exists(font_path, ec) && !ec)

No changes are needed for this code section.

Likely an incorrect or invalid review comment.

Comment on lines +82 to +84
float display_scale = get_display_scale(window);
layer->m_last_display_scale = display_scale;
rebuild_fonts(io, layer->m_font_path, layer->m_font_size_pixels, display_scale);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Incorrect function call with wrong argument count and missing member access.

Line 84 has two issues:

  1. Wrong number of arguments: Calls rebuild_fonts with 4 arguments (io, layer->m_font_path, layer->m_font_size_pixels, display_scale), but the function signature at line 38 accepts only 2 parameters (ImGuiIO&, float).

  2. Missing member function call: Should be layer->rebuild_fonts(io, display_scale) since rebuild_fonts is a member function (as indicated by the ImGuiLayer:: qualifier at line 38 and the AI summary).

🔧 Proposed fix
     float display_scale = get_display_scale(window);
     layer->m_last_display_scale = display_scale;
-    rebuild_fonts(io, layer->m_font_path, layer->m_font_size_pixels, display_scale);
+    layer->rebuild_fonts(io, display_scale);

The member function will access m_font_path, m_font_size_pixels, and m_font_exists directly as member variables.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
float display_scale = get_display_scale(window);
layer->m_last_display_scale = display_scale;
rebuild_fonts(io, layer->m_font_path, layer->m_font_size_pixels, display_scale);
float display_scale = get_display_scale(window);
layer->m_last_display_scale = display_scale;
layer->rebuild_fonts(io, display_scale);
🤖 Prompt for AI Agents
In @src/ui/imgui_layer.cpp around lines 82 - 84, The call to rebuild_fonts is
incorrect: ImGuiLayer::rebuild_fonts is a member that takes (ImGuiIO&, float)
but the code calls a free function with four args; change the call to use the
member on the layer instance and pass only (io, display_scale), i.e. replace
rebuild_fonts(io, layer->m_font_path, layer->m_font_size_pixels, display_scale)
with layer->rebuild_fonts(io, display_scale) so the member function reads
m_font_path, m_font_size_pixels, and m_font_exists directly and
m_last_display_scale remains updated.

Comment on lines +186 to +193
GOGGLES_LOG_WARN("waitIdle failed during ImGui DPI font rebuild: {}",
vk::to_string(wait_result));
}
vk::to_string(wait_result));
// Do not proceed with font texture recreation if we can't sync with the GPU.
return;
}
rebuild_fonts(io, m_font_path, m_font_size_pixels, display_scale);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Syntax error with malformed code - duplicate statements and unmatched braces.

Lines 186-193 contain severe syntax errors that will prevent compilation:

  1. Duplicate error message: Line 188 completes the log statement, but line 190 has another orphaned vk::to_string(wait_result)); that doesn't belong to any function call.

  2. Unmatched braces: Line 189 closes the if statement, but lines 191-193 have a comment, return statement, and closing brace that don't match any opening brace.

This appears to be a copy-paste or merge error during development.

🔧 Proposed fix to remove duplicate code
             auto wait_result = m_device.waitIdle();
             if (wait_result != vk::Result::eSuccess) {
                 GOGGLES_LOG_WARN("waitIdle failed during ImGui DPI font rebuild: {}",
                                  vk::to_string(wait_result));
+                // Do not proceed with font texture recreation if we can't sync with the GPU.
+                return;
             }
-                                         vk::to_string(wait_result));
-                        // Do not proceed with font texture recreation if we can't sync with the GPU.
-                        return;
-                    }
-            rebuild_fonts(io, m_font_path, m_font_size_pixels, display_scale);
+            
+            auto& io = ImGui::GetIO();
+            rebuild_fonts(io, display_scale);
 
             ImGui_ImplVulkan_DestroyFontsTexture();

Note: The rebuild_fonts call also needs correction (see separate comment).

🧰 Tools
🪛 Cppcheck (2.19.0)

[error] 189-189: Unmatched ')'. Configuration

(syntaxError)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant