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.
- 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.
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 installffi 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.rbUse 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.rbWhen 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.
require "renderdoc"
if RenderDoc.available?
RenderDoc.capture_path_template = "captures/my_app"
RenderDoc.capture(title: "Bloom pass") do
renderer.render(scene, camera)
end
endRenderDoc.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.
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)
endFor Vulkan, device must be the dispatch-table pointer described by RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE, not the VkInstance handle itself.
Schedule one or more captures without wrapping render code:
RenderDoc.trigger_capture
RenderDoc.trigger_capture(frames: 3)RenderDoc.api_version
# => [1, 6, 0] or a newer compatible version
RenderDoc.captures.each do |capture|
puts "#{capture.path} at #{capture.timestamp.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.
RenderDoc.overlay = 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.
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 |
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
endFor example, set STAGECRAFT_RENDERDOC=frame:120 before launching the application through RenderDoc to capture frame 120.
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
endWhen 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.
Run the unit suite:
bundle exec rakeThe 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.rbThe gem is available under the MIT License. RenderDoc is a separate project and is not distributed with this gem.