RenderDoc for Ruby
renderdoc provides Ruby bindings for RenderDoc's in-application API. Use it to capture a specific block of GPU work, schedule frame captures, annotate captures, and open the replay UI from a Ruby application.
The gem binds to RenderDoc but does not bundle, load, or inject RenderDoc itself.
Requirements
- Ruby 3.2 or newer
- RenderDoc with in-application API 1.6.0 or newer
- Linux or Windows
The only runtime gem dependency is ffi, which Bundler installs automatically. RenderDoc does not officially support macOS, so RenderDoc.available? always returns false there. Use Xcode GPU Frame Capture for Metal applications.
Installation
Add the gem to your Gemfile:
gem "renderdoc"
Until the first RubyGems release, install it directly from GitHub:
gem "renderdoc", github: "ydah/renderdoc"
Then run:
bundle install
Loading RenderDoc
ffi lets Ruby call native functions; it does not place librenderdoc.so or renderdoc.dll in the process. The RenderDoc library must already be loaded before this gem can use its API.
| Platform | How to load RenderDoc |
|---|---|
| Linux | Launch the application from RenderDoc, or use LD_PRELOAD |
| Windows | Launch the application from RenderDoc so renderdoc.dll is injected |
| macOS | Unsupported by RenderDoc |
For a direct Linux launch:
LD_PRELOAD=librenderdoc.so bundle exec ruby app.rb
Use an absolute library path if librenderdoc.so is not on the dynamic loader's search path:
LD_PRELOAD=/path/to/librenderdoc.so bundle exec ruby app.rb
When the application is launched from the RenderDoc UI, do not also set LD_PRELOAD. Requiring this gem without RenderDoc attached is safe: RenderDoc.available? returns false.
Quick start
require "renderdoc"
if RenderDoc.available?
RenderDoc.capture_path_template = "captures/my_app"
RenderDoc.capture(title: "Bloom pass") do
renderer.render(scene, camera)
end
end
RenderDoc.capture returns the block's result. If the block raises an exception, the gem discards the capture and re-raises the original exception. Nested and overlapping captures raise RenderDoc::CaptureError.
Capturing frames
Capture a block
By default, RenderDoc uses wildcard device and window pointers. Supply native pointers when a particular device or window must be selected:
RenderDoc.capture(device: device_pointer, window: window_pointer) do
renderer.render(scene, camera)
end
For Vulkan, device must be the dispatch-table pointer described by RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE, not the VkInstance handle itself.
Trigger upcoming frames
Schedule one or more captures without wrapping render code:
RenderDoc.trigger_capture
RenderDoc.trigger_capture(frames: 3)
Captures and replay UI
RenderDoc.api_version
# => [1, 6, 0] or a newer compatible version
RenderDoc.captures.each do |capture|
puts "#{capture.path} at #{capture..iso8601}"
end
RenderDoc.comments = "scene=atrium commit=abc123"
RenderDoc.set_capture_comments("regression case", path: "/tmp/frame.rdc")
pid = RenderDoc.launch_replay_ui(connect: true)
RenderDoc.show_replay_ui if RenderDoc.target_control_connected?
Capture#timestamp is a UTC Time. Paths are UTF-8 strings reported by RenderDoc; a listed capture may have been deleted after it was recorded.
Options, overlay, and keys
RenderDoc. = false
RenderDoc.capture_keys = [:f12]
RenderDoc.focus_toggle_keys = [:f11]
RenderDoc.option(:api_validation, true)
RenderDoc.option(:delay_for_debugger, 3)
RenderDoc.option_value(:api_validation)
Option names from the API 1.6.0 header are accepted in snake case. Input buttons include function keys, letters, digits, navigation keys, :print_screen, and :pause. Unknown names and invalid values raise ArgumentError before entering FFI.
Running without RenderDoc
By default, API calls other than available? raise RenderDoc::NotAttachedError when RenderDoc is absent. Applications where capture support is entirely optional can enable no-op behavior:
RenderDoc.configure(unavailable: :noop)
RenderDoc.capture { renderer.render(scene, camera) } # still renders
RenderDoc.trigger_capture # => false
RenderDoc.captures # => []
| Error | Meaning |
|---|---|
RenderDoc::NotAttachedError |
RenderDoc is not loaded in the process |
RenderDoc::VersionError |
The loaded RenderDoc cannot provide API 1.6.0 |
RenderDoc::CaptureError |
A capture failed or another capture is in progress |
Frame-loop integration
RenderDoc::FrameTrigger parses STAGECRAFT_RENDERDOC=frame:N and triggers exactly once. A frame loop such as stagecraft's can soft-require the gem and continue working when it is not installed:
begin
require "renderdoc"
renderdoc_trigger = RenderDoc::FrameTrigger.from_environment
rescue LoadError
renderdoc_trigger = nil
end
frame_number = 0
loop do
renderdoc_trigger&.tick(frame_number)
renderer.render
frame_number += 1
end
For example, set STAGECRAFT_RENDERDOC=frame:120 before launching the application through RenderDoc to capture frame 120.
RSpec failure captures
Require the optional integration from spec_helper.rb:
require "renderdoc/rspec"
Tag examples whose render work can safely run a second time:
it "renders the bloom pass", :renderdoc_on_failure do
render_and_compare
end
When a tagged example fails and RenderDoc is attached, the integration runs the example body once more inside RenderDoc.capture, preserves the original failure, and reports the resulting .rdc path. Do not use the tag for examples with non-repeatable external side effects.
Development
Run the unit suite:
bundle exec rake
The Linux integration spec is tagged :renderdoc and excluded from the default suite. CI runs it under renderdoccmd, Xvfb, SDL2, and Mesa software OpenGL, then verifies the generated .rdc file through the in-application API.
The ABI constants in lib/renderdoc/native/generated.rb are generated from RenderDoc v1.25's renderdoc_app.h. After reviewing an upstream header change, regenerate them with:
ruby script/generate_native_api.rb path/to/renderdoc_app.h lib/renderdoc/native/generated.rb
License
The gem is available under the MIT License. RenderDoc is a separate project and is not distributed with this gem.