First of all, congratulations for the OpenGL → Direct3D migration in Rhino 9.
I am developing a Grasshopper plugin which performs GPU spatial analysis with CUDA/OptiX and displays the results in the viewport through GPU interop. For this to work efficiently (zero-copy resources, no host round-trip), the plugin needs to access the same graphics device that Rhino itself uses for its display engine.
In Rhino 8, this was implicit: inside a DisplayConduit callback, the OpenGL context was already current on the calling thread, and any GL call would naturally target it. We could register VBOs with CUDA through cudaGraphicsGLRegisterBuffer without ever needing an explicit handle.
With Direct3D 11, this implicit model no longer applies — plugins need an actual ID3D11Device pointer to share resources with Rhino’s renderer. And as far as I can see, the public SDK does not currently expose it: neither RhinoCommon nor the C++ headers offer a documented way to retrieve the active device.
Would it be possible to envisage a public accessor for this? Even a minimal one returning ID3D11Device* (and ideally the immediate context) would restore for D3D11 plugins what was naturally available in Rhino 8.
If this is already planned, would you have an approximate idea of when it might land — even just “Rhino 9 release” vs “a later service release” would help me plan accordingly. And of course, if I have missed an existing mechanism, I would be grateful for a pointer.
I remain available to share more details about the use case if needed.
Thank you for your time, and have a good week ahead.
Laurent-Emmanuel
Pinging @stevebaer in case this is in your area — happy to move it elsewhere if there’s a better place to ask.
Hello @stevebaer, thank you so much for the quick reply — and even more for being open to exposing it.
Ideally both, but C++ would be the more valuable one to prioritize. The actual consumer of the ID3D11Device* in my plugin is cudaGraphicsD3D11RegisterResource, called from a native DLL (CUDA/OptiX) that does all the GPU work. A C++ SDK accessor would let that native code obtain the device directly, with no marshalling, no opaque pointer crossing the managed boundary, and no ambiguity about COM lifetime. It’s architecturally the cleanest path, and I imagine most plugins doing GPU interop are in a similar situation — the .NET side is mostly orchestration, the heavy lifting lives in native code.
A .NET accessor (returning an IntPtr that can be P/Invoked downstream) would also be welcome as a convenience for plugins that don’t have a C++ component, and since RhinoCommon typically wraps the C++ SDK through rhcommon_c anyway, it would follow naturally once the C++ side exists.
If you also expose the ID3D11DeviceContext* (or even just hint at how to obtain it — device->GetImmediateContext() is fine if there are no threading concerns on your side), that would round things out nicely.
Happy to test any preview build whenever it lands.
Thanks again!
Hi @stevebaer — apologies, I got a bit carried away with my last reply!
No rush at all — just one quick question whenever it’s convenient: is a C++ accessor for the active ID3D11Device* more of a “Rhino 9 release” thing, or a later service release? A rough idea is plenty.
Thank you again for being open to this, and have a good week.
I have added the following 2 functions to CRhinoDisplayPipeline in the C++ SDK. These will be available when we release a C++ SDK for Rhino 9. This will happen before we ship Rhino 9.
// Description:
// If the pipeline is running on top of Direct3d 11, this function
// will return the ID3D11Device* being used. nullptr will be returned
// if the pipeline is running on some other GPU technology.
// NOTE: AddRef is NOT called on this IUnknown instance
struct ID3D11Device* GetDirect3d11Device();
// Description:
// If the pipeline is running on top of Direct3d 11, this function
// will return the ID3D11DeviceContext* actively being used.
// nullptr will be returned if the pipeline is running on some other
// GPU technology.
// NOTE: AddRef is NOT called on this IUnknown instance
struct ID3D11DeviceContext1* GetDirect3d11DeviceContext();
+1 on the RhinoCommon side of this, and one addition.
I’m developing Krill, a Gaussian splatting plugin for Rhino 8/9 (krill.verkare.com). The renderer is our own GPU pipeline (wgpu/Vulkan compute), and we composite into the viewport from a display conduit — including per-pixel depth, so splats and Rhino geometry occlude each other correctly. On Rhino 8 we do this through the OpenGL pipeline with external memory/semaphore sharing and it holds up well. It also works on 9 BETA today once the display is switched to OpenGL on the GPU page.
@stevebaer — GetDirect3d11Device() / GetDirect3d11DeviceContext() cover the device, but for a .NET plugin there’s still no way in, so RhinoCommon equivalents on DisplayPipeline would be very welcome. And for plugins that composite into the existing view rather than run their own full display mode, the device alone isn’t enough: what makes or breaks hybrid rendering is reaching the current viewport’s color and depth targets from inside a conduit channel, so an external renderer can depth-test against what Rhino has already drawn. Color alone is not it — the depth exchange is the hard part.
Asking users to flip a global GPU option just for one plugin is fine for a beta, but not something I want to rely on long term. Is RhinoCommon exposure planned before 9 ships, and is there an intended pattern for external renderers under D3D11 (shared textures, sync, who owns the depth buffer)? Happy to test early bits — our OpenGL depth-interop path has been through most of the sync pitfalls already, so I have concrete test cases.
happy to file this as a formal wish if that helps tracking