Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  renderdoc.rs

  Sprache: Rust
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


//! On-demand integration with the RenderDoc in-application capture API.
//!
//! A capture is armed via a `DebugCommand::CaptureRenderDoc` (driven from the
//! WebRender debugger / wrshell); the next composited frame is then wrapped in a
//! RenderDoc frame capture and the path of the written `.rdc` is returned.
//!
//! RenderDoc hooks OpenGL at library-load time, so `librenderdoc` must be loaded
//! *before* the GL context is created. In practice this means launching the
//! host process (e.g. Firefox) with `LD_PRELOAD=.../librenderdoc.so`. We resolve
//! the already-loaded library with `RTLD_NOLOAD` and use the in-app API entry
//! point; we deliberately do not load a fresh copy, since a late load cannot
//! install the GL hooks.

use std::ffi::{c_void, CStr, CString};
use std::os::raw::{c_char, c_int};
use std::path::PathBuf;
use std::ptr;

/// Opaque handle to the active graphics device / window. Passing null for both
/// tells RenderDoc to capture whatever device and window are currently active,
/// which is what we want for WebRender's single GL context.
type DevicePointer = *mut c_void;
type WindowHandle = *mut c_void;

type SetCaptureOptionU32Fn = unsafe extern "C" fn(option: u32, val: u32) -> c_int;
type SetCaptureFilePathTemplateFn = unsafe extern "C" fn(path: *const c_char);

// RENDERDOC_CaptureOption values from renderdoc_app.h.
/// Capture all live resources, even those not referenced by the active frame.
const RENDERDOC_OPTION_REF_ALL_RESOURCES: u32 = 8;
type GetNumCapturesFn = unsafe extern "C" fn() -> u32;
type GetCaptureFn = unsafe extern "C" fn(
    idx: u32,
    filename: *mut c_char,
    path_length: *mut u32,
    timestamp: *mut u64,
) -> u32;
type StartFrameCaptureFn = unsafe extern "C" fn(device: DevicePointer, window: WindowHandle);
type EndFrameCaptureFn = unsafe extern "C" fn(device: DevicePointer, window: WindowHandle) -> u32;

/// `eRENDERDOC_API_Version_1_1_2`. We only request this version, so any newer
/// `librenderdoc` returns a struct whose prefix matches the layout below (the
/// RenderDoc ABI is append-only and never reorders existing members).
const RENDERDOC_API_VERSION_1_1_2: c_int = 10102;

/// Mirror of `RENDERDOC_API_1_1_2` from `renderdoc_app.h`. Each member is a
/// function pointer; we only spell out the signatures we actually call and
/// leave the rest as opaque pointers, which keeps the (pointer-sized) layout
/// correct without committing to signatures we don't need.
#[repr(C)]
#[allow(non_snake_case, dead_code)]
struct RenderDocApi {
    GetAPIVersion: *const c_void,
    SetCaptureOptionU32: SetCaptureOptionU32Fn,
    SetCaptureOptionF32: *const c_void,
    GetCaptureOptionU32: *const c_void,
    GetCaptureOptionF32: *const c_void,
    SetFocusToggleKeys: *const c_void,
    SetCaptureKeys: *const c_void,
    GetOverlayBits: *const c_void,
    MaskOverlayBits: *const c_void,
    Shutdown: *const c_void,
    UnloadCrashHandler: *const c_void,
    SetCaptureFilePathTemplate: SetCaptureFilePathTemplateFn,
    GetCaptureFilePathTemplate: *const c_void,
    GetNumCaptures: GetNumCapturesFn,
    GetCapture: GetCaptureFn,
    TriggerCapture: *const c_void,
    IsTargetControlConnected: *const c_void,
    LaunchReplayUI: *const c_void,
    SetActiveWindow: *const c_void,
    StartFrameCapture: StartFrameCaptureFn,
    IsFrameCapturing: *const c_void,
    EndFrameCapture: EndFrameCaptureFn,
    TriggerMultiFrameCapture: *const c_void,
}

type GetApiFn = unsafe extern "C" fn(version: c_int, out: *mut *mut c_void) -> c_int;

#[cfg(unix)]
const RTLD_NOLOAD: i32 = 0x4;

#[cfg(all(unix, not(target_os = "android")))]
const RENDERDOC_LIB: &str = "librenderdoc.so";
#[cfg(target_os = "android")]
const RENDERDOC_LIB: &str = "libVkLayer_GLES_RenderDoc.so";
#[cfg(windows)]
const RENDERDOC_LIB: &str = "renderdoc.dll";

/// A loaded RenderDoc API. The function table pointer is owned by RenderDoc and
/// stays valid for the lifetime of the loaded library, which we keep alive via
/// `_lib`.
struct RenderDocApiHandle {
    api: *const RenderDocApi,
    _lib: libloading::Library,
}

// The render thread is the only user, and the underlying pointer is owned by
// RenderDoc for the lifetime of the process.
unsafe impl Send for RenderDocApiHandle {}

#[cfg(unix)]
fn load_library() -> Result<libloading::Library, libloading::Error> {
    use libloading::os::unix::Library;
    // Only pick up an already-loaded library (injected via LD_PRELOAD); RenderDoc
    // cannot hook GL if loaded after the GL driver, so we don't load it fresh.
    unsafe { Library::open(Some(RENDERDOC_LIB), libloading::os::unix::RTLD_NOW | RTLD_NOLOAD) }
        .map(|lib| lib.into())
}

#[cfg(windows)]
fn load_library() -> Result<libloading::Library, libloading::Error> {
    libloading::os::windows::Library::open_already_loaded(RENDERDOC_LIB).map(|lib| lib.into())
}

fn load_api() -> Result<RenderDocApiHandle, String> {
    let lib = load_library().map_err(|e| {
        format!("{} not loaded ({:?}); launch with LD_PRELOAD={}", RENDERDOC_LIB, e, RENDERDOC_LIB)
    })?;

    let get_api: libloading::Symbol<GetApiFn> = unsafe { lib.get(b"RENDERDOC_GetAPI\0") }
        .map_err(|e| format!("RENDERDOC_GetAPI not found in {}: {:?}", RENDERDOC_LIB, e))?;

    let mut api: *mut c_void = ptr::null_mut();
    let ret = unsafe { get_api(RENDERDOC_API_VERSION_1_1_2, &mut api) };
    if ret != 1 || api.is_null() {
        return Err(format!("RENDERDOC_GetAPI returned {}", ret));
    }

    Ok(RenderDocApiHandle {
        api: api as *const RenderDocApi,
        _lib: lib,
    })
}

/// Drives on-demand RenderDoc frame captures for the WebRender renderer.
pub struct RenderDocCapture {
    handle: Option<RenderDocApiHandle>,
    /// Set when a capture has been requested; consumed by the next render.
    capture_next: bool,
    /// Number of captures RenderDoc had written when the current capture began,
    /// used to detect whether the capture actually produced a new file.
    captures_before: u32,
}

impl RenderDocCapture {
    /// Probe for an already-loaded librenderdoc. Always returns a value; if the
    /// library isn't present, captures will report a helpful error instead.
    pub fn new() -> Self {
        let handle = match load_api() {
            Ok(handle) => {
                info!("RenderDoc: in-app API available");
                // Capture the current contents of all resources so that WebRender's
                // persistent picture-cache tiles (rasterized in earlier frames) replay
                // correctly, even though the captured frame only re-composites them.
                // (SaveAllInitials is implicitly always-on since RenderDoc v1.1.)
                unsafe {
                    ((*handle.api).SetCaptureOptionU32)(RENDERDOC_OPTION_REF_ALL_RESOURCES, 1);
                }
                if let Ok(path) = std::env::var("WR_RENDERDOC_CAPTURE_PATH") {
                    if let Ok(cpath) = CString::new(path.clone()) {
                        unsafe {
                            ((*handle.api).SetCaptureFilePathTemplate)(cpath.as_ptr());
                        }
                        info!("RenderDoc: capture file path template set to {:?}", path);
                    }
                }
                Some(handle)
            }
            Err(reason) => {
                info!("RenderDoc: in-app API unavailable ({})", reason);
                None
            }
        };

        RenderDocCapture {
            handle,
            capture_next: false,
            captures_before: 0,
        }
    }

    /// Request that the next composited frame be captured.
    pub fn arm(&mut self) {
        self.capture_next = true;
    }

    /// Whether the library was found (i.e. captures can succeed).
    pub fn is_available(&self) -> bool {
        self.handle.is_some()
    }

    /// Consume any pending capture request, returning whether the upcoming
    /// frame should be wrapped in a capture.
    pub fn take_request(&mut self) -> bool {
        let armed = self.capture_next && self.handle.is_some();
        self.capture_next = false;
        armed
    }

    /// Begin a frame capture. Must be paired with [`Self::end`].
    pub fn start(&mut self) {
        if let Some(handle) = &self.handle {
            info!("RenderDoc: starting frame capture");
            self.captures_before = unsafe { ((*handle.api).GetNumCaptures)() };
            unsafe {
                ((*handle.api).StartFrameCapture)(ptr::null_mut(), ptr::null_mut());
            }
        }
    }

    /// End a frame capture started with [`Self::start`], returning the path of
    /// the written `.rdc` file on success.
    pub fn end(&mut self) -> Option<PathBuf> {
        let before = self.captures_before;
        let handle = self.handle.as_ref()?;
        unsafe { ((*handle.api).EndFrameCapture)(ptr::null_mut(), ptr::null_mut()) };

        let after = unsafe { ((*handle.api).GetNumCaptures)() };
        if after <= before {
            warn!(
                "RenderDoc: capture produced no file (no active GL capture — is the \
                 host launched with LD_PRELOAD=librenderdoc.so?)"
            );
            return None;
        }
        let path = unsafe { capture_path(handle, after - 1) };
        if let Some(ref p) = path {
            info!("RenderDoc: frame capture written to {:?}", p);
        }
        path
    }
}

/// Query RenderDoc for the path of the capture at `idx`.
unsafe fn capture_path(handle: &RenderDocApiHandle, idx: u32) -> Option<PathBuf> {
    // First call with a null buffer to discover the length (incl. null terminator).
    let mut len: u32 = 0;
    ((*handle.api).GetCapture)(idx, ptr::null_mut(), &mut len, ptr::null_mut());
    if len == 0 {
        return None;
    }
    let mut buf = vec![0u8; len as usize];
    if ((*handle.api).GetCapture)(idx, buf.as_mut_ptr() as *mut c_char, &mut len, ptr::null_mut()) != 1 {
        return None;
    }
    let cstr = CStr::from_ptr(buf.as_ptr() as *const c_char);
    Some(PathBuf::from(cstr.to_string_lossy().into_owned()))
}

Messung V0.5 in Prozent
C=84 H=94 G=88

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002