Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ option(onnxruntime_USE_AZURE "Build with azure inferencing support" OFF)
option(onnxruntime_USE_LOCK_FREE_QUEUE "Build with lock-free task queue for threadpool." OFF)
option(onnxruntime_FORCE_GENERIC_ALGORITHMS "Disable optimized arch-specific algorithms. Use only for testing and debugging generic algorithms." OFF)

# DX interop feature option
option(onnxruntime_USE_DX_INTEROP "Build with the DX Interop feature for graphics API synchronization." OFF)

if (onnxruntime_USE_DX_INTEROP)
add_compile_definitions(USE_DX_INTEROP=1)
else()
add_compile_definitions(USE_DX_INTEROP=0)
endif()

option(onnxruntime_USE_TENSORRT_INTERFACE "Build ONNXRuntime shared lib which is compatible with TensorRT EP interface" OFF)
option(onnxruntime_USE_NV_INTERFACE "Build ONNXRuntime shared lib which is compatible with NV EP interface" OFF)
option(onnxruntime_USE_CUDA_INTERFACE "Build ONNXRuntime shared lib which is compatible with Cuda EP interface" OFF)
Expand Down
4 changes: 2 additions & 2 deletions cmake/onnxruntime_providers_nv.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ endif ()
target_link_libraries(onnxruntime_providers_nv_tensorrt_rtx PRIVATE Eigen3::Eigen onnx flatbuffers::flatbuffers Boost::mp11 safeint_interface Eigen3::Eigen)
add_dependencies(onnxruntime_providers_nv_tensorrt_rtx onnxruntime_providers_shared ${onnxruntime_EXTERNAL_DEPENDENCIES})
if (onnxruntime_USE_TENSORRT_BUILTIN_PARSER)
target_link_libraries(onnxruntime_providers_nv_tensorrt_rtx PRIVATE ${trt_link_libs} ${ONNXRUNTIME_PROVIDERS_SHARED} ${PROTOBUF_LIB} flatbuffers::flatbuffers Boost::mp11 safeint_interface ${ABSEIL_LIBS} PUBLIC CUDA::cudart)
target_link_libraries(onnxruntime_providers_nv_tensorrt_rtx PRIVATE ${trt_link_libs} ${ONNXRUNTIME_PROVIDERS_SHARED} ${PROTOBUF_LIB} flatbuffers::flatbuffers Boost::mp11 safeint_interface ${ABSEIL_LIBS} PUBLIC CUDA::cudart CUDA::cuda_driver)
else()
target_link_libraries(onnxruntime_providers_nv_tensorrt_rtx PRIVATE ${onnxparser_link_libs} ${trt_link_libs} ${ONNXRUNTIME_PROVIDERS_SHARED} ${PROTOBUF_LIB} flatbuffers::flatbuffers ${ABSEIL_LIBS} PUBLIC CUDA::cudart)
target_link_libraries(onnxruntime_providers_nv_tensorrt_rtx PRIVATE ${onnxparser_link_libs} ${trt_link_libs} ${ONNXRUNTIME_PROVIDERS_SHARED} ${PROTOBUF_LIB} flatbuffers::flatbuffers ${ABSEIL_LIBS} PUBLIC CUDA::cudart CUDA::cuda_driver)
endif()
target_include_directories(onnxruntime_providers_nv_tensorrt_rtx PRIVATE ${ONNXRUNTIME_ROOT} ${CMAKE_CURRENT_BINARY_DIR} ${TENSORRT_RTX_INCLUDE_DIR} ${onnx_tensorrt_SOURCE_DIR}
PUBLIC ${CUDAToolkit_INCLUDE_DIRS})
Expand Down
100 changes: 100 additions & 0 deletions include/onnxruntime/core/session/onnxruntime_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,74 @@ typedef struct OrtExternalSemaphoreDescriptor {
void* native_handle; /**< Platform-specific handle (e.g., Windows HANDLE) */
} OrtExternalSemaphoreDescriptor;

/** \brief Graphics API type for interop configuration.
*
* Specifies the graphics API used for GPU interop with the execution provider.
* This enables synchronization between graphics workloads (e.g., rendering, compute shaders)
* and ONNX Runtime inference.
*
* \since Version 1.25.
*/
typedef enum OrtGraphicsApi {
ORT_GRAPHICS_API_NONE = 0, /**< No graphics interop (default) */
ORT_GRAPHICS_API_D3D12 = 1, /**< Direct3D 12 interop */
ORT_GRAPHICS_API_VULKAN = 2, /**< Vulkan interop */
} OrtGraphicsApi;

/** \brief Configuration for initializing graphics interop on an EP factory.
*
* This structure contains all parameters needed to set up graphics interop between
* ONNX Runtime and an external graphics API (D3D12, Vulkan). The factory stores this
* configuration and uses it when creating synchronization streams.
*
* Design rationale (following Scott McKay's suggestions):
* - Single init function with all required params to avoid multiple init signatures
* - Factory stores the context and uses it in stream creation
* - Supports extensibility via additional_options for future requirements
*
* Example usage for D3D12:
* \code
* OrtGraphicsInteropConfig config = {0};
* config.version = ORT_API_VERSION;
* config.graphics_api = ORT_GRAPHICS_API_D3D12;
* config.command_queue = my_d3d12_command_queue; // ID3D12CommandQueue*
* config.device = my_d3d12_device; // ID3D12Device* (optional)
* status = ep_factory->InitGraphicsInterop(ep_factory, ep_device, &config);
* \endcode
*
* \note The version field must be set to ORT_API_VERSION.
* This ensures forward compatibility as fields may be added in future versions.
*
* \since Version 1.25.
*/
typedef struct OrtGraphicsInteropConfig {
uint32_t version; /**< Must be ORT_API_VERSION */
OrtGraphicsApi graphics_api; /**< The graphics API to use for interop */

/** \brief Command queue/submission queue for graphics workloads.
*
* For D3D12: ID3D12CommandQueue*
* For Vulkan: VkQueue (cast to void*)
*
* The factory stores this and uses it for synchronization with inference streams.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: factory can do whatever it wants so I would expect storing or not is an implementation detail and not a requirement.

*/
void* command_queue;

/** \brief Graphics device handle (optional, may be inferred from command_queue).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't absolutely require a command_queue either right? That is more of a performance optimization.

This config shouldn't be required for the InteropApi to work

*
* For D3D12: ID3D12Device* (optional, can be obtained from command queue)
* For Vulkan: VkDevice (cast to void*)
*/
void* device;
Comment on lines +1076 to +1081
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the device be retrieved using info from the OrtEpDevice or does it need to be passed in? Prefer the former as there's no potential mismatch.


/** \brief Additional API-specific options (optional).
*
* Can be used for future extensibility without changing the struct layout.
* For example, Vulkan-specific queue family index, or D3D12 fence sharing flags.
*/
const OrtKeyValuePairs* additional_options;
} OrtGraphicsInteropConfig;

/** \brief Descriptor for creating a tensor from imported external memory.
*
* \note The version field must be set to ORT_API_VERSION.
Expand Down Expand Up @@ -7242,6 +7310,38 @@ struct OrtApi {
* \since Version 1.25.
*/
ORT_API2_STATUS(RunOptionsDisableProfiling, _Inout_ OrtRunOptions* options);

/** \brief Initialize graphics interop for an execution provider device.
*
* This function enables D3D12/Vulkan interoperability by creating a graphics interop context
* bound to the provided graphics command queue. Once initialized, any OrtSyncStream created for this
* ep_device via CreateSyncStreamForEpDevice will be created on the interop context, enabling efficient
* GPU-side synchronization between ONNX Runtime inference and graphics workloads.
*
* This must be called BEFORE CreateSyncStreamForEpDevice for the same ep_device.
Comment on lines +7317 to +7321
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These seem like they're potentially implementation specific details. Isn't it up to the EP factory what it does in the init and how that affects later calls?

Would also be good to explicitly call out that as this is tied to the OrtEpDevice instance it applies across all sessions the OrtEpDevice is used in (i.e. it's global not per-session).

*
* \param[in] ep_device The OrtEpDevice to initialize graphics interop for.
* \param[in] config Configuration specifying the graphics API (D3D12/Vulkan) and required handles.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
* \since Version 1.25.
*/
ORT_API2_STATUS(InitGraphicsInteropForEpDevice, _In_ const OrtEpDevice* ep_device,
_In_ const OrtGraphicsInteropConfig* config);
Comment on lines +7330 to +7331
Copy link
Contributor

@skottmckay skottmckay Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please add these two functions to the interop API struct instead of the global ORT API so we keep all the interop related pieces in one place?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, could this be a configuration that is "applied to" an OrtEpDevice's InteropApi? Rather than it being an independent thing.

I'm not sure we really need to qualify it as "Graphics" either, that is a bit more domain specific than this would afford.


/** \brief Deinitialize graphics interop for an execution provider device.
*
* This function cleans up the graphics interop context that was created by InitGraphicsInteropForEpDevice.
* Should be called when graphics interop is no longer needed for the ep_device.
*
* \param[in] ep_device The OrtEpDevice to deinitialize graphics interop for.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
* \since Version 1.25.
*/
ORT_API2_STATUS(DeinitGraphicsInteropForEpDevice, _In_ const OrtEpDevice* ep_device);
};

/*
Expand Down
50 changes: 50 additions & 0 deletions include/onnxruntime/core/session/onnxruntime_ep_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -2105,6 +2105,56 @@ struct OrtEpFactory {
*/
ORT_API2_STATUS(GetCustomOpDomains, _In_ OrtEpFactory* this_ptr,
_Out_writes_all_(num_domains) OrtCustomOpDomain** domains, _In_ size_t num_domains);

/** \brief Initialize graphics interop for the EP factory.
*
* This function sets up graphics interop context that enables synchronization between
* external graphics API workloads (D3D12, Vulkan) and ONNX Runtime inference.
*
* The factory stores the graphics context configuration and uses it when creating
* synchronization streams via CreateSyncStreamForDevice. This approach (suggested by
* Scott McKay) is more graceful than passing the command queue directly during stream creation.
*
* The implementation is EP-specific. EPs may create a specialized interop context using
* platform-specific APIs to enable GPU-GPU synchronization.
*
* Key design points:
* - Single init function with all required params (avoids multiple init signatures)
* - Factory stores context and uses it in stream creation
* - Paired with DeinitGraphicsInterop for cleanup
*
* \param[in] this_ptr The OrtEpFactory instance.
* \param[in] ep_device The OrtEpDevice to initialize graphics interop for.
* \param[in] config Configuration specifying the graphics API and required handles.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
* \note Implementation of this function is optional.
* EPs that don't support graphics interop should set this to nullptr or return ORT_NOT_IMPLEMENTED.
*
* \since Version 1.25.
*/
ORT_API2_STATUS(InitGraphicsInterop, _In_ OrtEpFactory* this_ptr,
_In_ const OrtEpDevice* ep_device,
_In_ const OrtGraphicsInteropConfig* config);

/** \brief Deinitialize graphics interop for the EP factory.
*
* This function cleans up any graphics interop context that was set up by InitGraphicsInterop.
* Should be called when graphics interop is no longer needed.
*
* \param[in] this_ptr The OrtEpFactory instance.
* \param[in] ep_device The OrtEpDevice to deinitialize graphics interop for.
*
* \snippet{doc} snippets.dox OrtStatus Return Value
*
* \note Implementation of this function is optional.
* EPs that don't support graphics interop should set this to nullptr or return ORT_NOT_IMPLEMENTED.
*
* \since Version 1.25.
*/
ORT_API2_STATUS(DeinitGraphicsInterop, _In_ OrtEpFactory* this_ptr,
_In_ const OrtEpDevice* ep_device);
};

#ifdef __cplusplus
Expand Down
Loading