feat(layer): cross-process semaphore export#16
Conversation
📝 WalkthroughWalkthroughImplements cross-process timeline semaphore export and IPC transfer: introduces two timeline semaphores (frame_ready, frame_consumed), new IPC messages (semaphore_init, frame_metadata), per-device DeviceSyncState, FD transfer via SCM_RIGHTS, and import/use in the Goggles Vulkan backend. Changes
Sequence Diagram(s)sequenceDiagram
participant Layer as Capture Layer
participant Socket as IPC Socket
participant Receiver as Goggles Receiver
participant App as Application
participant Backend as Vulkan Backend
rect rgb(240,248,255)
Note over Layer: Layer-side device sync init
Layer->>Layer: create two timeline semaphores
Layer->>Layer: export FDs via vkGetSemaphoreFdKHR
Layer->>Socket: send_semaphores(fd_ready, fd_consumed) (SCM_RIGHTS)
Socket->>Receiver: deliver semaphore_init + FDs
Receiver->>Receiver: process_message -> store FDs, semaphores_updated=true
end
rect rgb(255,250,240)
Note over App,Backend: Goggles import & runtime loop
App->>Receiver: poll semaphores_updated?
Receiver-->>App: true + FDs
App->>App: dup FDs
App->>Backend: import_sync_semaphores(dup_ready, dup_consumed)
Backend->>Backend: import as timeline semaphores
loop per frame
Backend->>Backend: wait timeline on frame_ready (with timeout)
Backend->>Backend: render frame
Backend->>Layer: (implicit) signal frame_consumed with frame number
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
4f11ec2 to
d615e01
Compare
- Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer
d8d4dfa to
ce53d52
Compare
Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated.
ce53d52 to
e34d2f5
Compare
- Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases)
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/capture/vk_layer/vk_capture.hpp (1)
75-113: Fix excessive padding in CaptureManager class.The pipeline failure indicates 71 bytes of padding where only 7 is optimal. Reorder the member fields as suggested by clang-tidy to fix the build error.
🔎 Proposed fix: Reorder fields for optimal padding
private: + // Async worker state + util::SPSCQueue<AsyncCaptureItem> async_queue_{16}; + std::thread worker_thread_; + + // Mutexes and synchronization std::mutex mutex_; + std::mutex cv_mutex_; + std::condition_variable cv_; + + // Data maps std::unordered_map<VkSwapchainKHR, SwapData> swaps_; std::unordered_map<VkDevice, DeviceSyncState> device_sync_; - // Async worker state - util::SPSCQueue<AsyncCaptureItem> async_queue_{16}; - std::thread worker_thread_; std::atomic<bool> shutdown_{false}; - std::mutex cv_mutex_; - std::condition_variable cv_;
🧹 Nitpick comments (6)
openspec/changes/add-semaphore-export/specs/vk-layer-capture/spec.md (1)
57-61: Consider hyphenating compound modifier (optional).For consistency with technical writing conventions, consider "Frame-ready signal" instead of "Frame ready signal" when used as a compound modifier.
📝 Suggested edit
-#### Scenario: Frame ready signal +#### Scenario: Frame-ready signalopenspec/changes/add-semaphore-export/tasks.md (1)
45-45: Reminder: Task 8.2 remains incomplete.The back-pressure testing with a slow Goggles receiver is marked as incomplete. Consider prioritizing this test to validate the timeout and reconnection behavior under load.
Would you like me to generate a test plan or open an issue to track this remaining test coverage?
src/capture/capture_receiver.cpp (2)
265-280: Semaphore FDs are stored as rawintwithout RAII wrapper, risking leaks.Unlike
m_frame.dmabuf_fdwhich usesUniqueFd, the semaphore FDs (m_frame_ready_fd,m_frame_consumed_fd) are stored as rawint. Ifclear_sync_semaphores()is not called in all error paths, these FDs may leak. Consider usingUniqueFdfor consistency and safety.
283-304:frame_metadatahandler logs info on every new DMA-BUF, which may be noisy.The
GOGGLES_LOG_INFOat line 293 logs every time a new DMA-BUF FD is received. In high-frame-rate scenarios, this could flood logs. Consider usingGOGGLES_LOG_DEBUGorGOGGLES_LOG_TRACEinstead.🔎 Proposed fix
if (fd_index < fds.size()) { int new_fd = fds[fd_index++]; m_frame.dmabuf_fd = util::UniqueFd{new_fd}; - GOGGLES_LOG_INFO("Received new DMA-BUF fd={}", new_fd); + GOGGLES_LOG_DEBUG("Received new DMA-BUF fd={}", new_fd); }src/capture/vk_layer/vk_capture.cpp (1)
545-552: Loop iterates all swaps for device, consider using device-keyed swap lookup.The loop searches all swapchains to find those belonging to a specific device. This O(n) scan is fine for typical use cases (few swapchains) but could be optimized with a device-to-swapchains index if needed in the future.
src/render/backend/vulkan_backend.cpp (1)
1021-1036: Semaphore wait timeout of 100ms may be too aggressive for normal operation.The 100ms timeout for
frame_readysemaphore wait could trigger false positives under heavy system load, causing premature cleanup of sync semaphores. Consider:
- Using a longer timeout (e.g., 500ms to match the layer side)
- Adding a retry before cleanup
🔎 Proposed fix - align timeout with layer side
- constexpr uint64_t TIMEOUT_NS = 100'000'000; + constexpr uint64_t TIMEOUT_NS = 500'000'000; // 500ms, aligned with layer timeout
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
.gitignoreopenspec/changes/add-semaphore-export/design.mdopenspec/changes/add-semaphore-export/proposal.mdopenspec/changes/add-semaphore-export/specs/vk-layer-capture/spec.mdopenspec/changes/add-semaphore-export/tasks.mdsrc/app/main.cppsrc/capture/capture_protocol.hppsrc/capture/capture_receiver.cppsrc/capture/capture_receiver.hppsrc/capture/vk_layer/ipc_socket.cppsrc/capture/vk_layer/ipc_socket.hppsrc/capture/vk_layer/vk_capture.cppsrc/capture/vk_layer/vk_capture.hppsrc/capture/vk_layer/vk_dispatch.hppsrc/capture/vk_layer/vk_hooks.cppsrc/render/backend/vulkan_backend.cppsrc/render/backend/vulkan_backend.hpptests/CMakeLists.txttests/capture/test_capture_protocol.cpp
🧰 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-semaphore-export/design.mdopenspec/changes/add-semaphore-export/specs/vk-layer-capture/spec.mdopenspec/changes/add-semaphore-export/tasks.mdopenspec/changes/add-semaphore-export/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-semaphore-export/specs/vk-layer-capture/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-semaphore-export/proposal.md
🧬 Code graph analysis (8)
src/capture/capture_receiver.cpp (3)
src/util/unique_fd.hpp (1)
fd(34-34)src/capture/vk_layer/ipc_socket.hpp (4)
control(24-24)data(20-20)metadata(22-22)metadata(23-23)src/capture/capture_receiver.hpp (2)
data(51-51)m_semaphores_updated(43-43)
src/capture/vk_layer/vk_hooks.cpp (3)
src/capture/vk_layer/vk_capture.cpp (2)
get_capture_manager(115-118)get_capture_manager(115-115)src/capture/vk_layer/vk_capture.hpp (8)
get_capture_manager(115-115)device(80-81)device(82-82)device(83-83)device(90-90)device(91-91)device(92-92)device(99-99)src/capture/vk_layer/vk_dispatch.hpp (5)
device(111-111)device(114-114)device(115-115)device(117-117)device(120-120)
src/capture/vk_layer/ipc_socket.cpp (1)
src/capture/vk_layer/ipc_socket.hpp (3)
frame_ready_fd(21-21)metadata(22-22)metadata(23-23)
src/capture/capture_receiver.hpp (3)
src/render/backend/vulkan_backend.hpp (2)
nodiscard(38-38)nodiscard(44-44)src/util/unique_fd.hpp (4)
nodiscard(34-39)nodiscard(41-46)nodiscard(48-48)nodiscard(50-50)src/capture/vk_layer/ipc_socket.hpp (1)
data(20-20)
src/capture/vk_layer/vk_capture.hpp (2)
src/capture/vk_layer/vk_dispatch.hpp (7)
device(111-111)device(114-114)device(115-115)device(117-117)device(120-120)queue(116-116)queue(119-119)src/render/backend/vulkan_backend.hpp (1)
image_index(70-70)
src/render/backend/vulkan_backend.cpp (4)
src/render/backend/vulkan_backend.hpp (4)
frame(35-35)frame(62-62)frame_ready_fd(42-43)image_index(70-70)src/util/error.hpp (1)
make_error(45-47)src/capture/vk_layer/ipc_socket.hpp (1)
frame_ready_fd(21-21)src/util/config.hpp (1)
to_string(19-19)
src/capture/vk_layer/ipc_socket.hpp (1)
src/render/backend/vulkan_backend.hpp (1)
frame_ready_fd(42-43)
src/capture/vk_layer/vk_capture.cpp (3)
src/capture/vk_layer/vk_capture.hpp (7)
device(80-81)device(82-82)device(83-83)device(90-90)device(91-91)device(92-92)device(99-99)src/capture/vk_layer/ipc_socket.cpp (2)
get_layer_socket(16-19)get_layer_socket(16-16)src/capture/vk_layer/ipc_socket.hpp (3)
get_layer_socket(34-34)metadata(22-22)metadata(23-23)
🪛 GitHub Actions: CI
src/capture/vk_layer/vk_hooks.cpp
[error] 1-1: Excessive padding in 'class goggles::capture::CaptureManager' (71 padding bytes, where 7 is optimal). Optimal fields order: async_queue_, worker_thread_, mutex_, cv_mutex_, cv_, swaps_, device_sync_, shutdown_, consider reordering the fields or adding explicit padding members [clang-analyzer-optin.performance.Padding,-warnings-as-errors]
src/capture/vk_layer/vk_capture.hpp
[error] 75-90: Excessive padding in 'class goggles::capture::CaptureManager' (71 padding bytes, where 7 is optimal). Optimal fields order: async_queue_, worker_thread_, mutex_, cv_mutex_, cv_, swaps_, device_sync_, shutdown_. clang-tidy performance padding warning treated as error. Failing compile command: "/usr/local/bin/cmake -E __run_co_compile --launcher=/usr/bin/ccache --tidy="/usr/bin/clang-tidy;--header-filter=/home/runner/work/Goggles/Goggles/src/.*;--extra-arg-before=--driver-mode=g++" --source=/home/runner/work/Goggles/Goggles/src/capture/vk_layer/vk_capture.cpp -- /usr/bin/c++ -DGOGGLES_PROJECT_NAME="Goggles" -DGOGGLES_VERSION="0.1.0" -DGOGGLES_VERSION_MAJOR=0 -DGOGGLES_VERSION_MINOR=1 -DGOGGLES_VERSION_PATCH=0 -Dgoggles_vklayer_EXPORTS -I/home/runner/work/Goggles/Goggles/src -I/home/runner/work/Goggles/Goggles/src/capture/vk_layer -g -std=c++20 -fPIC -Wall -Wextra -Wshadow -Wconversion -fsanitize=address -fno-omit-frame-pointer -MD -MT src/capture/CMakeFiles/goggles_vklayer.dir/vk_layer/vk_capture.cpp.o -MF src/capture/CMakeFiles/goggles_vklayer.dir/vk_layer/vk_capture.cpp.o.d -o src/capture/CMakeFiles/goggles_vklayer.dir/vk_layer/vk_capture.cpp.o -c /home/runner/work/Goggles/Goggles/src/capture/vk_layer/vk_capture.cpp".
[error] 75-90: Excessive padding in 'class goggles::capture::CaptureManager' (71 padding bytes, where 7 is optimal). Optimal fields order: async_queue_, worker_thread_, mutex_, cv_mutex_, cv_, swaps_, device_sync_, shutdown_. clang-tidy performance padding warning treated as error. Failing compile command: "/usr/local/bin/cmake -E __run_co_compile --launcher=/usr/bin/ccache --tidy="/usr/bin/clang-tidy;--header-filter=/home/runner/work/Goggles/Goggles/src/.*;--extra-arg-before=--driver-mode=g++" --source=/home/runner/work/Goggles/Goggles/src/capture/vk_layer/vk_capture.cpp -- /usr/bin/c++ -DGOGGLES_PROJECT_NAME="Goggles" -DGOGGLES_VERSION="0.1.0" -DGOGGLES_VERSION_MAJOR=0 -DGOGGLES_VERSION_MINOR=1 -DGOGGLES_VERSION_PATCH=0 -Dgoggles_vklayer_EXPORTS -I/home/runner/work/Goggles/Goggles/src -I/home/runner/work/Goggles/Goggles/src/capture/vk_layer -g -std=c++20 -fPIC -Wall -Wextra -Wshadow -Wconversion -fsanitize=address -fno-omit-frame-pointer -MD -MT src/capture/CMakeFiles/goggles_vklayer.dir/vk_layer/vk_capture.cpp.o -MF src/capture/CMakeFiles/goggles_vklayer.dir/vk_layer/vk_capture.cpp.o.d -o src/capture/CMakeFiles/goggles_vklayer.dir/vk_layer/vk_capture.cpp.o -c /home/runner/work/Goggles/Goggles/src/capture/vk_layer/vk_capture.cpp".
🪛 LanguageTool
openspec/changes/add-semaphore-export/specs/vk-layer-capture/spec.md
[grammar] ~57-~57: Use a hyphen to join words.
Context: ... if timeout occurs #### Scenario: Frame ready signal - WHEN the copy comman...
(QB_NEW_EN_HYPHEN)
🔇 Additional comments (39)
.gitignore (1)
87-91: LGTM! Standard IDE ignore entries.The Claude Code workspace entries are properly added to prevent tracking IDE-specific files.
src/capture/vk_layer/vk_dispatch.hpp (1)
59-60: LGTM! Function pointer properly placed.The
GetSemaphoreFdKHRfunction pointer is correctly added alongside other external memory/semaphore functions and is properly loaded invk_hooks.cppat line 231.openspec/changes/add-semaphore-export/specs/vk-layer-capture/spec.md (1)
1-103: Comprehensive spec with proper OpenSpec formatting.The spec correctly uses the required format with
## ADDED Requirements,## MODIFIED Requirements,#### Scenario:headers, andWHEN/THENstructure. The cross-process semaphore export requirements are well-documented with clear scenarios.tests/CMakeLists.txt (2)
33-34: LGTM! Test source properly added.The new capture protocol test is correctly added to the test sources.
49-49: LGTM! Required library linked.The
goggles_capturelibrary is properly linked to enable testing of capture protocol types.src/capture/vk_layer/vk_hooks.cpp (3)
170-171: LGTM! Required semaphore extensions added.The external semaphore extensions are correctly added to enable cross-process synchronization via FD export.
231-231: LGTM! Function pointer properly loaded.The
GetSemaphoreFdKHRfunction address is correctly loaded into the dispatch table, corresponding to the declaration invk_dispatch.hppat line 60.
294-294: LGTM! Device destruction hook correctly placed.The
on_device_destroyedcallback is invoked before device cleanup, ensuring the capture manager can properly release device-level synchronization resources while device data is still valid.tests/capture/test_capture_protocol.cpp (1)
1-73: LGTM! Comprehensive protocol test coverage.The test suite properly validates:
- Message type enum values for wire format stability
- Struct sizes (critical for IPC compatibility)
- Default initialization values
- Socket path format
This provides good coverage for the new cross-process synchronization protocol.
src/app/main.cpp (1)
49-68: LGTM! Semaphore import logic is sound.The implementation correctly:
- Cleans up old semaphores before importing new ones
- Uses
UniqueFd::dup_from()for proper FD ownership transfer- Handles errors by clearing receiver-side semaphores
- Logs success/failure appropriately
The placement after
poll_frame()ensures semaphores are imported before rendering begins.src/capture/capture_receiver.hpp (1)
7-7: LGTM! Clean API design for semaphore synchronization.The additions properly support cross-process synchronization:
frame_numberfield enables timeline semaphore tracking- Public accessors provide read-only access to sync state
- Private members maintain encapsulation
m_recv_bufsupports buffered message processingThe design aligns well with the broader synchronization flow implemented across the PR.
Also applies to: 18-18, 37-44, 51-51, 57-62
src/capture/vk_layer/ipc_socket.hpp (1)
21-23: LGTM: New IPC methods follow existing patterns.The three new methods extend the IPC interface consistently with the existing
send_texturemethod, using raw FD integers appropriate for SCM_RIGHTS transfer.src/capture/vk_layer/vk_capture.hpp (2)
33-42: LGTM: DeviceSyncState structure is well-designed.The per-device synchronization state correctly encapsulates frame-ready and frame-consumed semaphores with their FDs, along with the necessary tracking state for cross-process synchronization.
90-99: LGTM: Device-level sync management methods are appropriate.The transition from
init_sync_primitivestoinit_device_syncwith correspondingreset_device_sync,cleanup_device_sync, andget_device_syncmethods correctly reflects the shift to per-device synchronization lifecycle management.openspec/changes/add-semaphore-export/design.md (1)
1-91: LGTM: Design document is comprehensive and well-structured.The design clearly articulates the two-semaphore synchronization model, protocol changes, required extensions, and risk mitigation strategies. The sync flow diagram and protocol structure examples effectively communicate the cross-process synchronization approach.
openspec/changes/add-semaphore-export/proposal.md (1)
1-28: LGTM: Proposal follows the required format.The proposal correctly includes the "What Changes" section with clear specification deltas. The changes are appropriately identified as additive (no breaking changes marked), and the impact section clearly maps affected specs and code areas.
src/capture/vk_layer/ipc_socket.cpp (3)
138-180: LGTM: send_semaphores correctly implements dual-FD transfer.The method properly sends two FD handles via SCM_RIGHTS alongside the
CaptureSemaphoreInitpayload, with consistent error handling that distinguishes between transient (EAGAIN/EWOULDBLOCK) and fatal errors.
182-218: LGTM: send_texture_with_fd correctly combines metadata with FD.The implementation properly sends frame metadata alongside a DMA-BUF FD via SCM_RIGHTS, following the same error-handling pattern as other IPC methods.
220-239: LGTM: send_frame_metadata correctly sends metadata without FD.The method uses plain
send()(no ancillary data) for frame metadata, maintaining consistent error handling with the other IPC methods.src/render/backend/vulkan_backend.hpp (2)
42-45: LGTM: Sync semaphore API uses proper FD ownership semantics.The
import_sync_semaphoresmethod correctly usesutil::UniqueFdfor RAII-based FD ownership transfer, ensuring no leaks. The companion methodshas_sync_semaphoresandcleanup_sync_semaphoresprovide appropriate lifecycle management.
123-127: LGTM: Sync state members are appropriately designed.The member variables correctly track imported semaphores, synchronization state, and frame counters needed for cross-process GPU synchronization.
src/capture/capture_protocol.hpp (2)
18-20: LGTM: New message types extend the protocol cleanly.The
semaphore_initandframe_metadatamessage types are sequentially numbered and integrate well with the existing protocol enumeration.
50-70: LGTM: New protocol structures are well-designed with size validation.Both
CaptureSemaphoreInit(16 bytes) andCaptureFrameMetadata(40 bytes) have appropriatestatic_assertchecks for wire-format stability. The comment clarifying SCM_RIGHTS FD transfer inCaptureSemaphoreInitis helpful, andCaptureFrameMetadatacorrectly extends the texture metadata withframe_numberfor timeline synchronization.src/capture/capture_receiver.cpp (4)
189-192: Unknown message type clears buffer but continues loop, causing infinite loop.When an unknown message type is encountered, the buffer is cleared but no
breakfollows, causing thewhileloop to re-evaluate with an empty buffer. However, thebreakat line 192 is inside theswitchstatement and only exits theswitch, not thewhileloop. Sincemsg_sizeremains 0 after clearing the buffer, the check at line 195 will break out of thewhileloop correctly.
149-160: FD extraction logic is correct and handles multiple FDs properly.The loop correctly extracts all FDs from ancillary data using
CMSG_LEN(0)for header size calculation andmemcpyfor safe alignment handling.
206-209: Closing unused FDs prevents resource leaks.Good practice to close any FDs that weren't consumed during message processing. This ensures no file descriptor leaks when more FDs are received than expected.
120-121: Buffer size is adequate for all defined message types.The 256-byte buffer is sufficient. The largest message type is
CaptureClientHelloat 72 bytes, well within capacity. All message types have their sizes enforced viastatic_assertincapture_protocol.hpp. The persistent buffer pattern (m_recv_buf) handles message fragmentation across multiplerecv()calls, so the single-call buffer size does not constrain message reception.Likely an incorrect or invalid review comment.
src/capture/vk_layer/vk_capture.cpp (6)
502-528: FD export transfers ownership; FDs should not be closed on failure after export.When
GetSemaphoreFdKHRsucceeds, Vulkan transfers ownership of the FD to the caller. However, if the second FD export (line 518) fails, the code correctly closessync.frame_ready_fdat line 521. This is correct behavior.
813-818: Semaphore FDs sent only once; reconnection requiresreset_device_sync.The
semaphores_sentflag prevents re-sending FDs after reconnection. This is handled correctly viareset_device_syncwhich resets the flag at line 584. The design ensures FDs are re-exported and re-sent on reconnection.
820-836: Back-pressure wait uses 500ms timeout; consider making this configurable.The 500ms timeout for
frame_consumedsemaphore wait is reasonable for detecting disconnection but may cause visible stutter if Goggles is temporarily slow. The current approach of resetting sync primitives on timeout is a safe recovery mechanism.
838-848: Potential deadlock ifcmd.busyand sync semaphores were reset.After
reset_device_syncat line 833,cmd.busyflags are reset (lines 547-550), so line 839'sif (cmd.busy)check should befalseafter a reset. However, there's a subtle issue: ifreset_device_syncreturns early (sync not found),cmd.busymight still betruewhile the semaphore is destroyed. The current code at line 723 handles this by checkingsync && sync->frame_ready_sem != VK_NULL_HANDLE.
886-891: Frame number 1 always sends DMA-BUF FD, ensuring initial frame transfer.The condition
current_frame == 1 || !swap->dmabuf_sentensures the DMA-BUF FD is sent on the first frame and after any reset. Thedmabuf_sentflag is cleared incleanup_swap_data(line 920), ensuring proper behavior on swapchain recreation.
589-593: Device destruction cleanup correctly removes device sync state.The
on_device_destroyedhook properly cleans up sync resources and removes the device from the map. The mutex lock ensures thread safety.src/render/backend/vulkan_backend.cpp (6)
788-845: Import semaphore logic is correct with proper FD ownership transfer.The code correctly:
- Creates timeline semaphores with initial value 0
- Imports FDs using
vkImportSemaphoreFdKHR- Releases
UniqueFdownership only after successful import (lines 824, 836)- Cleans up both semaphores on any import failure
One consideration: the FD import is done with
flags = 0, meaning the semaphore will take permanent ownership. This is correct for this use case.
1053-1066: Timeline semaphore signaling logic has correct guard for duplicate signals.The condition
m_last_frame_number > m_last_signaled_framecorrectly prevents signaling the same frame number twice, which would be a Vulkan error for timeline semaphores. Them_last_signaled_frameis updated at line 1076 only after successful submission.
847-864:waitIdlebefore destroying semaphores ensures GPU operations complete.The
waitIdlecall at line 850 ensures any pending GPU work referencing these semaphores completes before destruction. The cast tovoidexplicitly discards the result, which is acceptable during cleanup.
1108-1109: Frame number tracking enables correct timeline semaphore synchronization.Setting
m_last_frame_numberat the start ofrender_frameensures the value is available for the wait operation insubmit_and_present. This establishes the correct frame-to-frame synchronization handshake.
686-695: Manual VkMemoryFdPropertiesKHR handling bypasses vulkan.hpp wrapper.The code uses the raw Vulkan API call via the dispatcher rather than vulkan.hpp's
getMemoryFdPropertiesKHR. This is likely intentional to handle thesTypeinitialization explicitly. The error message "Stale DMA-BUF fd, skipping frame" is appropriate for this failure case.
771-786: Handle reset after destruction prevents use-after-free.Setting handles to
nullptrafter destruction is good defensive practice. The vulkan.hppdestroyImageView,freeMemory, anddestroyImagemethods are null-safe, so this also protects against double-free ifcleanup_imported_imageis called multiple times.
- Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/render/backend/vulkan_backend.cpp (1)
390-398: UncheckedgetSurfacePresentModesKHRresult.The
pm_resultfromgetSurfacePresentModesKHRis not validated. While the fallback to FIFO is safe (guaranteed available), the call could fail on device loss.🔎 Suggested fix
auto [pm_result, present_modes] = m_physical_device.getSurfacePresentModesKHR(*m_surface); vk::PresentModeKHR chosen_mode = vk::PresentModeKHR::eFifo; - for (auto mode : present_modes) { - if (mode == vk::PresentModeKHR::eMailbox) { - chosen_mode = vk::PresentModeKHR::eMailbox; - break; + if (pm_result == vk::Result::eSuccess) { + for (auto mode : present_modes) { + if (mode == vk::PresentModeKHR::eMailbox) { + chosen_mode = vk::PresentModeKHR::eMailbox; + break; + } } }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/app/main.cppsrc/capture/capture_receiver.cppsrc/capture/vk_layer/vk_hooks.cppsrc/render/backend/vulkan_backend.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
- src/capture/vk_layer/vk_hooks.cpp
🧰 Additional context used
🧬 Code graph analysis (2)
src/capture/capture_receiver.cpp (2)
src/capture/capture_receiver.hpp (4)
data(51-51)CaptureReceiver(8-46)CaptureReceiver(19-44)struct CaptureFrame {(10-17)src/capture/capture_protocol.hpp (1)
CAPTURE_SOCKET_PATH(7-49)
src/render/backend/vulkan_backend.cpp (3)
src/render/backend/vulkan_backend.hpp (4)
frame(35-35)frame(62-62)frame_ready_fd(42-43)image_index(70-70)src/util/error.hpp (1)
make_error(45-47)src/capture/vk_layer/ipc_socket.hpp (1)
frame_ready_fd(21-21)
⏰ 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
🔇 Additional comments (20)
src/app/main.cpp (3)
14-14: LGTM!The include is properly added to support the new
UniqueFdusage for semaphore FD handling.
49-68: LGTM!The semaphore synchronization handling follows a proper cleanup-before-import pattern for reconnection scenarios. Error paths correctly clear semaphores on the receiver, and
UniqueFdownership is properly transferred viastd::move().
180-180: LGTM!The updated log message is more informative and accurately describes the fallback behavior.
src/capture/capture_receiver.cpp (7)
41-45: LGTM!Good UX improvement—providing a specific error message for
EADDRINUSEhelps users diagnose the common case of another instance already running.
103-107: LGTM!Correct single-client semantics with proper FD cleanup when rejecting additional connections.
125-126: LGTM!Buffer sizes are appropriately increased to handle the new message types and FD counts (up to 4 FDs).
154-165: LGTM!FD extraction from ancillary data is correctly implemented with proper iteration and safe memory copying.
265-286: LGTM!The semaphore initialization correctly validates FD availability, cleans up existing semaphores, and stores the new FDs. The lifecycle is properly managed through
clear_sync_semaphores()called in cleanup paths.
314-330: LGTM!Cleanup functions properly manage FD lifecycle and reset all relevant state including the new receive buffer and semaphore FDs.
306-306: No issue found.The
CaptureFramestruct includes aframe_numberfield (line 18 ofcapture_receiver.hpp), so the assignment at line 306 is valid.src/render/backend/vulkan_backend.cpp (10)
30-31: LGTM!Required extensions for cross-process semaphore import are correctly added.
107-107: LGTM!Proper cleanup ordering—sync semaphores are cleaned up before device destruction.
633-635: LGTM!Good defensive validation of the DMA-BUF FD before import.
695-704: LGTM!Using raw Vulkan types for
vkGetMemoryFdPropertiesKHRis appropriate for extension functions, and the error message is now more descriptive.
780-794: LGTM!Explicitly nullifying handles after destruction prevents use-after-free bugs.
797-854: LGTM!The semaphore import logic correctly:
- Cleans up existing semaphores before reimport
- Creates timeline semaphores with proper type info
- Handles failures by cleaning up partially created resources
- Releases
UniqueFdownership after successful import (Vulkan takes ownership)
856-873: LGTM!Cleanup properly waits for idle before destroying semaphores and resets all associated state.
885-888: LGTM!Using the raw dispatcher call for
vkAcquireNextImageKHRis consistent with other extension function calls in this file.
1030-1086: LGTM!The timeline semaphore synchronization logic correctly implements:
- Back-pressure via waiting on
frame_ready_sembefore rendering- Graceful degradation on timeout (cleans up sync state, assumes layer disconnected)
- Deduplication of signal operations via
m_last_signaled_frametracking- Proper chaining of
TimelineSemaphoreSubmitInfoonly when signaling
1117-1118: LGTM!Frame number is correctly captured early in
render_framefor use in timeline semaphore synchronization.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/capture/capture_receiver.cpp (1)
288-289: Consider using UniqueFd for semaphore FDs for consistency and safety.Lines 288-289 store semaphore FDs as raw integers (
m_frame_ready_fd,m_frame_consumed_fd), unlikem_frame.dmabuf_fdwhich usesUniqueFd. This creates inconsistent ownership semantics and increases the risk of double-close or leaks if error paths are added later.Using
UniqueFdwould:
- Provide automatic cleanup on destruction
- Make ownership transfer explicit
- Align with the pattern used for DMA-BUF FDs
🔎 Proposed refactor to use UniqueFd
In the header file (capture_receiver.hpp), change the member declarations:
- int m_frame_ready_fd = -1; - int m_frame_consumed_fd = -1; + util::UniqueFd m_frame_ready_fd; + util::UniqueFd m_frame_consumed_fd;Then update this code:
int ready_fd = fds[fd_index++]; int consumed_fd = fds[fd_index++]; clear_sync_semaphores(); m_frame.dmabuf_fd = util::UniqueFd{}; - m_frame_ready_fd = ready_fd; - m_frame_consumed_fd = consumed_fd; + m_frame_ready_fd = util::UniqueFd{ready_fd}; + m_frame_consumed_fd = util::UniqueFd{consumed_fd}; m_semaphores_updated = true; - GOGGLES_LOG_INFO("Received sync semaphores: ready_fd={}, consumed_fd={}", - m_frame_ready_fd, m_frame_consumed_fd); + GOGGLES_LOG_INFO("Received sync semaphores: ready_fd={}, consumed_fd={}", + m_frame_ready_fd.get(), m_frame_consumed_fd.get());And simplify
clear_sync_semaphores():void CaptureReceiver::clear_sync_semaphores() { - if (m_frame_ready_fd >= 0) { - close(m_frame_ready_fd); - m_frame_ready_fd = -1; - } - if (m_frame_consumed_fd >= 0) { - close(m_frame_consumed_fd); - m_frame_consumed_fd = -1; - } + m_frame_ready_fd.reset(); + m_frame_consumed_fd.reset(); }Note: Verify that UniqueFd supports the required operations (get(), reset(), valid()) based on unique_fd.hpp.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/capture/capture_receiver.cpp
🧰 Additional context used
🧬 Code graph analysis (1)
src/capture/capture_receiver.cpp (3)
src/util/unique_fd.hpp (1)
fd(34-34)src/capture/vk_layer/ipc_socket.hpp (4)
control(24-24)data(20-20)metadata(22-22)metadata(23-23)src/capture/capture_receiver.hpp (2)
data(51-51)m_semaphores_updated(43-43)
⏰ 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
- GitHub Check: Static Analysis (clang-tidy)
🔇 Additional comments (6)
src/capture/capture_receiver.cpp (6)
41-45: LGTM: Enhanced error diagnostics.The specific handling of
EADDRINUSEprovides clearer feedback when multiple instances are running.
103-107: LGTM: Proper duplicate client handling.Correctly rejects new connections when already connected and prevents FD leaks.
154-223: LGTM: Robust message processing with FD leak prevention.The upfront FD extraction and cleanup at line 217-220 prevent leaks. The disconnect-on-unknown-message (lines 194-203) properly addresses the past review comment by closing remaining FDs and disconnecting rather than just clearing the buffer.
229-237: LGTM: Safe string handling.The use of
strnlencorrectly addresses the past review comment about potential buffer over-read ofexe_name.
333-338: LGTM: Complete cleanup of all frame state.The addition of
m_recv_buf.clear()andclear_sync_semaphores()ensures all frame-related state is reset on disconnection.
287-287: Line 287 is inon_frame_metadata(), not a semaphore initialization handler. Review the actual message ordering and dmabuf_fd ownership semantics.Line 287 unconditionally clears
m_frame.dmabuf_fdwithin theon_frame_metadata()method. Iftexture_data()is processed first and provides a valid dmabuf fd, and thenframe_metadata()arrives without a dmabuf fd, the fd from texture_data is lost before being restored at line 303 (which only sets it ifframe_metadata.has_dmabuf_fd()is true).This suggests either: (1)
frame_metadatais expected to be the authoritative source for dmabuf_fd and should always come first or provide this field, or (2) the design should preserve the fd from texture_data if frame_metadata doesn't provide one. The code lacks documentation clarifying which message is responsible for dmabuf_fd and whether message ordering is guaranteed.Consider adding:
- A comment documenting which message type is responsible for dmabuf_fd ownership
- Conditional logic to preserve the fd if frame_metadata doesn't provide one, or assert that it must, or ensure frame_metadata always arrives first
- Frame identifiers if texture_data and frame_metadata can belong to different logical frames
* spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements
* spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements
* spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements
* spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements
* spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements
* spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements
* chore: migrate dependencies to pixi * feat: add quality build preset commands and a `NOLINT` comment to the preprocessor. * feat: Add Vulkan SDK installation to CI and lower cmake dependency version. * feat: add ccache to project dependencies and update lock file * refactor: update compiler dependencies to specific Linux packages and use safer integer comparison functions. * feat: Define dual-layer dependency management using Pixi for system dependencies and CPM for C++ libraries. * refactor: switch to pure Clang toolchain - Remove gxx_linux-64 (GCC) dependency - Add compiler-rt for Clang sanitizer runtime - Add libcxx for Clang's libc++ standard library - Eliminates GCC 15 false positive -Wmaybe-uninitialized warnings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(openspec): update dependency-management spec for Clang toolchain Update compiler description from "clang, gcc" to "Clang toolchain" including compiler-rt and libcxx components. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add Pixi configuration and recipe files for new packages: bs-thread-pool, expected-lite, sdl3-static, slang-shaders, and stb * feat: switches all dependency management to Pixi packages and enhance Pixi functions * feat: enhance IDE setup for clang-format with pixi integration and add support for multiple IDEs * chore: update PIXI_VERSION to v0.59.0 and upgrade setup-pixi to v0.9.3 in CI configuration * chore: update CI clang-format command, fix TracyClient linking, and add CMake as a requirement for Tracy package * feat: implement 32-bit sysroot for i686 cross-compilation with Clang and add necessary dependencies * chore: overhaul build system to be fully managed by Pixi, enforce environment checks, and enhance package integrity * chore: update .gitignore to include /memories, enhance project.md with Pixi integration details, and add CI spec documentation for formatting and dependency management * feat(layer): cross-process semaphore export (#16) * spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements * refactor: update compiler dependencies to specific Linux packages and use safer integer comparison functions. * chore: update pixi .lock * refactor(ci): switch to format check mode and add lightweight lint environment - Change CI from auto-commit to fail-on-diff check mode - Add taplo for TOML formatting (C++ and TOML only, no MD) - Create lightweight lint environment with clang-format and taplo - Add `pixi run format` task for local formatting - Remove outdated Serena config date comments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(layer): cross-process semaphore export (#16) * spec(layer): add cross-process semaphore export proposal * feat(capture): cross-process timeline semaphore sync * fix(capture): move semaphores to device level and fix message boundary - Move timeline semaphores from SwapData to DeviceSyncState - Add device destruction hook to cleanup sync state - Fix TCP message coalescing with persistent receive buffer * fix(capture): reset copy_cmds state on sync timeout Prevents app hang when goggles closes by resetting busy state for all copy command buffers when sync primitives are recreated. * refactor(sync): use UniqueFd for semaphore import and add protocol tests - Change import_sync_semaphores() to take UniqueFd parameters - Properly transfer FD ownership on successful Vulkan import - Add capture protocol unit tests (8 test cases) * fix: resolve clang-tidy warnings - Reorder CaptureManager fields for optimal padding - Fix unused parameter warning in acquire_next_image - Use std::cmp_less/cmp_greater for safe signed-unsigned comparison * fix: use eMailbox present mode and improve multi-instance handling * fix: capture receiver safety improvements * refactor: update compiler dependencies to specific Linux packages and use safer integer comparison functions. * refactor(ci): switch to format check mode and add lightweight lint environment - Change CI from auto-commit to fail-on-diff check mode - Add taplo for TOML formatting (C++ and TOML only, no MD) - Create lightweight lint environment with clang-format and taplo - Add `pixi run format` task for local formatting - Remove outdated Serena config date comments 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: update formatting and linting configurations; add pre-commit hooks * chore: update first-time setup command for IDE formatting and pre-commit hook * chore: update pre-commit hook to format staged C/C++ and TOML files; add IDE setup script * refactor: switch i686 to GCC and update environment configuration - Replace Clang with GCC for i686 cross-compilation toolchain - Switch VulkanSDK from VK_LAYER_PATH to VK_ADD_LAYER_PATH - Add LD_LIBRARY_PATH activation to pixi environment * Refactor: Vulkan SDK environment setup and enhance task commands - Updated `vulkansdk.sh` activation script to save and restore old VK_LAYER_PATH and VK_ADD_LAYER_PATH values during activation and deactivation. - Modified `pixi.lock` to include additional dependencies for Clang and Compiler-RT, while removing unused GCC packages. - Adjusted `pixi.toml` task commands to use a new script `pixi-env-clean.sh` for managing Vulkan layer environment variables during task execution. - Introduced `pixi-env-clean.sh` to unset VK_ADD_LAYER_PATH for cleaner task execution, with an option to retain the host value for debugging. * chore: refine Pixi environment isolation and remove redundant scripts; update README and tasks * docs: clarify interactivity in init-format-setup.sh header * chore: ignore and stop tracking .serena directory * chore: update Pixi backend version to 0.3.5 across multiple packages; refine IDE setup script for non-interactive environments * chore: simplify Pixi tasks and README; add help command * chore: fail build if pre-commit hook not installed; pin taplo version 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: fail build if pre-commit hook not installed; pin taplo version 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(sysroot-i686): add patchelf build dependency for CI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Jim Ban <77719403+BANANASJIM@users.noreply.github.com> Co-authored-by: kingstom.chen <kingstom.chen@gmail.com>
Why
Layer uses internal timeline semaphore for GPU sync, with Worker thread waiting on CPU-side before sending DMA-BUF FD. This adds latency and cannot achieve true back-pressure.
What Changes
frame_ready(Layer→Goggles) andframe_consumed(Goggles→Layer)VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BITUniqueFdfor type-safe FD ownership transferTesting
Files Changed
src/capture/capture_protocol.hpp- New message typessrc/capture/vk_layer/vk_capture.cpp- Semaphore creation and syncsrc/capture/vk_layer/ipc_socket.cpp- FD transfersrc/capture/capture_receiver.cpp- Receive semaphoressrc/render/backend/vulkan_backend.cpp- Import and use semaphorestests/capture/test_capture_protocol.cpp- Protocol testsSummary by CodeRabbit
New Features
Documentation
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.