Conversation
📝 WalkthroughWalkthroughAdds a Dear ImGui runtime UI and wiring to the SDL3+Vulkan app: vendor/import ImGui, new Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App as Application (main.cpp)
participant UI as ImGuiLayer
participant Backend as VulkanBackend
participant Chain as FilterChain
participant Vulkan as Vulkan Driver
App->>UI: begin_frame()
App->>UI: process_event(SDL_Event)
UI-->>App: user actions (reload, parameter change, passthrough)
alt Preset reload requested
App->>Backend: reload_shader_preset(path)
Backend->>Vulkan: spawn async compile/build
Backend->>Chain: prepare pending filter chain (async)
Backend->>Backend: mark pending chain ready
Backend->>Backend: check_pending_chain_swap()
Backend->>Chain: swap in new chain, schedule old chain deferred destroy
alt load failure
Backend->>Backend: rollback to previous chain
Backend-->>App: error
App->>UI: show error
end
end
alt Passthrough toggle
UI-->>App: toggle passthrough
App->>Backend: set_shader_enabled(enabled)
Backend->>Chain: set_bypass(enabled)
end
alt Parameter change
UI-->>App: parameter change callback
App->>Chain: set_parameter(name,value) (applies next frame)
end
App->>Backend: render_frame_with_ui(frame, ui_callback)
Backend->>UI: ui_callback(cmd, view, extent)
UI->>Vulkan: record ImGui draw commands into command buffer
Backend->>Vulkan: submit & present
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
0efd0ff to
3bb1787
Compare
54a3afe to
edd04a0
Compare
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
||||||||||||||||
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI Agents
In @openspec/changes/add-imgui-runtime-preset-control/design.md:
- Line 15: Update the "Dear ImGui dependency scope" sentence to reflect the
actual acquisition method used in the implementation: replace "vendored or
fetched via CPM" with the Pixi-local package approach (referencing
packages/imgui/recipe.yaml) and clarify that the ImGui docking tag (v1.91+
docking) is linked only to the Goggles app target while the capture layer
remains unchanged and does not link ImGui symbols.
In @packages/imgui/recipe.yaml:
- Around line 14-22: The build snippet runs the $CXX compile step then
unconditionally archives with ar, which can produce an incomplete libimgui.a if
compilation fails; ensure the script checks compilation success by either
enabling strict failure (set -euo pipefail) at the top of the shell block or
chaining the commands so ar rcs and mv only run on success (e.g., run the $CXX
... invocation and then && ar rcs libimgui.a *.o && mv libimgui.a $PREFIX/lib/),
or wrap the compile in an if-check that exits with non-zero on failure.
In @src/render/backend/vulkan_backend.cpp:
- Around line 1307-1341: The rollback path in
VulkanBackend::reload_shader_preset leaves m_preset_path pointing to the old
preset even when restoring previous_path fails or when previous_path is empty,
causing an inconsistent state with the filter chain; after a failed
load_preset(preset_path) and a failed restore
(m_filter_chain->load_preset(previous_path) returns false) or when
previous_path.empty(), clear m_preset_path (e.g., m_preset_path.clear()) and
emit a warning before returning the original error to keep the preset state
consistent with the passthrough filter chain.
In @src/render/chain/filter_chain.cpp:
- Line 303: m_bypass_enabled is a plain bool which can race between UI calls to
set_bypass() and per-frame reads in record() / is_bypass(); change its type to
std::atomic<bool> in the FilterChain class, update any initialization
(constructor) and assignments to use atomic operations (e.g., store) and reads
(load) in set_bypass(), is_bypass(), and where record() checks it so access is
lock-free and thread-safe, and include <atomic> in the header.
🧹 Nitpick comments (4)
pixi.toml (1)
128-128: Consider pinning the patchelf version for consistency.While patchelf is a build-time tool, most other dependencies in this file use at least major version constraints (e.g.,
cmake = "3.31.*",ninja = "1.12.*"). Using a wildcard version"*"may reduce build reproducibility.🔎 Suggested version constraint
-patchelf = "*" +patchelf = ">=0.17"src/app/main.cpp (1)
21-33: Consider exception handling for filesystem operations.
std::filesystem::recursive_directory_iteratorcan throw exceptions (e.g., permission denied, symlink loops). Since the codebase usesVULKAN_HPP_NO_EXCEPTIONS, consider wrapping the iteration withstd::error_codeoverloads to maintain consistency.🔎 Proposed fix using error_code
static auto scan_presets(const std::filesystem::path& dir) -> std::vector<std::filesystem::path> { std::vector<std::filesystem::path> presets; - if (!std::filesystem::exists(dir)) { + std::error_code ec; + if (!std::filesystem::exists(dir, ec) || ec) { return presets; } - for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) { + for (const auto& entry : std::filesystem::recursive_directory_iterator(dir, ec)) { + if (ec) { + GOGGLES_LOG_WARN("Error scanning presets directory: {}", ec.message()); + break; + } if (entry.is_regular_file() && entry.path().extension() == ".slangp") { presets.push_back(entry.path()); } } std::ranges::sort(presets); return presets; }src/render/backend/vulkan_backend.cpp (1)
1158-1238: Significant code duplication withrender_frameandrecord_render_commands.This function duplicates ~80 lines from
render_frame(lines 1112-1134) andrecord_render_commands(lines 910-968). Consider extracting common logic into a shared helper to reduce maintenance burden.🔎 Suggested refactoring approach
Extract the common barrier setup and filter chain recording into a private helper:
auto VulkanBackend::record_render_core(vk::CommandBuffer cmd, uint32_t image_index) -> Result<void> { // Common barrier setup and filter chain recording // ... return {}; }Then both paths can use:
// render_frame / render_frame_with_ui GOGGLES_TRY(record_render_core(cmd, image_index)); if (ui_callback) { ui_callback(cmd, *m_swapchain_image_views[image_index], m_swapchain_extent); } // final barrier and endsrc/ui/imgui_layer.hpp (1)
34-43: Remove unusedparameters_dirtyfield or add a TODO comment.The
parameters_dirtyfield inShaderControlStateis declared but never referenced anywhere in the codebase. If this is intended for future functionality, add a clarifying TODO comment; otherwise, remove it to reduce unnecessary state.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pixi.lockis excluded by!**/*.lock
📒 Files selected for processing (21)
cmake/Dependencies.cmakeopenspec/changes/add-imgui-runtime-preset-control/design.mdopenspec/changes/add-imgui-runtime-preset-control/proposal.mdopenspec/changes/add-imgui-runtime-preset-control/specs/app-window/spec.mdopenspec/changes/add-imgui-runtime-preset-control/specs/render-pipeline/spec.mdopenspec/changes/add-imgui-runtime-preset-control/tasks.mdpackages/imgui/pixi.tomlpackages/imgui/recipe.yamlpackages/wlroots_0_18/recipe.yamlpixi.tomlsrc/CMakeLists.txtsrc/app/CMakeLists.txtsrc/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hppsrc/render/backend/vulkan_error.hppsrc/render/chain/filter_chain.cppsrc/render/chain/filter_chain.hppsrc/ui/CMakeLists.txtsrc/ui/imgui_layer.cppsrc/ui/imgui_layer.hpp
🧰 Additional context used
📓 Path-based instructions (3)
openspec/**/*.md
📄 CodeRabbit inference engine (openspec/AGENTS.md)
openspec/**/*.md: Use#### Scenario:format (4 hashtags) for scenario headers in requirements, not bullets or bold text, with at least one scenario per requirement
Use format- **WHEN** [condition] - **THEN** [expected result]for scenario steps in requirements
Files:
openspec/changes/add-imgui-runtime-preset-control/design.mdopenspec/changes/add-imgui-runtime-preset-control/proposal.mdopenspec/changes/add-imgui-runtime-preset-control/specs/app-window/spec.mdopenspec/changes/add-imgui-runtime-preset-control/tasks.mdopenspec/changes/add-imgui-runtime-preset-control/specs/render-pipeline/spec.md
openspec/changes/**/proposal.md
📄 CodeRabbit inference engine (openspec/AGENTS.md)
List spec deltas in
proposal.mdunder 'What Changes' section, marking breaking changes with BREAKING
Files:
openspec/changes/add-imgui-runtime-preset-control/proposal.md
openspec/changes/**/specs/**/*.md
📄 CodeRabbit inference engine (openspec/AGENTS.md)
openspec/changes/**/specs/**/*.md: Write spec deltas using## ADDED|MODIFIED|REMOVED|RENAMED Requirementsformat with at least one#### Scenario:per requirement in spec files
When modifying existing requirements in a MODIFIED delta, paste the full requirement block including the header and all scenarios, as the archiver will replace the entire requirement
Use ADDED for new capabilities that can stand alone; use MODIFIED for changes to existing requirement behavior, scope, or acceptance criteria; use RENAMED for name-only changes
Files:
openspec/changes/add-imgui-runtime-preset-control/specs/app-window/spec.mdopenspec/changes/add-imgui-runtime-preset-control/specs/render-pipeline/spec.md
🧠 Learnings (25)
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
Applied to files:
src/ui/CMakeLists.txtopenspec/changes/add-imgui-runtime-preset-control/specs/app-window/spec.mdsrc/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_error.hppsrc/ui/imgui_layer.hppsrc/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : Use namespace `goggles` at top-level, `goggles::<module>` for modules (e.g., `goggles::capture`, `goggles::render`, `goggles::util`), `goggles::<module>::<submodule>` for sub-modules. Never use `using namespace` in headers. Prefer explicit namespace qualification or scoped `using` declarations.
Applied to files:
src/ui/CMakeLists.txtsrc/app/CMakeLists.txt
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : Define project-specific logging macros (`GOGGLES_LOG_TRACE`, `GOGGLES_LOG_DEBUG`, `GOGGLES_LOG_INFO`, `GOGGLES_LOG_WARN`, `GOGGLES_LOG_ERROR`, `GOGGLES_LOG_CRITICAL`) wrapping spdlog for consistent usage.
Applied to files:
src/ui/CMakeLists.txt
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Exception - Capture Layer Code (`src/capture/vk_layer/`) is exempt and MUST use the raw Vulkan C API because layer dispatch tables require C function pointers, layer must intercept C API calls from host applications, and minimal dependencies required.
Applied to files:
src/ui/CMakeLists.txtsrc/app/main.cppsrc/render/backend/vulkan_error.hppsrc/render/backend/vulkan_backend.hppsrc/ui/imgui_layer.hppsrc/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/CMakeLists.txtsrc/app/main.cppsrc/render/backend/vulkan_backend.hppsrc/ui/imgui_layer.hppsrc/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} : Application code MUST use vulkan-hpp with `VULKAN_HPP_NO_EXCEPTIONS` and dynamic dispatch
Applied to files:
src/ui/CMakeLists.txtsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_error.hppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Application code MUST use `vulkan-hpp` (C++ bindings), NOT the raw Vulkan C API. Use `vk::` types (e.g., `vk::Instance`, `vk::Device`, `vk::Image`). Never use raw C handles (`VkInstance`, `VkDevice`, etc.) in application code. Required configuration: `#define VULKAN_HPP_NO_EXCEPTIONS` and `#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1`.
Applied to files:
src/ui/CMakeLists.txtcmake/Dependencies.cmakesrc/app/main.cppsrc/render/backend/vulkan_error.hppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2025-12-19T08:54:18.412Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: openspec/AGENTS.md:0-0
Timestamp: 2025-12-19T08:54:18.412Z
Learning: Create a `design.md` file only when the change is cross-cutting (multiple services/modules), introduces new external dependencies, has security/performance/migration complexity, or has ambiguity requiring technical decisions
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/design.md
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/config/goggles.toml : Development/testing configuration: `config/goggles.toml` in repository root. Structure should include sections for [capture], [pipeline], [render], and [logging].
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/design.mdopenspec/changes/add-imgui-runtime-preset-control/specs/app-window/spec.mdsrc/app/CMakeLists.txt
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Use hybrid dependency management: CPM.cmake for simple and header-only dependencies, Conan for complex compiled dependencies, system packages for platform-specific libraries.
Applied to files:
cmake/Dependencies.cmake
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/tests/**/*.cpp : Only non-GPU logic is tested initially: in scope are utility functions, configuration parsing, error handling, pipeline graph logic. Out of scope: Vulkan initialization, GPU resource management, rendering, capture layer.
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/specs/app-window/spec.mdopenspec/changes/add-imgui-runtime-preset-control/tasks.mdsrc/app/main.cppsrc/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer runs in host application threads (no control). Hooks execute in calling thread (usually render thread). No blocking in hooks, especially `vkQueuePresentKHR`. Use atomics or locks for thread-safe shared layer state.
Applied to files:
src/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/ui/imgui_layer.hppsrc/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/app/main.cppsrc/render/backend/vulkan_backend.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/app/main.cppsrc/render/backend/vulkan_backend.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: Default to single-threaded render loop on main thread; use central job system (goggles::util::JobSystem) for any task parallelism
Applied to files:
src/app/main.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Due to async GPU execution, RAII handles (`vk::Unique*`) cannot automatically handle synchronization. Use selectively: `vk::Unique*` for Instance, Device, Surface (long-lived singletons), Swapchain, Pipelines, Layouts (created once). Use plain handles for Command buffers, per-frame sync primitives (Fence, Semaphore), Imported external images.
Applied to files:
src/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_error.hppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Initialize logger once at application startup on the main thread. Capture layer should initialize logger lazily on first `vkCreateInstance` hook.
Applied to files:
src/app/main.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : For Vulkan resources, follow RAII guidelines. Use `vk::Unique*` only for appropriate resource types (per D.2 table). Call `device.waitIdle()` or wait on appropriate fences before destroying resources with GPU-async lifetime. Store creation info with resources for debugging. Never leak Vulkan objects. Declare members in reverse destruction order (device before instance).
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_error.hppsrc/render/backend/vulkan_backend.hppsrc/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : All `vulkan-hpp` calls returning `vk::Result` must be explicitly checked. Prohibited: masking with `static_cast<void>()`. Use `VK_TRY` macro for early return with `vk::Result`, `GOGGLES_TRY` for Result<T> propagation (expression-style), `GOGGLES_MUST` for internal invariants (aborts on failure).
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_error.hpp
📚 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: The shader pipeline is a multi-pass model designed for compatibility with RetroArch shader semantics
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/specs/render-pipeline/spec.md
📚 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/app/CMakeLists.txt
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : All fallible operations must return `tl::expected<T, Error>` using the martinmoene/expected-lite library. No exceptions for expected failures (file I/O, resource creation, parsing). Exceptions allowed only for programming errors (assertions, contract violations).
Applied to files:
src/render/backend/vulkan_error.hpp
📚 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/**/*.{cpp,hpp} : All fallible operations MUST return `tl::expected<T, Error>` for error handling
Applied to files:
src/render/backend/vulkan_error.hpp
📚 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/**/*.{cpp,hpp} : Exceptions are only for unrecoverable programming errors; use `tl::expected` for recoverable errors
Applied to files:
src/render/backend/vulkan_error.hpp
🧬 Code graph analysis (3)
src/render/backend/vulkan_backend.cpp (7)
src/render/backend/vulkan_backend.hpp (10)
frame(36-36)frame(50-51)frame(87-87)ui_callback(52-52)image_index(95-95)cmd(91-92)cmd(93-94)preset_path(40-40)preset_path(41-42)enabled(47-47)src/util/error.hpp (1)
make_error(50-52)src/capture/vk_layer/wsi_virtual.cpp (2)
acquire_next_image(413-466)acquire_next_image(413-416)src/render/chain/filter_chain.hpp (5)
cmd(42-45)cmd(73-73)preset_path(40-40)enabled(53-53)enabled(53-53)src/render/chain/output_pass.hpp (1)
cmd(28-28)src/render/chain/pass.hpp (1)
cmd(44-44)src/render/chain/preset_parser.hpp (2)
preset_path(59-59)preset_path(62-64)
src/render/backend/vulkan_backend.hpp (5)
src/render/chain/filter_chain.hpp (5)
nodiscard(51-51)nodiscard(54-54)preset_path(40-40)enabled(53-53)enabled(53-53)src/capture/capture_receiver.hpp (7)
nodiscard(34-34)nodiscard(35-35)nodiscard(36-36)nodiscard(38-38)nodiscard(39-39)nodiscard(40-42)nodiscard(43-43)src/util/config.hpp (1)
nodiscard(19-31)src/render/chain/filter_pass.hpp (1)
nodiscard(87-89)src/render/chain/preset_parser.hpp (2)
preset_path(59-59)preset_path(62-64)
src/ui/imgui_layer.cpp (1)
src/util/error.hpp (2)
make_result_ptr_error(63-64)make_result_ptr(57-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build and Test (test preset)
- GitHub Check: Static Analysis (clang-tidy)
🔇 Additional comments (34)
packages/wlroots_0_18/recipe.yaml (1)
61-61: LGTM! Standard practice for conda binary packages.Adding patchelf as a build dependency is appropriate for conda/rattler-build packages that install pre-built shared libraries. The tool is typically used to fix RPATH/RUNPATH entries to ensure libraries can locate their dependencies within the conda environment.
pixi.toml (1)
143-143: LGTM! Consistent with existing local package pattern.The imgui local package reference follows the established pattern for other local packages in this file and properly integrates the new ImGui dependency for the shader controls UI.
openspec/changes/add-imgui-runtime-preset-control/tasks.md (1)
1-7: LGTM! Clear and well-structured task breakdown.The implementation tasks are clearly defined with appropriate scope. The completed items align with the code changes in this PR, and the pending tasks (parameter sliders and tests) are logical follow-ups.
src/render/chain/filter_chain.hpp (2)
99-99: LGTM! Appropriate default initialization.The bypass flag is correctly default-initialized to
false, ensuring the filter chain is active by default. The placement and initialization style follow modern C++ best practices.
53-54: Bypass API is properly implemented and used.The getter and setter follow conventions and are const-correct. Verification confirms
m_bypass_enabledis checked inFilterChainprocessing logic (line 303 of filter_chain.cpp) to skip filtering when bypass is enabled.packages/imgui/pixi.toml (1)
1-6: LGTM! Well-formed package manifest.The package manifest correctly defines the imgui package with version 1.91.8 (a stable Dear ImGui release from December 2024) and uses an appropriately pinned build backend.
src/app/CMakeLists.txt (1)
15-15: LGTM - UI library integration.The addition of
goggles_uito the link libraries correctly integrates the new ImGui-based UI module with the main application executable.src/CMakeLists.txt (1)
8-8: LGTM - UI module integration.The UI subdirectory addition follows the established pattern and correctly includes the new module in the build system.
src/render/chain/filter_chain.cpp (1)
446-449: LGTM - Defensive guard for uninitialized state.The early return correctly prevents resize calculations when no source frame has been rendered yet, avoiding potential issues with zero-dimension extents.
openspec/changes/add-imgui-runtime-preset-control/specs/render-pipeline/spec.md (1)
1-53: LGTM - Well-structured requirements specification.The spec correctly follows the required format with
## ADDED Requirements, proper#### Scenario:headers, and GIVEN-WHEN-THEN scenario steps. The requirements comprehensively cover runtime reload, passthrough mode, and parameter access with clear, testable acceptance criteria that align with the PR objectives.src/render/backend/vulkan_error.hpp (1)
9-16: [rewritten review comment]
[classification tag]openspec/changes/add-imgui-runtime-preset-control/proposal.md (1)
1-14: LGTM! Proposal structure follows guidelines.The proposal correctly uses the "What Changes" section format and appropriately omits BREAKING markers since all changes are additions without API breakage.
cmake/Dependencies.cmake (1)
65-71: LGTM! ImGui integration follows Pixi dependency pattern.The IMPORTED target correctly references the Pixi-built library and aligns with the project's hybrid dependency management approach. The INTERFACE linkage to SDL3 and Vulkan is appropriate for ImGui's backend requirements.
Based on learnings, using Pixi for this complex compiled dependency (with multiple backends) is the right choice rather than CPM.
openspec/changes/add-imgui-runtime-preset-control/design.md (1)
1-39: Well-structured design document.The design comprehensively covers integration approach, preset discovery, reload protocol, UI state management, risks, and migration plan. The structure appropriately justifies the cross-cutting nature of this change.
src/ui/CMakeLists.txt (2)
1-22: Library target structure is well-organized.The
goggles_uitarget correctly exposes its public API, links necessary dependencies (imgui, Vulkan, SDL3), and enables appropriate build tooling (clang-tidy, sanitizers).
17-19: Add VULKAN_HPP_DISPATCH_LOADER_DYNAMIC to goggles_ui target.The target currently defines
VULKAN_HPP_NO_EXCEPTIONSbut is missing the requiredVULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1definition. The code uses vulkan-hpp extensively (vk::Device, vk::DescriptorPool, etc.) and both definitions are required for proper dynamic dispatch configuration:target_compile_definitions(goggles_ui PRIVATE VULKAN_HPP_NO_EXCEPTIONS VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 )⛔ Skipped due to learnings
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} : Application code MUST use vulkan-hpp with `VULKAN_HPP_NO_EXCEPTIONS` and dynamic dispatchLearnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/src/**/*.{cpp,hpp} : Application code MUST use `vulkan-hpp` (C++ bindings), NOT the raw Vulkan C API. Use `vk::` types (e.g., `vk::Instance`, `vk::Device`, `vk::Image`). Never use raw C handles (`VkInstance`, `VkDevice`, etc.) in application code. Required configuration: `#define VULKAN_HPP_NO_EXCEPTIONS` and `#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1`.Learnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Exception - Capture Layer Code (`src/capture/vk_layer/`) is exempt and MUST use the raw Vulkan C API because layer dispatch tables require C function pointers, layer must intercept C API calls from host applications, and minimal dependencies required.Learnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/**/*.{cpp,hpp} : All `vulkan-hpp` calls returning `vk::Result` must be explicitly checked. Prohibited: masking with `static_cast<void>()`. Use `VK_TRY` macro for early return with `vk::Result`, `GOGGLES_TRY` for Result<T> propagation (expression-style), `GOGGLES_MUST` for internal invariants (aborts on failure).Learnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.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 resourcesLearnt 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-hppLearnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/src/**/*.{cpp,hpp} : For Vulkan resources, follow RAII guidelines. Use `vk::Unique*` only for appropriate resource types (per D.2 table). Call `device.waitIdle()` or wait on appropriate fences before destroying resources with GPU-async lifetime. Store creation info with resources for debugging. Never leak Vulkan objects. Declare members in reverse destruction order (device before instance).Learnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/src/**/*.{cpp,hpp} : Due to async GPU execution, RAII handles (`vk::Unique*`) cannot automatically handle synchronization. Use selectively: `vk::Unique*` for Instance, Device, Surface (long-lived singletons), Swapchain, Pipelines, Layouts (created once). Use plain handles for Command buffers, per-frame sync primitives (Fence, Semaphore), Imported external images.Learnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/**/*.{cpp,hpp} : Define project-specific logging macros (`GOGGLES_LOG_TRACE`, `GOGGLES_LOG_DEBUG`, `GOGGLES_LOG_INFO`, `GOGGLES_LOG_WARN`, `GOGGLES_LOG_ERROR`, `GOGGLES_LOG_CRITICAL`) wrapping spdlog for consistent usage.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 headersLearnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/**/*.{cpp,hpp} : Use namespace `goggles` at top-level, `goggles::<module>` for modules (e.g., `goggles::capture`, `goggles::render`, `goggles::util`), `goggles::<module>::<submodule>` for sub-modules. Never use `using namespace` in headers. Prefer explicit namespace qualification or scoped `using` declarations.Learnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/tests/**/*.cpp : Only non-GPU logic is tested initially: in scope are utility functions, configuration parsing, error handling, pipeline graph logic. Out of scope: Vulkan initialization, GPU resource management, rendering, capture layer.src/app/main.cpp (5)
35-64: LGTM!The function correctly handles shader state synchronization with the UI. Error handling for preset reload is appropriate, and the early return pattern keeps the logic clean.
79-133: LGTM!The event handling correctly forwards SDL events to ImGui first, then conditionally forwards to the input forwarder based on
wants_capture_keyboard()andwants_capture_mouse(). The F1 toggle logic is clean and the pattern is consistent across all input types.
173-206: LGTM!The UI integration into the render loop is well-structured:
- State handling before
begin_frame()ensures UI state is applied before rendering- The callback lambda correctly captures
end_frame()andrecord()calls- Conditional render paths (
render_frame_with_uivsrender_frame) avoid overhead when UI is not active
299-320: LGTM!Good defensive programming with graceful degradation - the application continues to function if ImGui initialization fails. The warning log provides visibility, and the conditional UI usage throughout the codebase handles the nullptr case.
354-358: LGTM!The shutdown sequence correctly resets the ImGui layer before the Vulkan backend, ensuring proper resource cleanup order.
openspec/changes/add-imgui-runtime-preset-control/specs/app-window/spec.md (1)
1-53: LGTM!The spec document follows the required format:
- Uses
## ADDED Requirementsformat as specified- Each requirement has proper
#### Scenario:headers- Scenario steps use
**GIVEN**,**WHEN**,**THEN**format- Multiple scenarios per requirement cover key use cases
As per coding guidelines for
openspec/**/*.md.src/render/backend/vulkan_backend.cpp (2)
1240-1305: Same duplication pattern asrender_frame_with_ui.This shares the duplication pattern flagged above. The refactoring suggestion would also apply here.
1343-1347: LGTM!Simple and correct implementation. The inversion of
enabledto!enabledfor bypass is the expected behavior.src/render/backend/vulkan_backend.hpp (2)
41-52: LGTM!The new API surface is well-designed:
reload_shader_presetreturnsResult<void>for error handlingcurrent_preset_pathreturns const reference to avoid copiesUiRenderCallbacktype alias clearly documents the expected signature- UI-enabled render methods follow the existing
render_frame/render_clearpattern
54-63: LGTM!These accessors are necessary for initializing the ImGui Vulkan backend. They follow the existing patterns in the codebase and are appropriately marked
[[nodiscard]]andconst.src/ui/imgui_layer.cpp (5)
25-100: LGTM!The creation factory has proper error handling:
- Each failure path cleans up previously created resources
- Descriptor pool with generous sizes supports ImGui's internal allocations
- Dynamic rendering setup is correctly configured for Vulkan 1.3
Based on learnings, using raw
vk::Deviceinstead ofvk::UniqueDeviceis appropriate here since the device lifetime is managed externally.
106-119: LGTM!Proper shutdown sequence with
waitIdle()before destroying ImGui resources. The null check onm_deviceprovides idempotency for double-shutdown protection.
163-180: LGTM!The tree-building logic correctly handles:
- Path decomposition via iterator
- Distinguishing directories (intermediate nodes) from files (leaf nodes with
preset_index)- Automatic creation of parent nodes
259-305: LGTM!Well-structured UI with:
- Sensible defaults for window position/size
- Clear separation of concerns (enable toggle, preset browser, parameters)
- Search filter that switches display mode
- Consistent button behavior
307-328: LGTM!Parameter controls correctly:
- Compare old/new values to avoid redundant callbacks
- Respect min/max bounds via
SliderFloat- Show helpful tooltips for parameter descriptions
- Support reset to defaults via callback
src/ui/imgui_layer.hpp (3)
13-16: LGTM!Simple and effective tree structure using
std::mapfor ordered children traversal. The-1sentinel forpreset_indexclearly distinguishes directories from preset files.
18-26: LGTM!
ImGuiConfigcleanly encapsulates all Vulkan handles needed for ImGui initialization, matching the requirements ofImGui_ImplVulkan_InitInfo. Usingvk::types aligns with project policies.
49-101: LGTM!The
ImGuiLayerclass follows project patterns:
- Factory method returns
ResultPtrfor error handling- Deleted copy/move constructors prevent resource management issues
- Private default constructor enforces factory usage
- Clear separation between public API and private implementation details
| - No generalized file picker; we only need preset discovery from known directories for now. | ||
|
|
||
| ## Decisions | ||
| 1. **Dear ImGui dependency scope** – Use the latest docking tag (`v1.91+ docking`) vendored or fetched via CPM, but link it solely with the Goggles app target. The capture layer build remains unchanged and never links ImGui symbols. |
There was a problem hiding this comment.
Update dependency acquisition method to match implementation.
The design mentions "vendored or fetched via CPM," but the actual implementation uses a Pixi local package (packages/imgui/recipe.yaml). Update this line to reflect the Pixi-based approach.
🔎 Suggested correction
-1. **Dear ImGui dependency scope** – Use the latest docking tag (`v1.91+ docking`) vendored or fetched via CPM, but link it solely with the Goggles app target. The capture layer build remains unchanged and never links ImGui symbols.
+1. **Dear ImGui dependency scope** – Use the latest docking tag (`v1.91.8-docking`) built via Pixi local package, but link it solely with the Goggles app target. The capture layer build remains unchanged and never links ImGui symbols.📝 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.
| 1. **Dear ImGui dependency scope** – Use the latest docking tag (`v1.91+ docking`) vendored or fetched via CPM, but link it solely with the Goggles app target. The capture layer build remains unchanged and never links ImGui symbols. | |
| 1. **Dear ImGui dependency scope** – Use the latest docking tag (`v1.91.8-docking`) built via Pixi local package, but link it solely with the Goggles app target. The capture layer build remains unchanged and never links ImGui symbols. |
🤖 Prompt for AI Agents
In @openspec/changes/add-imgui-runtime-preset-control/design.md at line 15,
Update the "Dear ImGui dependency scope" sentence to reflect the actual
acquisition method used in the implementation: replace "vendored or fetched via
CPM" with the Pixi-local package approach (referencing
packages/imgui/recipe.yaml) and clarify that the ImGui docking tag (v1.91+
docking) is linked only to the Goggles app target while the capture layer
remains unchanged and does not link ImGui symbols.
| - | | ||
| # Build static library | ||
| $CXX $CXXFLAGS -c -fPIC -I. \ | ||
| imgui.cpp imgui_demo.cpp imgui_draw.cpp imgui_tables.cpp imgui_widgets.cpp \ | ||
| backends/imgui_impl_sdl3.cpp backends/imgui_impl_vulkan.cpp \ | ||
| -I$PREFIX/include -I$PREFIX/include/SDL3 | ||
|
|
||
| ar rcs libimgui.a *.o | ||
| mv libimgui.a $PREFIX/lib/ |
There was a problem hiding this comment.
Add error handling to the build script.
The build script doesn't verify compilation success before archiving. If any source file fails to compile, the script will continue and create an incomplete libimgui.a, leading to cryptic linker errors downstream.
🔎 Proposed fix to add error checking
- |
# Build static library
$CXX $CXXFLAGS -c -fPIC -I. \
imgui.cpp imgui_demo.cpp imgui_draw.cpp imgui_tables.cpp imgui_widgets.cpp \
backends/imgui_impl_sdl3.cpp backends/imgui_impl_vulkan.cpp \
- -I$PREFIX/include -I$PREFIX/include/SDL3
+ -I$PREFIX/include -I$PREFIX/include/SDL3 || exit 1
- ar rcs libimgui.a *.o
+ ar rcs libimgui.a *.o || exit 1
mv libimgui.a $PREFIX/lib/📝 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.
| - | | |
| # Build static library | |
| $CXX $CXXFLAGS -c -fPIC -I. \ | |
| imgui.cpp imgui_demo.cpp imgui_draw.cpp imgui_tables.cpp imgui_widgets.cpp \ | |
| backends/imgui_impl_sdl3.cpp backends/imgui_impl_vulkan.cpp \ | |
| -I$PREFIX/include -I$PREFIX/include/SDL3 | |
| ar rcs libimgui.a *.o | |
| mv libimgui.a $PREFIX/lib/ | |
| - | | |
| # Build static library | |
| $CXX $CXXFLAGS -c -fPIC -I. \ | |
| imgui.cpp imgui_demo.cpp imgui_draw.cpp imgui_tables.cpp imgui_widgets.cpp \ | |
| backends/imgui_impl_sdl3.cpp backends/imgui_impl_vulkan.cpp \ | |
| -I$PREFIX/include -I$PREFIX/include/SDL3 || exit 1 | |
| ar rcs libimgui.a *.o || exit 1 | |
| mv libimgui.a $PREFIX/lib/ |
🤖 Prompt for AI Agents
In @packages/imgui/recipe.yaml around lines 14 - 22, The build snippet runs the
$CXX compile step then unconditionally archives with ar, which can produce an
incomplete libimgui.a if compilation fails; ensure the script checks compilation
success by either enabling strict failure (set -euo pipefail) at the top of the
shell block or chaining the commands so ar rcs and mv only run on success (e.g.,
run the $CXX ... invocation and then && ar rcs libimgui.a *.o && mv libimgui.a
$PREFIX/lib/), or wrap the compile in an if-check that exits with non-zero on
failure.
- Use std::error_code for filesystem operations - Make bypass flag atomic for thread safety - Search filter matches filename only - Add format change detection flag
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
src/render/backend/vulkan_backend.cpp (2)
1159-1239: Consider extracting shared command recording logic.
render_frame_with_uiduplicates ~80 lines fromrender_frameandrecord_render_commands, differing only by the UI callback invocation (lines 1224-1226). While the duplication is localized and functional, extracting the shared barrier setup and command recording into a helper would improve maintainability.🔎 Potential refactor approach
Extract a shared
record_frame_commandshelper that accepts an optional UI callback parameter:auto VulkanBackend::record_frame_commands(vk::CommandBuffer cmd, uint32_t image_index, const std::optional<UiRenderCallback>& ui_callback) -> Result<void> { // Common barrier setup and filter chain recording // ... m_filter_chain->record(cmd, ...); if (ui_callback) { (*ui_callback)(cmd, *m_swapchain_image_views[image_index], m_swapchain_extent); } // Final barrier and end // ... }Then both
render_frameandrender_frame_with_uican call this helper.
1241-1306: Consider extracting shared clear command recording logic.Similar to
render_frame_with_ui, this method duplicatesrender_clearandrecord_clear_commands(~65 lines), differing only by the UI callback invocation (lines 1291-1293). Extracting shared logic would reduce duplication.🔎 Potential refactor approach
Extract a shared
record_clear_commands_with_callbackhelper:auto VulkanBackend::record_clear_commands_internal(vk::CommandBuffer cmd, uint32_t image_index, const std::optional<UiRenderCallback>& ui_callback) -> Result<void> { // Barrier setup, clear rendering // ... cmd.beginRendering(rendering_info); cmd.endRendering(); if (ui_callback) { (*ui_callback)(cmd, *m_swapchain_image_views[image_index], m_swapchain_extent); } // Final barrier and end // ... }src/ui/imgui_layer.cpp (3)
14-21: Consider usingstd::transformfor more idiomatic C++.The current implementation is correct and properly avoids undefined behavior with the
unsigned charcast. However, a more idiomatic approach would usestd::transform:auto to_lower(std::string_view str) -> std::string { std::string result; result.resize(str.size()); std::transform(str.begin(), str.end(), result.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); }); return result; }
43-61: Consider making descriptor pool sizes configurable.The descriptor pool sizes are hardcoded to 1000 for each descriptor type. This could be wasteful for simple UIs or insufficient for complex ones. Consider making these configurable via
ImGuiConfigor calculating them based on expected UI complexity.
182-190: Linear search may impact performance for large preset catalogs.The function performs a linear O(n) search through the preset catalog. For catalogs with hundreds of presets, this could introduce noticeable lag during preset selection.
Consider maintaining a
std::unordered_map<std::filesystem::path, int>index alongside the catalog:// In header std::unordered_map<std::filesystem::path, int> m_preset_index_map; // In set_preset_catalog void ImGuiLayer::set_preset_catalog(std::vector<std::filesystem::path> presets) { m_state.preset_catalog = std::move(presets); m_preset_index_map.clear(); for (size_t i = 0; i < m_state.preset_catalog.size(); ++i) { m_preset_index_map[m_state.preset_catalog[i]] = static_cast<int>(i); } rebuild_preset_tree(); } // In set_current_preset void ImGuiLayer::set_current_preset(const std::filesystem::path& path) { m_state.current_preset = path; if (auto it = m_preset_index_map.find(path); it != m_preset_index_map.end()) { m_state.selected_preset_index = it->second; } }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
.gitignoresrc/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hppsrc/render/chain/filter_chain.cppsrc/render/chain/filter_chain.hppsrc/ui/imgui_layer.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
- src/render/chain/filter_chain.cpp
🧰 Additional context used
🧠 Learnings (15)
📚 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/render/backend/vulkan_backend.cppsrc/app/main.cppsrc/render/backend/vulkan_backend.hpp
📚 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/render/backend/vulkan_backend.cppsrc/app/main.cppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Due to async GPU execution, RAII handles (`vk::Unique*`) cannot automatically handle synchronization. Use selectively: `vk::Unique*` for Instance, Device, Surface (long-lived singletons), Swapchain, Pipelines, Layouts (created once). Use plain handles for Command buffers, per-frame sync primitives (Fence, Semaphore), Imported external images.
Applied to files:
src/render/backend/vulkan_backend.cppsrc/app/main.cppsrc/render/backend/vulkan_backend.hpp
📚 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} : Application code MUST use vulkan-hpp with `VULKAN_HPP_NO_EXCEPTIONS` and dynamic dispatch
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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/render/backend/vulkan_backend.cppsrc/app/main.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer runs in host application threads (no control). Hooks execute in calling thread (usually render thread). No blocking in hooks, especially `vkQueuePresentKHR`. Use atomics or locks for thread-safe shared layer state.
Applied to files:
src/render/backend/vulkan_backend.cppsrc/app/main.cppsrc/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/tests/**/*.cpp : Only non-GPU logic is tested initially: in scope are utility functions, configuration parsing, error handling, pipeline graph logic. Out of scope: Vulkan initialization, GPU resource management, rendering, capture layer.
Applied to files:
src/render/backend/vulkan_backend.cppsrc/app/main.cppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
Applied to files:
src/render/backend/vulkan_backend.cppsrc/app/main.cppsrc/render/backend/vulkan_backend.hppsrc/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : All `vulkan-hpp` calls returning `vk::Result` must be explicitly checked. Prohibited: masking with `static_cast<void>()`. Use `VK_TRY` macro for early return with `vk::Result`, `GOGGLES_TRY` for Result<T> propagation (expression-style), `GOGGLES_MUST` for internal invariants (aborts on failure).
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : For Vulkan resources, follow RAII guidelines. Use `vk::Unique*` only for appropriate resource types (per D.2 table). Call `device.waitIdle()` or wait on appropriate fences before destroying resources with GPU-async lifetime. Store creation info with resources for debugging. Never leak Vulkan objects. Declare members in reverse destruction order (device before instance).
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hppsrc/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: Default to single-threaded render loop on main thread; use central job system (goggles::util::JobSystem) for any task parallelism
Applied to files:
src/app/main.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/app/main.cppsrc/render/backend/vulkan_backend.hppsrc/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Exception - Capture Layer Code (`src/capture/vk_layer/`) is exempt and MUST use the raw Vulkan C API because layer dispatch tables require C function pointers, layer must intercept C API calls from host applications, and minimal dependencies required.
Applied to files:
src/app/main.cppsrc/render/backend/vulkan_backend.hppsrc/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Initialize logger once at application startup on the main thread. Capture layer should initialize logger lazily on first `vkCreateInstance` hook.
Applied to files:
src/app/main.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Application code MUST use `vulkan-hpp` (C++ bindings), NOT the raw Vulkan C API. Use `vk::` types (e.g., `vk::Instance`, `vk::Device`, `vk::Image`). Never use raw C handles (`VkInstance`, `VkDevice`, etc.) in application code. Required configuration: `#define VULKAN_HPP_NO_EXCEPTIONS` and `#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1`.
Applied to files:
src/render/backend/vulkan_backend.hpp
🧬 Code graph analysis (2)
src/render/backend/vulkan_backend.cpp (7)
src/render/backend/vulkan_backend.hpp (10)
frame(36-36)frame(50-51)frame(93-93)ui_callback(52-52)image_index(101-101)cmd(97-98)cmd(99-100)preset_path(40-40)preset_path(41-42)enabled(47-47)src/util/error.hpp (1)
make_error(50-52)src/render/chain/filter_chain.hpp (5)
cmd(43-46)cmd(76-76)preset_path(41-41)enabled(54-54)enabled(54-54)src/ui/imgui_layer.hpp (1)
cmd(66-66)src/render/chain/output_pass.hpp (1)
cmd(28-28)src/render/chain/pass.hpp (1)
cmd(44-44)src/render/chain/preset_parser.hpp (2)
preset_path(59-59)preset_path(62-64)
src/ui/imgui_layer.cpp (1)
src/util/error.hpp (2)
make_result_ptr_error(63-64)make_result_ptr(57-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build and Test (test preset)
- GitHub Check: Static Analysis (clang-tidy)
🔇 Additional comments (20)
.gitignore (1)
122-122: LGTM: Standard ImGui state file exclusion.The
imgui.inientry correctly excludes ImGui's per-user window layout state from version control.src/render/chain/filter_chain.hpp (1)
9-9: LGTM: Atomic bypass flag with appropriate memory ordering.The atomic bypass flag uses
memory_order_relaxed, which is appropriate given the project's single-threaded render loop (main thread handles both UI state updates and rendering). The atomic type provides thread-safe access without unnecessary synchronization overhead.Based on learnings, the default threading model uses a single-threaded render loop on the main thread.
Also applies to: 54-57, 102-102
src/app/main.cpp (5)
22-36: LGTM: Robust filesystem traversal with proper error handling.The function correctly uses
std::error_codefor exception-free filesystem operations and gracefully returns an empty vector on failure. Silent failure is appropriate here since the preset catalog is optional functionality.
38-67: LGTM: Clean UI state management with proper bounds checking.The function correctly:
- Tracks shader enablement changes with a function-local static
- Uses
std::cmp_greater_equalfor safe signed/unsigned comparison (line 54)- Handles preset reload errors gracefully with logging
- Updates UI state consistently after operations
82-110: LGTM: Proper ImGui event handling and input capture logic.The implementation correctly:
- Forwards events to ImGui first (lines 82-84)
- Implements F1 toggle for UI visibility (lines 92-93)
- Gates input forwarding based on ImGui's capture state (lines 94-101, 103-105)
- Prevents input leakage to the application when UI is focused
176-209: LGTM: Clean UI integration into render loop.The UI callback pattern (lines 182-187) cleanly separates UI rendering concerns from the main loop. The conditional rendering paths (lines 191-209) correctly select between
render_frame_with_ui/render_clear_with_uiand their non-UI counterparts based on ImGui availability.
302-323: LGTM: Robust ImGui initialization with graceful fallback.The initialization correctly:
- Constructs
ImGuiConfigfrom Vulkan backend state (lines 302-310)- Handles creation failure gracefully without crashing (lines 314-316)
- Synchronizes preset catalog and current state with the backend (lines 318-321)
- Treats ImGui as optional functionality with appropriate logging
src/render/backend/vulkan_backend.cpp (3)
503-503: LGTM: Format change tracking flag.The flag correctly signals that a swapchain format change has occurred, consumed later via the
consume_format_change()accessor.
1308-1346: LGTM: Rollback logic correctly handles failure states.The preset reload logic properly:
- Waits for device idle before chain shutdown (lines 1315-1316)
- Attempts rollback to previous preset on failure (lines 1328-1333)
- Clears
m_preset_pathwhen rollback fails (line 1337), maintaining consistency with the passthrough filter chain- Logs appropriate warnings at each failure stage (lines 1326, 1334)
The past review comment concern about inconsistent
m_preset_pathstate is now resolved by line 1337.
1348-1352: LGTM: Correct shader enablement logic.The method correctly inverts the
enabledflag to set bypass state (enabled=true→bypass=false), allowing shaders to be toggled via the UI.src/render/backend/vulkan_backend.hpp (2)
41-74: LGTM: Clean public API extensions for UI integration.The additions provide a cohesive interface for runtime UI functionality:
- Preset management:
reload_shader_preset,current_preset_path(lines 41-45)- Shader control:
set_shader_enabled(line 47)- UI rendering:
UiRenderCallback,render_frame_with_ui,render_clear_with_ui(lines 49-52)- ImGui configuration: Vulkan object accessors (lines 54-63)
- Format change tracking:
consume_format_change(lines 70-74)All accessors appropriately use
[[nodiscard]]and const-correctness.
157-157: LGTM: Format change tracking flag.The
m_format_changedflag backs theconsume_format_change()public accessor, supporting the consume-once pattern for format change notifications.src/ui/imgui_layer.cpp (8)
25-41: Validate config parameters before use.The
create()method does not validate the providedconfigparameters before using them. Invalid handles (e.g., null device, invalid queue family) could lead to crashes or undefined behavior during ImGui initialization.Consider adding validation checks:
auto ImGuiLayer::create(SDL_Window* window, const ImGuiConfig& config) -> ResultPtr<ImGuiLayer> { + if (!window) { + return make_result_ptr_error<ImGuiLayer>(ErrorCode::invalid_argument, "Window is null"); + } + if (!config.device || !config.physical_device || !config.instance) { + return make_result_ptr_error<ImGuiLayer>(ErrorCode::invalid_argument, "Invalid Vulkan config"); + } + auto layer = std::unique_ptr<ImGuiLayer>(new ImGuiLayer());
102-119: LGTM! Proper Vulkan resource cleanup.The shutdown sequence correctly calls
device.waitIdle()before destroying Vulkan resources, follows the proper cleanup order (ImGui Vulkan → SDL3 → descriptor pool → context), and sets handles to nullptr. This aligns with RAII guidelines for Vulkan resources.Based on learnings, per Vulkan RAII guidelines: wait for device idle before destroying resources with GPU-async lifetime.
139-156: LGTM! Correct dynamic rendering setup for UI overlay.The command buffer recording correctly uses
AttachmentLoadOp::eLoadto preserve existing frame content, which is appropriate for rendering the UI overlay on top of the scene. The dynamic rendering setup matches the initialization increate().
221-257: LGTM! Proper ImGui tree and list rendering.The preset rendering correctly uses
PushID/PopIDfor unique widget IDs, shows tooltips with full paths for usability, and properly distinguishes between leaf nodes (presets) and folders usingpreset_index >= 0. The recursive tree traversal is well-structured.
307-328: LGTM! Efficient parameter control implementation.The parameter controls correctly:
- Invoke callbacks only when values actually change (line 319 comparison)
- Show tooltips from parameter descriptions for better UX
- Provide a reset-to-defaults button
212-219: No action required. Thesearch_filteris correctly declared asstd::array<char, 256>in the state structure, making the.data()call safe and idiomatic. The array is zero-initialized and properly sized for use withImGui::InputTextWithHint, ensuring null-termination is maintained.
163-180: Edge cases are handled correctly bystd::filesystem::pathiteration.Paths come from
std::filesystem::recursive_directory_iterator, which returns validstd::filesystem::pathobjects that are automatically normalized. The iteration logic usingpath.begin()/path.end()is robust for all valid path types—empty components are skipped, absolute/relative paths are handled identically, and single-component paths work correctly. No changes needed.
281-281: No action required. The code at line 281 correctly usesImGuiChildFlags_Borders, which is supported in ImGui v1.90.0 and later. The project's ImGui dependency is managed via Conda, ensuring a compatible version is in use.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/ui/imgui_layer.cpp (2)
65-72: Consider using modern vulkan-hpp result handling.The structured binding
auto [pool_result, pool] = config.device.createDescriptorPool(...)works but is legacy style. Modern vulkan-hpp typically uses:auto result = config.device.createDescriptorPool(pool_info); if (result.result != vk::Result::eSuccess) { // error handling } layer->m_descriptor_pool = result.value;Or if the project has a
VK_TRYmacro (as suggested by the mention ofVK_TRYin vulkan_error.hpp changes), use that pattern instead.🔎 Proposed refactor using result/value pattern
- auto [pool_result, pool] = config.device.createDescriptorPool(pool_info); - if (pool_result != vk::Result::eSuccess) { + auto pool_result = config.device.createDescriptorPool(pool_info); + if (pool_result.result != vk::Result::eSuccess) { ImGui_ImplSDL3_Shutdown(); ImGui::DestroyContext(); return make_result_ptr_error<ImGuiLayer>(ErrorCode::vulkan_init_failed, "Failed to create ImGui descriptor pool"); } - layer->m_descriptor_pool = pool; + layer->m_descriptor_pool = pool_result.value;
261-307: UI structure is well-organized.The Shader Controls window layout is logical and the interaction flow is clear. Note that
shader_enabledchanges (line 266) are handled via direct state mutation while parameter changes use callbacks. This is acceptable if the application pollsshader_enabled, but consider adding a callback for consistency if you add other toggles in the future.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/ui/imgui_layer.cpp
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
Applied to files:
src/ui/imgui_layer.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Exception - Capture Layer Code (`src/capture/vk_layer/`) is exempt and MUST use the raw Vulkan C API because layer dispatch tables require C function pointers, layer must intercept C API calls from host applications, and minimal dependencies required.
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-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer runs in host application threads (no control). Hooks execute in calling thread (usually render thread). No blocking in hooks, especially `vkQueuePresentKHR`. Use atomics or locks for thread-safe shared layer state.
Applied to files:
src/ui/imgui_layer.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Analysis (clang-tidy)
- GitHub Check: Build and Test (test preset)
🔇 Additional comments (7)
src/ui/imgui_layer.cpp (7)
108-121: Cleanup order and error handling look good.The shutdown sequence properly waits for the device to idle before destroying ImGui resources, and the cleanup order is correct. Note that
waitIdle()errors are intentionally discarded during shutdown, which is acceptable since recovery options are limited in a destructor/shutdown context.
141-158: LGTM: Correct dynamic rendering setup for UI overlay.The rendering configuration properly uses
LoadOp::eLoadto render the UI on top of existing content without clearing, which is correct for an overlay.
165-182: Tree building logic is correct.The preset tree construction properly iterates through path components and creates the hierarchical structure, setting
preset_indexonly for leaf nodes (actual presets).
223-239: Well-implemented filtered preset list.Good use of
std::cmp_equalfor safe signed/unsigned comparison and proper ImGui ID scoping. The tooltip showing the full path is a nice UX touch.
241-259: Recursive tree rendering is correctly implemented.The distinction between leaf nodes (presets) and directory nodes is handled properly, and TreeNode/TreePop pairing is correct.
309-330: Parameter controls implemented correctly.The defensive check for actual value change (line 321) before invoking the callback is good practice, even though
SliderFloat's return value typically indicates user interaction. The tooltip display for parameter descriptions is a nice UX touch.
214-221: No issues found. Thesearch_filteris declared asstd::array<char, 256>and is zero-initialized, providing ample space for ImGui::InputTextWithHint to write strings with null-termination. ImGui's InputTextWithHint ensures null-termination of the buffer, making the use ofsearch_filter.data()into_lower()safe.
src/app/main.cpp
Outdated
| if (!result) { | ||
| GOGGLES_LOG_ERROR("Failed to load preset '{}': {}", preset.string(), | ||
| result.error().message); | ||
| } else { | ||
| state.current_preset = preset; |
There was a problem hiding this comment.
Suggestion: Synchronize UI state after reload
| if (!result) { | |
| GOGGLES_LOG_ERROR("Failed to load preset '{}': {}", preset.string(), | |
| result.error().message); | |
| } else { | |
| state.current_preset = preset; | |
| } else { | |
| GOGGLES_LOG_INFO("Loaded preset: {}", vulkan_backend.current_preset_path().string()); | |
| } | |
| // Always sync UI state with backend state after reload attempt | |
| imgui_layer.set_current_preset(vulkan_backend.current_preset_path()); | |
| } |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI Agents
In @src/render/backend/vulkan_backend.cpp:
- Around line 1348-1388: The async shader-load lambda captures this and writes
to members (m_pending_shader_runtime, m_pending_filter_chain,
m_pending_chain_ready), risking use-after-free because shutdown() only calls
m_device->waitIdle() and doesn't wait for m_pending_load_future; fix by making
shutdown() (and the VulkanBackend destructor as a safeguard) wait for the
pending future to finish (check m_pending_load_future.valid() then call wait()
or get()) before allowing member destruction, ensuring the async task cannot
touch freed members.
🧹 Nitpick comments (1)
src/render/backend/vulkan_backend.cpp (1)
1163-1247: Significant code duplication withrender_frame()andrecord_render_commands().This function duplicates the frame lifecycle logic from
render_frame()(lines 1171-1181) and the barrier setup fromrecord_render_commands()(lines 1194-1226). The only difference is the UI callback insertion point.Consider extracting common logic into a private helper or template to reduce maintenance burden. For example, a
record_render_commands_implthat accepts an optional callback could be used by both paths.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
🧰 Additional context used
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
📚 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/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Due to async GPU execution, RAII handles (`vk::Unique*`) cannot automatically handle synchronization. Use selectively: `vk::Unique*` for Instance, Device, Surface (long-lived singletons), Swapchain, Pipelines, Layouts (created once). Use plain handles for Command buffers, per-frame sync primitives (Fence, Semaphore), Imported external images.
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/tests/**/*.cpp : Only non-GPU logic is tested initially: in scope are utility functions, configuration parsing, error handling, pipeline graph logic. Out of scope: Vulkan initialization, GPU resource management, rendering, capture layer.
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : For Vulkan resources, follow RAII guidelines. Use `vk::Unique*` only for appropriate resource types (per D.2 table). Call `device.waitIdle()` or wait on appropriate fences before destroying resources with GPU-async lifetime. Store creation info with resources for debugging. Never leak Vulkan objects. Declare members in reverse destruction order (device before instance).
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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} : Application code MUST use vulkan-hpp with `VULKAN_HPP_NO_EXCEPTIONS` and dynamic dispatch
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer runs in host application threads (no control). Hooks execute in calling thread (usually render thread). No blocking in hooks, especially `vkQueuePresentKHR`. Use atomics or locks for thread-safe shared layer state.
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : Per-frame code paths (main thread render loop) MUST NOT perform dynamic memory allocations (`new`, `malloc`, `std::make_shared`), use blocking synchronization primitives (`std::mutex`, `std::condition_variable`), or exceed 8ms CPU time budget (excluding GPU sync).
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Application code MUST use `vulkan-hpp` (C++ bindings), NOT the raw Vulkan C API. Use `vk::` types (e.g., `vk::Instance`, `vk::Device`, `vk::Image`). Never use raw C handles (`VkInstance`, `VkDevice`, etc.) in application code. Required configuration: `#define VULKAN_HPP_NO_EXCEPTIONS` and `#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1`.
Applied to files:
src/render/backend/vulkan_backend.hpp
📚 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/app/**/*.{cpp,hpp} : Do not perform dynamic memory allocations or use blocking synchronization primitives in per-frame main render loop code paths
Applied to files:
src/render/backend/vulkan_backend.hpp
📚 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: Default to single-threaded render loop on main thread; use central job system (goggles::util::JobSystem) for any task parallelism
Applied to files:
src/render/backend/vulkan_backend.hpp
🧬 Code graph analysis (1)
src/render/backend/vulkan_backend.hpp (4)
src/render/chain/filter_chain.hpp (5)
nodiscard(52-52)nodiscard(55-57)preset_path(41-41)enabled(54-54)enabled(54-54)src/capture/capture_receiver.hpp (7)
nodiscard(34-34)nodiscard(35-35)nodiscard(36-36)nodiscard(38-38)nodiscard(39-39)nodiscard(40-42)nodiscard(43-43)src/util/config.hpp (1)
nodiscard(19-31)src/render/chain/preset_parser.hpp (2)
preset_path(59-59)preset_path(62-64)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build and Test (test preset)
- GitHub Check: Static Analysis (clang-tidy)
🔇 Additional comments (9)
src/render/backend/vulkan_backend.cpp (5)
1249-1318: LGTM with the same refactor consideration.The UI callback integration is correctly placed after the clear rendering and before the present barrier. The implementation is functionally correct. The duplication with
render_clear()is similar to the pattern noted above.
1393-1425: LGTM—error handling maintains consistent state.The error path correctly resets the pending chain state without modifying
m_preset_path, ensuring the active preset path remains consistent with the running filter chain. The release-acquire ordering on the atomic flag properly synchronizes access to the pending chain objects.
1427-1435: LGTM!The deferred destruction logic correctly waits for
MAX_FRAMES_IN_FLIGHT + 1frames before destroying old chains, ensuring GPU work referencing them has completed.
1437-1441: LGTM!Clean delegation with appropriate null check.
1120-1123: Per-frame dynamic allocation incheck_pending_chain_swap().The call to
check_pending_chain_swap()performsm_deferred_destroys.push_back()(line 1411), which triggers dynamic memory allocation on the per-frame render path. Based on learnings, per-frame code paths must not perform dynamic memory allocations.Consider pre-reserving capacity for
m_deferred_destroysduring initialization (e.g., reserve 2-4 slots which should cover typical shader reload frequency) to avoid allocations in the hot path.🔎 Proposed mitigation
In
init_filter_chain()orcreate():m_deferred_destroys.reserve(4); // Pre-allocate for typical usage⛔ Skipped due to learnings
Learnt from: CR Repo: K1ngst0m/Goggles PR: 0 File: docs/project_policies.md:0-0 Timestamp: 2026-01-02T07:02:32.720Z Learning: Applies to docs/**/*.{cpp,hpp} : Per-frame code paths (main thread render loop) MUST NOT perform dynamic memory allocations (`new`, `malloc`, `std::make_shared`), use blocking synchronization primitives (`std::mutex`, `std::condition_variable`), or exceed 8ms CPU time budget (excluding GPU sync).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/app/**/*.{cpp,hpp} : Do not perform dynamic memory allocations or use blocking synchronization primitives in per-frame main render loop code pathssrc/render/backend/vulkan_backend.hpp (4)
51-54: LGTM—std::functioncallback is appropriate here.The
UiRenderCallbackis passed by const reference and invoked once per frame. The type-erasure overhead ofstd::functionis acceptable for this use case where flexibility (supporting lambdas, function pointers, bound methods) outweighs the minor indirection cost.
56-65: LGTM!Appropriate accessors for ImGui integration. Returning raw pointers for non-owning access (
filter_chain()) follows the project conventions.
72-76: LGTM!The consume-once pattern is appropriate for single-threaded render loop usage.
159-177: LGTM!The async reload state is well-structured. Using
std::atomic<bool>for cross-thread signaling with release-acquire semantics (as implemented in .cpp) is correct. TheDeferredDestroystruct properly bundles the chain and runtime with the destruction threshold. Based on learnings,unique_ptris appropriate for these long-lived shader resources.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI Agents
In @src/render/backend/vulkan_backend.cpp:
- Around line 1321-1392: The async lambda in reload_shader_preset captures this
and writes to members (m_pending_shader_runtime, m_pending_filter_chain,
m_pending_chain_ready) causing potential use-after-free if VulkanBackend is
destroyed while m_pending_load_future is running; modify shutdown() to check
m_pending_load_future.valid() and if so wait on it (e.g., get() or wait())
before destroying resources (after calling m_device->waitIdle()), and add the
same safe-wait in the destructor as a safeguard so the background job finishes
before member destruction.
- Around line 1164-1248: The methods render_frame_with_ui, render_frame, and
render_clear_with_ui duplicate ~80 lines of command recording/submit logic;
refactor by adding an optional UiRenderCallback parameter to
record_render_commands (or create a private do_render_frame helper) and move the
shared logic—acquire_next_image, command buffer reset/begin, image memory
barriers setup, m_filter_chain->record invocation, the conditional UI callback
invocation (ui_callback(cmd, *m_swapchain_image_views[image_index],
m_swapchain_extent)), final barrier, cmd.end and submit_and_present—into that
single helper; then update render_frame, render_frame_with_ui and
render_clear_with_ui to call the new record_render_commands/do_render_frame with
or without the UiRenderCallback so duplicate code is removed.
- Around line 1250-1319: render_clear_with_ui duplicates logic from render_clear
and record_clear_commands; refactor by extracting the shared acquisition,
command buffer setup, image barrier, rendering begin/end and barrier teardown
into a single helper (e.g., VulkanBackend::record_clear_commands or reuse
record_clear_commands) that returns the command buffer and image_index or
accepts a lambda to inject UI work; then have render_clear_with_ui call that
helper, invoke ui_callback(cmd, *m_swapchain_image_views[image_index],
m_swapchain_extent) only when provided, finish with VK_TRY(cmd.end(), ...) and
call submit_and_present(image_index); update render_clear to call the same
helper so duplication is eliminated and ensure symbols referenced include
VulkanBackend::render_clear_with_ui, VulkanBackend::render_clear,
record_clear_commands, ui_callback, submit_and_present, acquire_next_image and
m_frames[m_current_frame].command_buffer.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/render/backend/vulkan_backend.cpp
🧰 Additional context used
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
📚 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/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Due to async GPU execution, RAII handles (`vk::Unique*`) cannot automatically handle synchronization. Use selectively: `vk::Unique*` for Instance, Device, Surface (long-lived singletons), Swapchain, Pipelines, Layouts (created once). Use plain handles for Command buffers, per-frame sync primitives (Fence, Semaphore), Imported external images.
Applied to files:
src/render/backend/vulkan_backend.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/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/tests/**/*.cpp : Only non-GPU logic is tested initially: in scope are utility functions, configuration parsing, error handling, pipeline graph logic. Out of scope: Vulkan initialization, GPU resource management, rendering, capture layer.
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : Per-frame code paths (main thread render loop) MUST NOT perform dynamic memory allocations (`new`, `malloc`, `std::make_shared`), use blocking synchronization primitives (`std::mutex`, `std::condition_variable`), or exceed 8ms CPU time budget (excluding GPU sync).
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : For Vulkan resources, follow RAII guidelines. Use `vk::Unique*` only for appropriate resource types (per D.2 table). Call `device.waitIdle()` or wait on appropriate fences before destroying resources with GPU-async lifetime. Store creation info with resources for debugging. Never leak Vulkan objects. Declare members in reverse destruction order (device before instance).
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer runs in host application threads (no control). Hooks execute in calling thread (usually render thread). No blocking in hooks, especially `vkQueuePresentKHR`. Use atomics or locks for thread-safe shared layer state.
Applied to files:
src/render/backend/vulkan_backend.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} : Application code MUST use vulkan-hpp with `VULKAN_HPP_NO_EXCEPTIONS` and dynamic dispatch
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : All resources must be managed via RAII. Wrap Vulkan objects in RAII types (custom or from libraries like `vk-bootstrap`, `vulkan-hpp`). Use RAII wrappers for file handles, sockets, etc. (`std::fstream`, `UniqueFd`, custom types). No manual cleanup in destructors unless encapsulated in RAII.
Applied to files:
src/render/backend/vulkan_backend.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/app/**/*.{cpp,hpp} : Do not perform dynamic memory allocations or use blocking synchronization primitives in per-frame main render loop code paths
Applied to files:
src/render/backend/vulkan_backend.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: Default to single-threaded render loop on main thread; use central job system (goggles::util::JobSystem) for any task parallelism
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : All concurrent processing MUST use the project's central job system. Phase 1 library: BS::thread_pool. Phase 2 library: Taskflow. Wrapper interface: `goggles::util::JobSystem`. Direct use of `std::thread` or `std::jthread` for pipeline work is PROHIBITED. Exception: External integration code (networking, IPC) outside real-time path may use `std::jthread` with RAII guarantees.
Applied to files:
src/render/backend/vulkan_backend.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/**/*.{cpp,hpp} : Use inter-thread communication via `goggles::util::SPSCQueue` (lock-free SPSC queue) for task parallelism
Applied to files:
src/render/backend/vulkan_backend.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/tests/**/*.{cpp,hpp} : Focus unit tests on non-GPU logic: utility functions, configuration parsing, and pipeline graph logic
Applied to files:
src/render/backend/vulkan_backend.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/render/backend/vulkan_backend.cpp
🧬 Code graph analysis (1)
src/render/backend/vulkan_backend.cpp (13)
src/render/backend/vulkan_backend.hpp (10)
frame(38-38)frame(52-53)frame(95-95)ui_callback(54-54)image_index(103-103)cmd(99-100)cmd(101-102)preset_path(42-42)preset_path(43-44)enabled(49-49)src/util/error.hpp (1)
make_error(50-52)src/render/chain/filter_chain.hpp (6)
cmd(43-46)cmd(76-76)preset_path(41-41)vk_ctx(29-32)enabled(54-54)enabled(54-54)src/ui/imgui_layer.hpp (1)
cmd(66-66)src/render/chain/filter_pass.hpp (2)
cmd(50-50)vk_ctx(39-40)src/render/chain/output_pass.hpp (2)
cmd(28-28)vk_ctx(17-18)src/render/chain/pass.hpp (1)
cmd(44-44)src/render/texture/texture_loader.hpp (1)
cmd(59-60)src/render/chain/preset_parser.hpp (2)
preset_path(59-59)preset_path(62-64)src/capture/vk_layer/wsi_virtual.hpp (2)
device(65-66)device(67-67)src/render/chain/framebuffer.hpp (1)
device(10-12)src/render/chain/filter_chain.cpp (2)
create(52-77)create(52-54)src/render/shader/shader_runtime.cpp (2)
create(190-257)create(190-190)
🔇 Additional comments (5)
src/render/backend/vulkan_backend.cpp (5)
9-9: LGTM: JobSystem integration follows project policy.The include is necessary for async shader compilation at line 1349.
Based on learnings, all concurrent processing must use the project's central job system, which this change correctly implements.
1121-1123: LGTM: Frame lifecycle management for async shader reload.These operations correctly manage the async shader reload state: incrementing the frame counter for deferred destruction timing, checking for completed async shader compilations, and cleaning up resources that are safe to destroy.
The ordering is appropriate: counter increment, pending swap check, then cleanup.
1394-1426: LGTM: Correct pending chain swap with deferred destruction.The acquire-release memory ordering (line 1395 ↔ line 1384 in
reload_shader_preset) ensures visibility of the pending resources before swapping. Thefuture.get()call at line 1401 is safe because the flag guarantees task completion.The deferred destruction timing (
m_frame_count + MAX_FRAMES_IN_FLIGHT + 1) correctly ensures GPU-async resources are no longer in use before destruction, aligning with project RAII policies for Vulkan resources.Based on learnings, this approach properly handles the GPU-async resource lifetime constraint.
1428-1436: LGTM: Clean deferred destruction cleanup.The
std::erase_ifpattern correctly removes entries when the frame delay has elapsed, triggering RAII destruction of the old filter chain and shader runtime.
1438-1442: LGTM: Straightforward shader toggle.The bypass flag inversion correctly maps the enabled state to the filter chain's bypass behavior.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI Agents
In @src/render/backend/vulkan_backend.cpp:
- Around line 1398-1430: The push_back on m_deferred_destroys inside
VulkanBackend::check_pending_chain_swap can allocate during per-frame execution;
change this to avoid dynamic allocations by either pre-reserving capacity for
m_deferred_destroys (call reserve(...) in the VulkanBackend constructor or
init_filter_chain) or replace m_deferred_destroys with a fixed-size ring buffer
(e.g., std::array<DeferredDestroy, N> plus a size/index counter like
m_deferred_count) and write into that buffer instead of calling push_back,
ensuring deferred entries are safely wrapped/overwritten or rejected; update all
code that reads m_deferred_destroys accordingly.
🧹 Nitpick comments (1)
src/render/backend/vulkan_backend.cpp (1)
1432-1440: Consider per-frame performance ofstd::erase_if.While
std::erase_ifprimarily deallocates, it still performs linear scanning and element shifting on each frame. Ifm_deferred_destroysgrows large during rapid preset changes, this could impact the per-frame CPU budget (8ms constraint).Consider tracking the removal index during insertion and processing deletions in O(1) time, or move cleanup to a separate timer-based tick outside the frame loop.
Based on learnings, per-frame paths should not exceed the 8ms CPU time budget.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/render/backend/vulkan_backend.cpp
🧰 Additional context used
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
📚 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/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : Due to async GPU execution, RAII handles (`vk::Unique*`) cannot automatically handle synchronization. Use selectively: `vk::Unique*` for Instance, Device, Surface (long-lived singletons), Swapchain, Pipelines, Layouts (created once). Use plain handles for Command buffers, per-frame sync primitives (Fence, Semaphore), Imported external images.
Applied to files:
src/render/backend/vulkan_backend.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/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/tests/**/*.cpp : Only non-GPU logic is tested initially: in scope are utility functions, configuration parsing, error handling, pipeline graph logic. Out of scope: Vulkan initialization, GPU resource management, rendering, capture layer.
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : For Vulkan resources, follow RAII guidelines. Use `vk::Unique*` only for appropriate resource types (per D.2 table). Call `device.waitIdle()` or wait on appropriate fences before destroying resources with GPU-async lifetime. Store creation info with resources for debugging. Never leak Vulkan objects. Declare members in reverse destruction order (device before instance).
Applied to files:
src/render/backend/vulkan_backend.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} : Application code MUST use vulkan-hpp with `VULKAN_HPP_NO_EXCEPTIONS` and dynamic dispatch
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer runs in host application threads (no control). Hooks execute in calling thread (usually render thread). No blocking in hooks, especially `vkQueuePresentKHR`. Use atomics or locks for thread-safe shared layer state.
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : Per-frame code paths (main thread render loop) MUST NOT perform dynamic memory allocations (`new`, `malloc`, `std::make_shared`), use blocking synchronization primitives (`std::mutex`, `std::condition_variable`), or exceed 8ms CPU time budget (excluding GPU sync).
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan layer code in `src/capture/vk_layer/` has strict logging constraints: use only `error` and `critical` levels, no file I/O or blocking operations in hot paths, prefix all logs with `[goggles_vklayer]`, never log in `vkQueuePresentKHR` hot path.
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/**/*.{cpp,hpp} : All resources must be managed via RAII. Wrap Vulkan objects in RAII types (custom or from libraries like `vk-bootstrap`, `vulkan-hpp`). Use RAII wrappers for file handles, sockets, etc. (`std::fstream`, `UniqueFd`, custom types). No manual cleanup in destructors unless encapsulated in RAII.
Applied to files:
src/render/backend/vulkan_backend.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/app/**/*.{cpp,hpp} : Do not perform dynamic memory allocations or use blocking synchronization primitives in per-frame main render loop code paths
Applied to files:
src/render/backend/vulkan_backend.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: Default to single-threaded render loop on main thread; use central job system (goggles::util::JobSystem) for any task parallelism
Applied to files:
src/render/backend/vulkan_backend.cpp
📚 Learning: 2026-01-02T07:02:32.720Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: docs/project_policies.md:0-0
Timestamp: 2026-01-02T07:02:32.720Z
Learning: Applies to docs/src/**/*.{cpp,hpp} : All concurrent processing MUST use the project's central job system. Phase 1 library: BS::thread_pool. Phase 2 library: Taskflow. Wrapper interface: `goggles::util::JobSystem`. Direct use of `std::thread` or `std::jthread` for pipeline work is PROHIBITED. Exception: External integration code (networking, IPC) outside real-time path may use `std::jthread` with RAII guarantees.
Applied to files:
src/render/backend/vulkan_backend.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/**/*.{cpp,hpp} : Use inter-thread communication via `goggles::util::SPSCQueue` (lock-free SPSC queue) for task parallelism
Applied to files:
src/render/backend/vulkan_backend.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/tests/**/*.{cpp,hpp} : Focus unit tests on non-GPU logic: utility functions, configuration parsing, and pipeline graph logic
Applied to files:
src/render/backend/vulkan_backend.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/render/backend/vulkan_backend.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Analysis (clang-tidy)
- GitHub Check: Build and Test (test preset)
🔇 Additional comments (4)
src/render/backend/vulkan_backend.cpp (4)
90-93: LGTM—Async task safety addressed.The addition of the future wait in
shutdown()correctly prevents use-after-free when the async shader-load task (lines 1353-1393) writes tom_pending_shader_runtimeandm_pending_filter_chain. This ensures the background job completes before member destruction.
1325-1396: Well-structured async preset reload.The implementation correctly uses
util::JobSystem(per project policy) and safely captures values for the async task. The atomicm_pending_chain_readyflag with acquire/release ordering ensures proper synchronization. The earlier fix inshutdown()(lines 90-93) prevents use-after-free when writing to pending members (lines 1386-1388).Based on learnings, using the central job system for concurrent processing.
1442-1446: LGTM—Clean shader enable/disable toggle.Simple passthrough to the filter chain's bypass flag. Implementation is straightforward and correct.
508-508: Remove this comment —m_format_changedis properly consumed.The flag is read and consumed by the public
consume_format_change()method (lines 72-76 in vulkan_backend.hpp), which returns its value and resets it. This is a standard pattern for signaling state changes to external code.
- Add consume_chain_swapped() for UI parameter sync after chain swap - Refactor record_*_commands to accept optional UI callback - Use 3s timeout in shutdown for pending async tasks - Make m_chain_swapped atomic for thread safety - Update OpenSpec documentation
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @src/render/backend/vulkan_backend.hpp:
- Line 165: m_format_changed is a plain bool and can race with other threads;
change its declaration to std::atomic<bool> (matching m_chain_swapped) and
update any access sites—especially consume_format_change() and places like
check_pending_chain_swap() or reload logic—to use atomic operations
(load/exchange/compare_exchange as appropriate) rather than non-atomic
reads/writes so reads/writes are thread-safe and consistent across threads.
In @src/ui/imgui_layer.cpp:
- Line 110: The call static_cast<void>(m_device.waitIdle()) suppresses the
vk::Result; replace it with explicit error handling for m_device.waitIdle():
either invoke the VK_TRY macro on m_device.waitIdle() (e.g.,
VK_TRY(m_device.waitIdle())) or capture the vk::Result r = m_device.waitIdle()
and check if r != vk::Result::eSuccess then handle appropriately
(log/error/abort) consistent with project policy; ensure you reference
m_device.waitIdle() and vk::Result in the fix so the return value is never
ignored.
🧹 Nitpick comments (2)
src/render/chain/filter_chain.cpp (1)
517-532: Inefficient parameter update - consider optimizing pass iteration.The current implementation calls
set_parameter_overrideandupdate_ubo_parameterson ALL passes, even those that don't declare the parameter. This wastes CPU cycles on passes that don't use the parameter.While this occurs only during user interaction (not per-frame), it could be optimized to update only the passes that declare the parameter.
♻️ Suggested optimization
void FilterChain::set_parameter(const std::string& name, float value) { bool found = false; for (auto& pass : m_passes) { + bool param_exists = false; for (const auto& param : pass->parameters()) { if (param.name == name) { found = true; + param_exists = true; break; } } - pass->set_parameter_override(name, value); - GOGGLES_MUST(pass->update_ubo_parameters()); + if (param_exists) { + pass->set_parameter_override(name, value); + GOGGLES_MUST(pass->update_ubo_parameters()); + } } if (!found) { GOGGLES_LOG_WARN("set_parameter: '{}' not found in any pass", name); } }src/render/backend/vulkan_backend.hpp (1)
175-183: Deferred destruction design is sound; consider overflow handling.The fixed-size
m_deferred_destroysarray withMAX_DEFERRED_DESTROYS = 4is appropriate for typical usage. However, if rapid consecutive reloads exceed this limit before old resources are cleaned up (e.g., user spamming reload), you could silently drop destroy requests or overwrite active entries.Consider adding overflow detection in the implementation (e.g., assert or log a warning if
m_deferred_count >= MAX_DEFERRED_DESTROYS).
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
cmake/Dependencies.cmakeopenspec/changes/add-imgui-runtime-preset-control/design.mdopenspec/changes/add-imgui-runtime-preset-control/tasks.mdsrc/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hppsrc/render/chain/filter_chain.cppsrc/render/chain/filter_pass.cppsrc/ui/imgui_layer.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
- src/render/chain/filter_pass.cpp
- cmake/Dependencies.cmake
🧰 Additional context used
📓 Path-based instructions (1)
openspec/**/*.md
📄 CodeRabbit inference engine (openspec/AGENTS.md)
openspec/**/*.md: Use#### Scenario:format (4 hashtags) for scenario headers in requirements, not bullets or bold text, with at least one scenario per requirement
Use format- **WHEN** [condition] - **THEN** [expected result]for scenario steps in requirements
Files:
openspec/changes/add-imgui-runtime-preset-control/tasks.mdopenspec/changes/add-imgui-runtime-preset-control/design.md
🧠 Learnings (29)
📓 Common learnings
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.
📚 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: Testing scope: Only non-GPU logic tested initially. In scope: utility functions, configuration parsing, error handling, pipeline graph logic. Out of scope: Vulkan initialization, GPU resource management, rendering, capture layer.
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/tasks.mdopenspec/changes/add-imgui-runtime-preset-control/design.mdsrc/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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:
openspec/changes/add-imgui-runtime-preset-control/tasks.mdsrc/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 Learning: 2025-12-19T08:54:18.412Z
Learnt from: CR
Repo: K1ngst0m/Goggles PR: 0
File: openspec/AGENTS.md:0-0
Timestamp: 2025-12-19T08:54:18.412Z
Learning: Create a `design.md` file only when the change is cross-cutting (multiple services/modules), introduces new external dependencies, has security/performance/migration complexity, or has ambiguity requiring technical decisions
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/design.md
📚 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/**/{pixi.toml,pixi.lock} : Use Pixi as the single source of truth for dependencies. Use `pixi.toml` + `pixi.lock` for toolchains and third-party libraries. Local packages pulled into Pixi environment by path for pinned/forked builds. System packages acceptable only when not in Pixi and not practical to vendor; must be documented.
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/design.md
📚 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/**/{pixi.toml,pixi.lock} : All dependencies must have explicit versions. Pin versions in `pixi.toml` and commit `pixi.lock`. Pin local packages by repository state or explicit version. Document rationale for version changes in commit messages.
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/design.md
📚 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: Before adding new dependencies: (1) Justify necessity, (2) Check license compatibility, (3) Assess maintenance status, (4) Consider alternatives, (5) Discuss with team. Selection criteria: Prefer Pixi packages, use local package for pinned forks, use system packages only when necessary and documented.
Applied to files:
openspec/changes/add-imgui-runtime-preset-control/design.md
📚 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 : Single-threaded render loop on main thread by default. Render backend (`goggles::render`) runs on main thread. Pipeline execution runs on main thread. Job system for non-render work on worker threads. Threading introduced only when profiling justifies it (main thread CPU consistently >8ms).
Applied to files:
src/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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/src/capture/vk_layer/**/*.{cpp,hpp} : Vulkan capture layer logging constraints: Use only `error` and `critical` levels. No file I/O or blocking operations in hot paths. Prefix all logs with `[goggles_vklayer]`. Never log in `vkQueuePresentKHR` hot path.
Applied to files:
src/app/main.cppsrc/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/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/chain/filter_chain.cppsrc/render/backend/vulkan_backend.hpp
📚 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/app/main.cppsrc/render/backend/vulkan_backend.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/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/chain/filter_chain.cppsrc/render/backend/vulkan_backend.hppsrc/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/app/main.cppsrc/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/app/main.cppsrc/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/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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/app/main.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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 : Initialize logger once at application startup for console output (development) or file output (optional). For capture layer, initialize logger lazily on first `vkCreateInstance` hook.
Applied to files:
src/app/main.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/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hppsrc/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/render/backend/vulkan_backend.cppsrc/render/chain/filter_chain.cppsrc/render/backend/vulkan_backend.hpp
📚 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/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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,hpp} : All resources must be managed via RAII. Wrap Vulkan objects in RAII types. Use RAII wrappers for file handles and sockets. No manual cleanup in destructors unless encapsulated in RAII.
Applied to files:
src/render/backend/vulkan_backend.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/app/**/*.{cpp,hpp} : Do not perform dynamic memory allocations or use blocking synchronization primitives in per-frame main render loop code paths
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/chain/filter_chain.cppsrc/render/backend/vulkan_backend.hpp
📚 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,hpp} : Object initialization policy: Use factory pattern `static auto create(...) -> ResultPtr<T>` for: (1) Manager/singleton objects (VulkanBackend, FilterChain), (2) Objects owning multiple interdependent resources, (3) Objects where uninitialized state causes undefined behavior. Banned pattern: No two-phase initialization (no constructor + `init()`).
Applied to files:
src/render/backend/vulkan_backend.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} : Application code MUST use vulkan-hpp with `VULKAN_HPP_NO_EXCEPTIONS` and dynamic dispatch
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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: Default to single-threaded render loop on main thread; use central job system (goggles::util::JobSystem) for any task parallelism
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpp
📚 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 : All concurrent processing MUST use the project's central job system (`goggles::util::JobSystem`). Phase 1: BS::thread_pool. Phase 2: Taskflow. Direct use of `std::thread` or `std::jthread` for pipeline work is PROHIBITED. Exception: External integration code (networking, IPC) outside real-time path may use `std::jthread` with RAII.
Applied to files:
src/render/backend/vulkan_backend.cppsrc/render/chain/filter_chain.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,hpp} : All real-time inter-thread communication MUST use `goggles::util::SPSCQueue` for fixed producer-consumer pairs (wait-free guarantee). Never use MPMC queues in latency-critical paths. Operations must be non-blocking (`try_push`, `try_pop`). Use pointer passing with pre-allocated buffers.
Applied to files:
src/render/chain/filter_chain.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/**/*.{cpp,hpp} : Use inter-thread communication via `goggles::util::SPSCQueue` (lock-free SPSC queue) for task parallelism
Applied to files:
src/render/chain/filter_chain.cpp
🧬 Code graph analysis (4)
src/render/backend/vulkan_backend.cpp (5)
src/render/backend/vulkan_backend.hpp (9)
cmd(103-105)cmd(106-108)image_index(109-109)ui_callback(54-54)frame(38-38)frame(52-53)frame(99-99)preset_path(42-42)preset_path(43-44)src/render/chain/filter_chain.hpp (4)
cmd(53-56)cmd(91-91)preset_path(51-51)vk_ctx(39-42)src/util/error.hpp (1)
make_error(50-52)src/render/chain/preset_parser.hpp (2)
preset_path(59-59)preset_path(62-64)src/render/chain/framebuffer.hpp (1)
device(10-12)
src/render/chain/filter_chain.cpp (2)
src/render/chain/filter_chain.hpp (3)
pass(89-90)name(75-75)name(76-76)src/render/chain/filter_pass.hpp (5)
name(74-76)name(74-74)name(79-81)name(79-79)name(87-87)
src/render/backend/vulkan_backend.hpp (8)
src/render/chain/filter_pass.hpp (3)
nodiscard(84-86)nodiscard(92-94)cmd(50-50)src/util/config.hpp (1)
nodiscard(19-31)src/capture/capture_receiver.hpp (7)
nodiscard(34-34)nodiscard(35-35)nodiscard(36-36)nodiscard(38-38)nodiscard(39-39)nodiscard(40-42)nodiscard(43-43)src/render/chain/preset_parser.hpp (2)
preset_path(59-59)preset_path(62-64)src/capture/vk_layer/ipc_socket.hpp (1)
frame_ready_fd(21-21)src/ui/imgui_layer.hpp (1)
cmd(66-66)src/render/chain/frame_history.hpp (1)
cmd(19-19)src/render/chain/output_pass.hpp (1)
cmd(28-28)
src/ui/imgui_layer.cpp (1)
src/util/error.hpp (2)
make_result_ptr_error(63-64)make_result_ptr(57-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build and Test (test preset)
- GitHub Check: Static Analysis (clang-tidy)
🔇 Additional comments (23)
src/render/chain/filter_chain.cpp (4)
446-449: LGTM - Good defensive check.The guard prevents resize operations when no source has been rendered yet, avoiding unnecessary framebuffer allocations and potential issues with zero-sized extents.
489-515: LGTM - Parameter collection implementation is sound.The method correctly deduplicates parameters by name across passes and provides current values for UI display. The allocations (map and vector) are acceptable since this is only called at startup and after chain swaps, not in per-frame paths.
534-544: LGTM - Parameter reset correctly handles multiple passes.The implementation correctly resets the parameter to its default value in all passes that declare it. The inner
break(Line 540) exits the parameter iteration once found, while continuing to check remaining passes.
546-551: LGTM - Clean implementation of parameter override reset.The method correctly clears all parameter overrides across all passes and updates the UBOs accordingly. Called only from UI reset action, so the iteration overhead is acceptable.
src/app/main.cpp (5)
22-36: LGTM - Well-implemented preset scanning.The function safely scans for preset files using error codes to avoid exceptions, and is called only once at startup. The filesystem iteration overhead is acceptable in this context.
38-65: LGTM - Efficient parameter state synchronization.The helper correctly converts filter chain parameters to UI state and uses
std::moveto efficiently transfer ownership. Called only at initialization and after chain swaps, not in per-frame paths.
67-93: LGTM - Clean UI state handling with proper validation.The function correctly tracks state changes using a static variable, validates preset indices, and handles errors appropriately. The reload is triggered only on user action, not per-frame.
108-163: LGTM - Proper ImGui event handling and input capture logic.The event processing correctly forwards events to ImGui first, then conditionally forwards to the game input based on capture state. This prevents input conflicts when the UI is active.
202-241: LGTM - Well-structured ImGui frame lifecycle integration.The per-frame UI integration correctly follows ImGui's begin/end lifecycle, with the UI callback appropriately placed during command recording. The chain swap notification (Line 237) properly updates UI state after asynchronous preset loads complete.
src/render/backend/vulkan_backend.cpp (7)
919-982: LGTM - Clean UI callback integration into render command recording.The optional UI callback parameter elegantly resolves the code duplication issue flagged in previous reviews. The callback is correctly invoked after filter chain rendering and before the final present barrier.
984-1041: LGTM - Consistent UI callback integration pattern.The implementation mirrors the render path with the same clean optional callback approach, resolving the duplication issue from previous reviews.
1179-1207: LGTM - render_frame_with_ui correctly integrates UI rendering.The implementation properly extends the standard render path with UI callback support while maintaining the same frame lifecycle and error handling.
1209-1226: LGTM - render_clear_with_ui maintains consistency.The clear path correctly includes frame lifecycle management and UI callback integration, maintaining consistency with the render path.
1228-1299: LGTM - Async reload implementation with proper lifecycle management.The async shader preset loading is well-designed:
- Early returns prevent overlapping reloads (Lines 1235-1244)
- Value captures isolate the async task from state changes
- The shutdown wait (Lines 90-96) ensures task completion before destruction
- Ready flag synchronization is correct with release/acquire semantics
1301-1356: LGTM - Per-frame chain swap and cleanup adhere to allocation constraints.Both methods correctly avoid dynamic allocations:
check_pending_chain_swap: Uses a fixed-size deferred destroy array, resolving the allocation concern from previous reviewscleanup_deferred_destroys: Performs in-place array compaction without allocationsThe atomic synchronization ensures the future is ready before calling
.get(), preventing blocking in the per-frame path. Based on learnings, this complies with main thread constraints.
1358-1362: LGTM - Simple and clear shader enable/disable toggle.The method correctly translates the "enabled" semantic to the filter chain's "bypass" flag with appropriate null safety.
src/ui/imgui_layer.cpp (5)
85-90: Mixing Vulkan C and C++ API for ImGui compatibility.Line 85 casts
vk::FormattoVkFormatfor the C-styleVkPipelineRenderingCreateInfostruct required by ImGui's C API (ImGui_ImplVulkan_Init). This is a necessary compatibility layer and is acceptable here, but be aware that the project otherwise mandates vulkan-hpp (vk::types) per coding guidelines.
27-102: Initialization logic is correct and follows best practices.The factory method properly:
- Uses structured bindings to check
vk::Result(line 65-66)- Creates the descriptor pool with appropriate flags and pool sizes for ImGui
- Initializes ImGui with dynamic rendering support (
UseDynamicRendering = true)- Cleans up resources on failure paths
- Logs success at line 100
141-158: UI rendering integration with dynamic rendering is implemented correctly.The
record()method:
- Uses
vk::AttachmentLoadOp::eLoadto preserve underlying content (line 145), allowing UI to overlay on top of the rendered frame- Properly sets up
vk::RenderingInfofor dynamic rendering (lines 148-153)- Calls
ImGui_ImplVulkan_RenderDrawDatawithin the render pass (line 156)This is the correct pattern for ImGui integration with Vulkan dynamic rendering.
165-182: Preset tree construction is sound but relatively complex.The
rebuild_preset_tree()method constructs a hierarchical tree from flat file paths. The nested loop (lines 172-180) iterates over path components and builds directory/file nodes. While the logic is correct, the complexity is O(n × depth) where n is the preset count and depth is the average directory depth.This is acceptable since it's not called per-frame (only on catalog updates). If preset counts grow very large (>1000s), consider caching or incremental updates.
319-322: Dummy parameter skip logic is correct with>=condition.The condition
param.info.min_value >= param.info.max_valuecorrectly identifies both dummy/separator parameters (where min == max) and malformed ranges (where min > max), preventing invalid sliders. The comment at line 318 mentions "min == max" but the code defensively handles the broader case.src/render/backend/vulkan_backend.hpp (2)
65-65: API design allows pointer caching but current usage is safe due to deferred destruction.The
filter_chain()method returns a raw pointer to the activeFilterChain. Whilecheck_pending_chain_swap()can replacem_filter_chainduring async reloads, the deferred destruction mechanism queues old chains for cleanupMAX_FRAMES_IN_FLIGHT + 1frames later, keeping them valid during that window. All current usage patterns get the pointer immediately and use it within the same scope—the lambdas in callbacks capture the backend reference rather than caching pointers.However, this API design is error-prone and invites misuse (e.g., storing the pointer for later use). Consider documenting the pointer's lifetime constraint (valid only until the next call to
check_pending_chain_swap()) or returningstd::shared_ptr<FilterChain>to make the safety semantics explicit.
168-173: Async reload design is correct and maintains main thread safety.The implementation properly addresses the concerns:
No blocking in per-frame paths: The
m_pending_load_future.get()at line 1308 is safe becausem_pending_chain_readyis set to true (line 1292) only after the async task completes. Whencheck_pending_chain_swap()is called inrender_frame(), the future is already ready andget()returns immediately without blocking.No per-frame allocations: The
std::moveoperations incheck_pending_chain_swap()perform no allocations. Old resources are queued for deferred destruction viam_deferred_destroys, ensuring their destructors run only afterMAX_FRAMES_IN_FLIGHTframes have passed, safely outside the critical timing window.Proper synchronization: The atomic flag with acquire/release semantics ensures the main thread sees the completed async work before swapping chains.
The pattern is well-designed for background shader compilation without impacting the per-frame budget.
Summary
Completed
Pending