SharpDX: best practice for multiple RenderForms?
- by Rob Jellinghaus
I have an XNA app, but I really need to add multiple render windows, which XNA doesn't do. I'm looking at SharpDX (both for multi-window support and for DX11 / Metro / many other reasons). I decided to hack up the SharpDX DX11 MultiCubeTexture sample to see if I could make it work.
My changes are pretty trivial. The original sample had:
[STAThread]
private static void Main()
{
var form = new RenderForm("SharpDX - MiniCubeTexture Direct3D11 Sample");
...
I changed this to:
struct RenderFormWithActions
{
internal readonly RenderForm Form;
// should just be Action but it's not in System namespace?!
internal readonly Action RenderAction;
internal readonly Action DisposeAction;
internal RenderFormWithActions(RenderForm form, Action renderAction, Action disposeAction)
{
Form = form;
RenderAction = renderAction;
DisposeAction = disposeAction;
}
}
[STAThread]
private static void Main()
{
// hackity hack
new Thread(new ThreadStart(() =
{
RenderFormWithActions form1 = CreateRenderForm();
RenderLoop.Run(form1.Form, () = form1.RenderAction(0));
form1.DisposeAction(0);
})).Start();
new Thread(new ThreadStart(() =
{
RenderFormWithActions form2 = CreateRenderForm();
RenderLoop.Run(form2.Form, () = form2.RenderAction(0));
form2.DisposeAction(0);
})).Start();
}
private static RenderFormWithActions CreateRenderForm()
{
var form = new RenderForm("SharpDX - MiniCubeTexture Direct3D11 Sample");
...
Basically, I split out all the Main() code into a separate method which creates a RenderForm and two delegates (a render delegate, and a dispose delegate), and bundles them all together into a struct. I call this method twice, each time from a separate, new thread. Then I just have one RenderLoop on each new thread.
I was thinking this wouldn't work because of the [STAThread] declaration -- I thought I would need to create the RenderForm on the main (STA) thread, and run only a single RenderLoop on that thread. Fortunately, it seems I was wrong. This works quite well -- if you drag one of the forms around, it stops rendering while being dragged, but starts again when you drop it; and the other form keeps chugging away.
My questions are pretty basic:
Is this a reasonable approach, or is there some lurking threading issue that might make trouble?
My code simply duplicates all the setup code -- it makes a duplicate SwapChain, Device, Texture2D, vertex buffer, everything. I don't have a problem with this level of duplication -- my app is not intensive enough to suffer resource issues -- but nonetheless, is there a better practice? Is there any good reference for which DirectX structures can safely be shared, and which can't?
It appears that RenderLoop.Run calls the render delegate in a tight loop. Is there any standard way to limit the frame rate of RenderLoop.Run, if you don't want a 400FPS app eating 100% of your CPU? Should I just Thread.Sleep(30) in the render delegate?
(I asked on the sharpdx.org forums as well, but Alexandre is on vacation for two weeks, and my sister wants me to do a performance with my app at her wedding in three and a half weeks, so I'm mighty incented here! http://robjsoftware.org for details of what I'm building....)