Created
August 21, 2025 00:12
-
-
Save Flopgop/c59f7a12084ff394e269ec727410ddc3 to your computer and use it in GitHub Desktop.
Java Foreign wrapper for the RenderDoc API ( https://github.com/baldurk/renderdoc/blob/v1.x/renderdoc/api/app/renderdoc_app.h ). Licensed identically to the original RenderDoc header as they're basically the same.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.renderdoc.api; | |
/****************************************************************************** | |
* The MIT License (MIT) | |
* | |
* Copyright (c) 2019-2025 Baldur Karlsson | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
******************************************************************************/ | |
import org.jetbrains.annotations.NotNull; | |
import org.jetbrains.annotations.Nullable; | |
import java.lang.foreign.*; | |
import java.lang.invoke.MethodHandle; | |
import java.nio.charset.StandardCharsets; | |
@SuppressWarnings("UnusedReturnValue") | |
public class RenderDoc implements AutoCloseable { | |
@Override | |
public void close() { | |
removeHooks(); | |
} | |
// API version changelog: | |
// | |
// 1.0.0 - initial release | |
// 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered | |
// by keypress or TriggerCapture, instead of Start/EndFrameCapture. | |
// 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation | |
// 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new | |
// function pointer is added to the end of the struct, the original layout is identical | |
// 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote | |
// replay/remote server concept in replay UI) | |
// 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these | |
// are captures and not debug logging files. This is the first API version in the v1.0 | |
// branch. | |
// 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be | |
// displayed in the UI program on load. | |
// 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions | |
// which allows users to opt-in to allowing unsupported vendor extensions to function. | |
// Should be used at the user's own risk. | |
// Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to | |
// eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to | |
// 0xdddddddd of uninitialised buffer contents. | |
// 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop | |
// capturing without saving anything to disk. | |
// 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening | |
// 1.4.2 - Refactor: Renamed 'draws' to 'actions' in callstack capture option. | |
// 1.5.0 - Added feature: ShowReplayUI() to request that the replay UI show itself if connected | |
// 1.6.0 - Added feature: SetCaptureTitle() which can be used to set a title for a | |
// capture made with StartFrameCapture() or EndFrameCapture() | |
public enum KnownVersion { | |
API_1_0_0(10000), | |
API_1_0_1(10001), | |
API_1_0_2(10002), | |
API_1_1_0(10100), | |
API_1_1_1(10101), | |
API_1_1_2(10102), | |
API_1_2_0(10200), | |
API_1_3_0(10300), | |
API_1_4_0(10400), | |
API_1_4_1(10401), | |
API_1_4_2(10402), | |
API_1_5_0(10500), | |
API_1_6_0(10600), | |
; | |
final int value; | |
KnownVersion(int value) { | |
this.value = value; | |
} | |
} | |
public enum CaptureOption { | |
// Allow the application to enable vsync | |
// | |
// Default - enabled | |
// | |
// 1 - The application can enable or disable vsync at will | |
// 0 - vsync is force disabled | |
ALLOW_VSYNC(0), | |
// Allow the application to enable fullscreen | |
// | |
// Default - enabled | |
// | |
// 1 - The application can enable or disable fullscreen at will | |
// 0 - fullscreen is force disabled | |
ALLOW_FULLSCREEN(1), | |
// Record API debugging events and messages | |
// | |
// Default - disabled | |
// | |
// 1 - Enable built-in API debugging features and records the results into | |
// the capture, which is matched up with events on replay | |
// 0 - no API debugging is forcibly enabled | |
API_VALIDATION(2), | |
@Deprecated DEBUG_DEVICE_MODE(2), | |
// Capture CPU callstacks for API events | |
// | |
// Default - disabled | |
// | |
// 1 - Enables capturing of callstacks | |
// 0 - no callstacks are captured | |
CAPTURE_CALLSTACKS(3), | |
// When capturing CPU callstacks, only capture them from actions. | |
// This option does nothing without the above option being enabled | |
// | |
// Default - disabled | |
// | |
// 1 - Only captures callstacks for actions. | |
// Ignored if CaptureCallstacks is disabled | |
// 0 - Callstacks, if enabled, are captured for every event. | |
CAPTURE_CALLSTACKS_ONLY_DRAWS(4), | |
CAPTURE_CALLSTACKS_ONLY_ACTIONS(4), | |
// Specify a delay in seconds to wait for a debugger to attach, after | |
// creating or injecting into a process, before continuing to allow it to run. | |
// | |
// 0 indicates no delay, and the process will run immediately after injection | |
// | |
// Default - 0 seconds | |
// | |
DELAY_FOR_DEBUGGER(5), | |
// Verify buffer access. This includes checking the memory returned by a Map() call to | |
// detect any out-of-bounds modification, as well as initialising buffers with undefined contents | |
// to a marker value to catch use of uninitialised memory. | |
// | |
// NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do | |
// not do the same kind of interception & checking and undefined contents are really undefined. | |
// | |
// Default - disabled | |
// | |
// 1 - Verify buffer access | |
// 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in | |
// RenderDoc. | |
VERIFY_BUFFER_ACCESS(6), | |
// The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites. | |
// This option now controls the filling of uninitialised buffers with 0xdddddddd which was | |
// previously always enabled | |
@Deprecated VERIFY_MAP_WRITES(6), | |
// Hooks any system API calls that create child processes, and injects | |
// RenderDoc into them recursively with the same options. | |
// | |
// Default - disabled | |
// | |
// 1 - Hooks into spawned child processes | |
// 0 - Child processes are not hooked by RenderDoc | |
HOOK_INTO_CHILDREN(7), | |
// By default, RenderDoc only includes resources in the final capture necessary | |
// for that frame, this allows you to override that behaviour. | |
// | |
// Default - disabled | |
// | |
// 1 - all live resources at the time of capture are included in the capture | |
// and available for inspection | |
// 0 - only the resources referenced by the captured frame are included | |
REF_ALL_RESOURCES(8), | |
// **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or | |
// getting it will be ignored, to allow compatibility with older versions. | |
// In v1.1 the option acts as if it's always enabled. | |
// | |
// By default, RenderDoc skips saving initial states for resources where the | |
// previous contents don't appear to be used, assuming that writes before | |
// reads indicate previous contents aren't used. | |
// | |
// Default - disabled | |
// | |
// 1 - initial contents at the start of each captured frame are saved, even if | |
// they are later overwritten or cleared before being used. | |
// 0 - unless a read is detected, initial contents will not be saved and will | |
// appear as black or empty data. | |
@Deprecated SAVE_ALL_INITIALS(9), | |
// In APIs that allow for the recording of command lists to be replayed later, | |
// RenderDoc may choose to not capture command lists before a frame capture is | |
// triggered, to reduce overheads. This means any command lists recorded once | |
// and replayed many times will not be available and may cause a failure to | |
// capture. | |
// | |
// NOTE: This is only true for APIs where multithreading is difficult or | |
// discouraged. Newer APIs like Vulkan and D3D12 will ignore this option | |
// and always capture all command lists since the API is heavily oriented | |
// around it and the overheads have been reduced by API design. | |
// | |
// 1 - All command lists are captured from the start of the application | |
// 0 - Command lists are only captured if their recording begins during | |
// the period when a frame capture is in progress. | |
CAPTURE_ALL_CMD_LISTS(10), | |
// Mute API debugging output when the API validation mode option is enabled | |
// | |
// Default - enabled | |
// | |
// 1 - Mute any API debug messages from being displayed or passed through | |
// 0 - API debugging is displayed as normal | |
DEBUG_OUTPUT_MUTE(11), | |
// Option to allow vendor extensions to be used even when they may be | |
// incompatible with RenderDoc and cause corrupted replays or crashes. | |
// | |
// Default - inactive | |
// | |
// No values are documented, this option should only be used when absolutely | |
// necessary as directed by a RenderDoc developer. | |
ALLOW_UNSUPPORTED_VENDOR_EXTENSIONS(12), | |
// Define a soft memory limit which some APIs may aim to keep overhead under where | |
// possible. Anything above this limit will where possible be saved directly to disk during | |
// capture. | |
// This will cause increased disk space use (which may cause a capture to fail if disk space is | |
// exhausted) as well as slower capture times. | |
// | |
// Not all memory allocations may be deferred like this so it is not a guarantee of a memory | |
// limit. | |
// | |
// Units are in MBs, suggested values would range from 200MB to 1000MB. | |
// | |
// Default - 0 Megabytes | |
SOFT_MEMORY_LIMIT(13) | |
; | |
final int value; | |
CaptureOption(int value) { | |
this.value = value; | |
} | |
} | |
public enum OverlayBits { | |
ENABLED(0x1), | |
FRAMERATE(0x2), | |
FRAME_NUMBER(0x4), | |
CAPTURE_LIST(0x8), | |
DEFAULT(ENABLED.value | FRAMERATE.value | FRAME_NUMBER.value | CAPTURE_LIST.value), | |
ALL(0x7ffffff), | |
NONE(0) | |
; | |
final int value; | |
OverlayBits(int value) { | |
this.value = value; | |
} | |
public int bits() { | |
return value; | |
} | |
} | |
public enum InputButton { | |
KEY_0(0x30), | |
KEY_1(0x31), | |
KEY_2(0x32), | |
KEY_3(0x33), | |
KEY_4(0x34), | |
KEY_5(0x35), | |
KEY_6(0x36), | |
KEY_7(0x37), | |
KEY_8(0x38), | |
KEY_9(0x39), | |
KEY_A(0x41), | |
KEY_B(0x42), | |
KEY_C(0x43), | |
KEY_D(0x44), | |
KEY_E(0x45), | |
KEY_F(0x46), | |
KEY_G(0x47), | |
KEY_H(0x48), | |
KEY_I(0x49), | |
KEY_J(0x4a), | |
KEY_K(0x4b), | |
KEY_L(0x4c), | |
KEY_M(0x4d), | |
KEY_N(0x4e), | |
KEY_O(0x4f), | |
KEY_P(0x50), | |
KEY_Q(0x51), | |
KEY_R(0x52), | |
KEY_S(0x53), | |
KEY_T(0x54), | |
KEY_U(0x55), | |
KEY_V(0x56), | |
KEY_W(0x57), | |
KEY_X(0x58), | |
KEY_Y(0x59), | |
KEY_Z(0x5a), | |
KEY_NONPRINTABLE(0x100), | |
KEY_DIVIDE(0x101), | |
KEY_MULTIPLY(0x102), | |
KEY_SUBTRACT(0x103), | |
KEY_PLUS(0x104), | |
KEY_F1(0x105), | |
KEY_F2(0x106), | |
KEY_F3(0x107), | |
KEY_F4(0x108), | |
KEY_F5(0x109), | |
KEY_F6(0x110), | |
KEY_F7(0x111), | |
KEY_F8(0x112), | |
KEY_F9(0x113), | |
KEY_F10(0x114), | |
KEY_F11(0x115), | |
KEY_F12(0x116), | |
KEY_HOME(0x117), | |
KEY_END(0x118), | |
KEY_INSERT(0x119), | |
KEY_DELETE(0x120), | |
KEY_PAGEUP(0x121), | |
KEY_PAGEDN(0x122), | |
KEY_BACKSPACE(0x123), | |
KEY_TAB(0x124), | |
KEY_PRTSCRN(0x125), | |
KEY_PAUSE(0x126), | |
KEY_MAX(0x127), | |
; | |
final int value; | |
InputButton(int value) { | |
this.value = value; | |
} | |
} | |
public record Version(int major, int minor, int patch) { | |
@NotNull | |
@Override | |
public String toString() { | |
return major + "." + minor + "." + patch; | |
} | |
} | |
private static final Linker LINKER; | |
private static final SymbolLookup DEFAULT_LOOKUP; | |
static { | |
System.loadLibrary("renderdoc"); | |
LINKER = Linker.nativeLinker(); | |
DEFAULT_LOOKUP = SymbolLookup.loaderLookup(); | |
} | |
private static final int API_FUNCTION_COUNT = 27; | |
private final MemorySegment apiStructPtr; | |
private final MethodHandle hGetAPIVersion; // 0; addr, addr, addr -> void | |
private final MethodHandle hSetCaptureOptionU32; // 1; int, int -> int | |
private final MethodHandle hSetCaptureOptionF32; // 2; int, float -> int | |
private final MethodHandle hGetCaptureOptionU32; // 3; int -> int | |
private final MethodHandle hGetCaptureOptionF32; // 4; int -> float | |
private final MethodHandle hSetFocusToggleKeys; // 5; addr, int -> void | |
private final MethodHandle hSetCaptureKeys; // 6; addr, int -> void | |
private final MethodHandle hGetOverlayBits; // 7; void -> int | |
private final MethodHandle hMaskOverlayBits; // 8; int, int -> void | |
private final MethodHandle hRemoveHooks; // 9; void -> void | |
private final MethodHandle hUnloadCrashHandler; // 10; void -> void | |
private final MethodHandle hSetCaptureFilePathTemplate; // 11; String -> void | |
private final MethodHandle hGetCaptureFilePathTemplate; // 12; void -> String | |
private final MethodHandle hGetNumCaptures; // 13; void -> int | |
private final MethodHandle hGetCapture; // 14; int, addr, addr, addr -> int | |
private final MethodHandle hTriggerCapture; // 15; void -> void | |
private final MethodHandle hIsTargetControlConnected; // 16; void -> int | |
private final MethodHandle hLaunchReplayUI; // 17; int, addr, addr -> int | |
private final MethodHandle hSetActiveWindow; // 18; addr, addr -> void | |
private final MethodHandle hStartFrameCapture; // 19; addr, addr -> void | |
private final MethodHandle hIsFrameCapturing; // 20; void -> int | |
private final MethodHandle hEndFrameCapture; // 21; addr, addr -> int | |
// since 1.1.0 | |
private @Nullable MethodHandle hTriggerMultiFrameCapture; // 22; int -> void | |
// since 1.2.0 | |
private @Nullable MethodHandle hSetCaptureFileComments; // 23; addr, addr -> void | |
// since 1.4.0 | |
private @Nullable MethodHandle hDiscardFrameCapture; // 24; addr, addr -> void | |
// since 1.5.0 | |
private @Nullable MethodHandle hShowReplayUI; // 25; void -> int | |
// since 1.6.0 | |
private @Nullable MethodHandle hSetCaptureTitle; // 26; addr -> void | |
private final int version; | |
private RenderDoc(KnownVersion version, MemorySegment apiStructPtr) { | |
this.version = version.value; | |
this.apiStructPtr = apiStructPtr; | |
this.hGetAPIVersion = downcallIndex(0, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
this.hSetCaptureOptionU32 = downcallIndex(1, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)); | |
this.hSetCaptureOptionF32 = downcallIndex(2, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_FLOAT)); | |
this.hGetCaptureOptionU32 = downcallIndex(3, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)); | |
this.hGetCaptureOptionF32 = downcallIndex(4, FunctionDescriptor.of(ValueLayout.JAVA_FLOAT, ValueLayout.JAVA_INT)); | |
this.hSetFocusToggleKeys = downcallIndex(5, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT)); | |
this.hSetCaptureKeys = downcallIndex(6, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT)); | |
this.hGetOverlayBits = downcallIndex(7, FunctionDescriptor.of(ValueLayout.JAVA_INT)); | |
this.hMaskOverlayBits = downcallIndex(8, FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)); | |
this.hRemoveHooks = downcallIndex(9, FunctionDescriptor.ofVoid()); | |
this.hUnloadCrashHandler = downcallIndex(10, FunctionDescriptor.ofVoid()); | |
this.hSetCaptureFilePathTemplate = downcallIndex(11, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); | |
this.hGetCaptureFilePathTemplate = downcallIndex(12, FunctionDescriptor.of(ValueLayout.ADDRESS)); | |
this.hGetNumCaptures = downcallIndex(13, FunctionDescriptor.of(ValueLayout.JAVA_INT)); | |
this.hGetCapture = downcallIndex(14, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
this.hTriggerCapture = downcallIndex(15, FunctionDescriptor.ofVoid()); | |
this.hIsTargetControlConnected = downcallIndex(16, FunctionDescriptor.of(ValueLayout.JAVA_INT)); | |
this.hLaunchReplayUI = downcallIndex(17, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
this.hSetActiveWindow = downcallIndex(18, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
this.hStartFrameCapture = downcallIndex(19, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
this.hIsFrameCapturing = downcallIndex(20, FunctionDescriptor.of(ValueLayout.JAVA_INT)); | |
this.hEndFrameCapture = downcallIndex(21, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
if (this.version < KnownVersion.API_1_1_0.value) return; | |
this.hTriggerMultiFrameCapture = downcallIndex(22, FunctionDescriptor.ofVoid(ValueLayout.JAVA_INT)); | |
if (this.version < KnownVersion.API_1_2_0.value) return; | |
this.hSetCaptureFileComments = downcallIndex(23, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
if (this.version < KnownVersion.API_1_4_0.value) return; | |
this.hDiscardFrameCapture = downcallIndex(24, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS)); | |
if (this.version < KnownVersion.API_1_5_0.value) return; | |
this.hShowReplayUI = downcallIndex(25, FunctionDescriptor.of(ValueLayout.JAVA_INT)); | |
if (this.version < KnownVersion.API_1_6_0.value) return; | |
this.hSetCaptureTitle = downcallIndex(26, FunctionDescriptor.ofVoid(ValueLayout.ADDRESS)); | |
} | |
private MemorySegment functionPointerAt(int idx) { | |
if (idx < 0 || idx >= API_FUNCTION_COUNT) throw new IndexOutOfBoundsException(idx); | |
long offset = idx * ValueLayout.ADDRESS.byteSize(); | |
return apiStructPtr.get(ValueLayout.ADDRESS, offset); | |
} | |
private MethodHandle downcallIndex(int idx, FunctionDescriptor fd) { | |
MemorySegment fnAddr = functionPointerAt(idx); | |
if (fnAddr == null || fnAddr.equals(MemorySegment.NULL)) | |
throw new UnsatisfiedLinkError("API function pointer at index " + idx + " is null! (Did you load the right version?)"); | |
return LINKER.downcallHandle(fnAddr, fd); | |
} | |
public static RenderDoc load(KnownVersion version) { | |
MemorySegment getApiSym = DEFAULT_LOOKUP.find("RENDERDOC_GetAPI") | |
.orElseThrow(() -> new UnsatisfiedLinkError("RENDERDOC_GetAPI symbol not found")); | |
MethodHandle getApiMH = LINKER.downcallHandle(getApiSym, FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS)); | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment out = arena.allocate(ValueLayout.ADDRESS.byteSize()); | |
int rc = (int) getApiMH.invoke(version.value, out); | |
if (rc != 1) { | |
throw new UnsatisfiedLinkError("RENDERDOC_GetAPI rejected version " + version); | |
} | |
MemorySegment apiPtr = out.get(ValueLayout.ADDRESS, 0); | |
if (apiPtr == null || apiPtr.equals(MemorySegment.NULL)) | |
throw new UnsatisfiedLinkError("RENDERDOC_GetAPI returned null pointer"); | |
long ptrSize = ValueLayout.ADDRESS.byteSize(); | |
long structBytes = API_FUNCTION_COUNT * ptrSize; | |
MemorySegment apiStruct = apiPtr.reinterpret(structBytes, Arena.global(), _ -> {}); | |
return new RenderDoc(version, apiStruct); | |
} catch (Throwable e) { | |
if (e instanceof UnsatisfiedLinkError u) throw u; | |
throw new RuntimeException(e); | |
} | |
} | |
public Version getApiVersion() { | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment major = arena.allocate(ValueLayout.JAVA_INT.byteSize()), minor = arena.allocate(ValueLayout.JAVA_INT.byteSize()), patch = arena.allocate(ValueLayout.JAVA_INT.byteSize()); | |
this.hGetAPIVersion.invoke(major, minor, patch); | |
return new Version(major.get(ValueLayout.JAVA_INT, 0), minor.get(ValueLayout.JAVA_INT, 0), patch.get(ValueLayout.JAVA_INT, 0)); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public int setCaptureOptionU32(CaptureOption option, int val) { | |
try { | |
return (int)this.hSetCaptureOptionU32.invoke(option.value, val); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public int setCaptureOptionF32(CaptureOption option, float val) { | |
try { | |
return (int)this.hSetCaptureOptionF32.invoke(option.value, val); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public int getCaptureOptionU32(CaptureOption option) { | |
try { | |
return (int)this.hGetCaptureOptionU32.invoke(option.value); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public float getCaptureOptionF32(CaptureOption option) { | |
try { | |
return (float)this.hGetCaptureOptionF32.invoke(option.value); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void setFocusToggleKeys(InputButton[] keys) { | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment array = arena.allocate(ValueLayout.JAVA_INT, keys.length); | |
for (int i = 0; i < keys.length ; i++) { | |
array.setAtIndex(ValueLayout.JAVA_INT, i, keys[i].value); | |
} | |
this.hSetFocusToggleKeys.invoke(array, keys.length); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void setCaptureKeys(InputButton[] keys) { | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment array = arena.allocate(ValueLayout.JAVA_INT, keys.length); | |
for (int i = 0; i < keys.length ; i++) { | |
array.setAtIndex(ValueLayout.JAVA_INT, i, keys[i].value); | |
} | |
this.hSetCaptureKeys.invoke(array, keys.length); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public int getOverlayBits() { | |
try { | |
return (int)this.hGetOverlayBits.invoke(); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void maskOverlayBits(int and, int or) { | |
try { | |
this.hMaskOverlayBits.invoke(and, or); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void removeHooks() { | |
try { | |
this.hRemoveHooks.invoke(); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Deprecated | |
public void shutdown() { | |
removeHooks(); | |
} | |
public void unloadCrashHandler() { | |
try { | |
this.hUnloadCrashHandler.invoke(); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void setCaptureFilePathTemplate(String template) { | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment str = arena.allocateFrom(template, StandardCharsets.UTF_8); | |
this.hSetCaptureFilePathTemplate.invoke(str); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Deprecated | |
public void setLogFilePathTemplate(String template) { | |
setCaptureFilePathTemplate(template); | |
} | |
public String getCaptureFilePathTemplate() { | |
try { | |
MemorySegment segment = (MemorySegment) this.hGetCaptureFilePathTemplate.invoke(); | |
return segment.getString(0, StandardCharsets.UTF_8); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Deprecated | |
public String getLogFilePathTemplate() { | |
return getCaptureFilePathTemplate(); | |
} | |
public int getNumCaptures() { | |
try { | |
return (int) this.hGetNumCaptures.invoke(); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public record Capture(boolean valid, String filename, long timestamp) {} | |
public Capture getCapture(int idx) { | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment lengthPtr = arena.allocate(ValueLayout.JAVA_INT); | |
this.hGetCapture.invoke(idx, MemorySegment.NULL, lengthPtr, MemorySegment.NULL); | |
int length = lengthPtr.get(ValueLayout.JAVA_INT, 0); | |
MemorySegment strPtr = arena.allocate(ValueLayout.JAVA_BYTE, length + 1); | |
MemorySegment timestampPtr = arena.allocate(ValueLayout.JAVA_LONG); | |
int valid = (int) this.hGetCapture.invoke(idx, strPtr, lengthPtr, timestampPtr); | |
return new Capture(valid == 1, strPtr.getString(0, StandardCharsets.UTF_8), timestampPtr.get(ValueLayout.JAVA_LONG, 0)); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void triggerCapture() { | |
try { | |
this.hTriggerCapture.invoke(); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public boolean isTargetControlConnected() { | |
try { | |
return ((int)this.hIsTargetControlConnected.invoke()) == 1; | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Deprecated | |
public boolean isRemoteAccessConnected() { | |
return isTargetControlConnected(); | |
} | |
public int launchReplayUI(boolean connectTargetControl, @Nullable String cmdLineParams) { | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment cmdLine = cmdLineParams == null ? MemorySegment.NULL : arena.allocateFrom(cmdLineParams, StandardCharsets.UTF_8); | |
return (int) this.hLaunchReplayUI.invoke(connectTargetControl ? 1 : 0, cmdLine); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void setActiveWindow(long devicePtr, long nativeWindowHandle) { | |
try { | |
this.hSetActiveWindow.invoke(MemorySegment.ofAddress(devicePtr), MemorySegment.ofAddress(nativeWindowHandle)); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void startFrameCapture(long devicePtr, long nativeWindowHandle) { | |
try { | |
this.hStartFrameCapture.invoke(MemorySegment.ofAddress(devicePtr), MemorySegment.ofAddress(nativeWindowHandle)); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public boolean isFrameCapturing() { | |
try { | |
return ((int)this.hIsFrameCapturing.invoke()) == 1; | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public boolean endFrameCapture(long devicePtr, long nativeWindowHandle) { | |
try { | |
return ((int)this.hEndFrameCapture.invoke(MemorySegment.ofAddress(devicePtr), MemorySegment.ofAddress(nativeWindowHandle)) == 1); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void triggerMultiFrameCapture(int numFrames) { | |
if (this.version < KnownVersion.API_1_1_0.value || hTriggerMultiFrameCapture == null) throw new UnsupportedOperationException("TriggerMultiFrameCapture requires RenderDoc >=1.1.0!"); | |
try { | |
this.hTriggerMultiFrameCapture.invoke(numFrames); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void setCaptureFileComments(String filePath, String comments) { | |
if (this.version < KnownVersion.API_1_2_0.value || hSetCaptureFileComments == null) throw new UnsupportedOperationException("SetCaptureFileComments requires RenderDoc >=1.2.0!"); | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment pathPtr = arena.allocateFrom(filePath, StandardCharsets.UTF_8); | |
MemorySegment commentPtr = arena.allocateFrom(comments, StandardCharsets.UTF_8); | |
this.hSetCaptureFileComments.invoke(pathPtr, commentPtr); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public boolean discardFrameCapture(long devicePtr, long nativeWindowHandle) { | |
if (this.version < KnownVersion.API_1_4_0.value || hDiscardFrameCapture == null) throw new UnsupportedOperationException("DiscardFrameCapture requires RenderDoc >=1.4.0!"); | |
try { | |
return ((int)this.hDiscardFrameCapture.invoke(MemorySegment.ofAddress(devicePtr), MemorySegment.ofAddress(nativeWindowHandle))) == 1; | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public boolean showReplayUI() { | |
if (this.version < KnownVersion.API_1_5_0.value || hShowReplayUI == null) throw new UnsupportedOperationException("ShowReplayUI requires RenderDoc >=1.5.0!"); | |
try { | |
return ((int)this.hShowReplayUI.invoke()) == 1; | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
public void setCaptureTitle(String title) { | |
if (this.version < KnownVersion.API_1_6_0.value || hSetCaptureTitle == null) throw new UnsupportedOperationException("SetCaptureTitle requires RenderDoc >=1.6.0!"); | |
try (Arena arena = Arena.ofConfined()) { | |
MemorySegment titlePtr = arena.allocateFrom(title, StandardCharsets.UTF_8); | |
this.hSetCaptureTitle.invoke(titlePtr); | |
} catch (Throwable e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment