Jump to content

  • Log In with Google      Sign In   
  • Create Account

Swap chain rendering to multiple views

  • You cannot reply to this topic
18 replies to this topic

#1 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted 18 December 2014 - 04:29 PM

I'm creating two swap chains on my device so I can render to two separate views.  My engine is in c++ and I'm housing the rendering in controls within a c# front end.  At the moment I have two panels, each showing a different scene.  One of them takes longer to render than the other one.

 

I create my device with a backbuffer that is the full size of the screen.  This means I can resize my views freely and not worry about losing the device.

 

I'm seeing some strange artifacts which I think is because one of them take longer to render than the other, even though I call render1() and render2() consecutively in the same thread.  If I put a 20 ms thread sleep in between these calls, it renders fine

 

Could this be because the CPU has sent everything to the GPU for render1() and when render2() starts, the GPU is still partway through rendering view 1?  If so, is there a way for the CPU to wait until the Present on the swap chain has actually finished with the shared back buffer?



Sponsor:

#2 Buckeye   Crossbones+   -  Reputation: 6287

Like
1Likes
Like

Posted 18 December 2014 - 04:57 PM


I'm creating two swap chains on my device so I can render to two separate views. ... At the moment I have two panels, each showing a different scene.

 

Can you be a bit more precise by what you mean by "view" and "panel?" And why you're using two swapchains? Do you, perhaps, mean 2 separate viewports?


Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#3 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted 18 December 2014 - 05:30 PM

I'm creating two swap chains on my device so I can render to two separate views. ... At the moment I have two panels, each showing a different scene.

 
Can you be a bit more precise by what you mean by "view" and "panel?" And why you're using two swapchains? Do you, perhaps, mean 2 separate viewports?

That's it yes. My c# client currently has a scene view and an animation view (and any other views I might come up with later). I pass the window handle of each panel/view to the c++ engine via a c++/cli dll. The scene view renders the whole scene, with terrain, etc and the animation view just renders my character so I can play about with the animation blends, etc.

I'm using swapchains because the view(ports) in the c# client can be resized and with swapchains, you can resize them and reset them without losing all your loaded resources.

#4 L. Spiro   Crossbones+   -  Reputation: 14245

Like
2Likes
Like

Posted 18 December 2014 - 06:25 PM

2 swap-chains and a single back-buffer?  Swap-chains have their own back-buffers, so I don’t know about what you are talking, but it doesn’t matter because it seems you’ve just jumped through a million hoops to avoid a problem that doesn’t exist.
 

This means I can resize my views freely and not worry about losing the device.

You can already do that while using buffers that are correctly sized for each view. You only have to reset the device when swapping between full-screen and windowed modes (not an exhaustive list), so it isn’t clear what you are trying to accomplish here.
 

I'm using swapchains because the view(ports) in the c# client can be resized and with swapchains, you can resize them and reset them without losing all your loaded resources.

This has nothing to do with swap-chains. You aren’t going to lose anything if you just stay in windowed mode, regardless of swap-chains, and swap-chains are necessary to Present(). They don’t solve a problem, they facilitate necessary functionality.


I don’t understand your setup entirely, but it seems to be the result of misunderstanding that resizing anything is going to cause you to lose your device.
#1: Stop misunderstanding that and implement things correctly instead of using work-around hacks. This will solve all of your problems immediately.
#2: Stop running away from lost devices. The full list of things that can cause the device to be lost is intentionally left unspecified so that people don’t make assumptions as to what can cause it and thus will implement proper handling of such a scenario. It is also trivial to restore resources after a lost device, so there is really no reason not to handle it properly.

 

 

L. Spiro


It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#5 Buckeye   Crossbones+   -  Reputation: 6287

Like
1Likes
Like

Posted 18 December 2014 - 06:59 PM


I pass the window handle of each panel/view to the c++ engine via a c++/cli dll. ... I'm using swapchains because the view(ports) in the c# client can be resized

 

As you're asking a question about what appears to be a technical issue, it's important to use technical terms to describe the situation. In the context of your problem description, what you intend by the terms "view" and "panel" isn't clear. As L. Spiro mentions, if you have a single backbuffer, and you're rendering to one viewport, or a dozen, of that backbuffer, one swapchain is sufficient. That is, a viewport is a structure that specifies which portion of the backbuffer rendering is currently to be limited to.

 

In that regard, you then mention a "window handle" of each "panel/view." Are you talking about the technical term "window handle"? Or do you mean information to set a viewport for rendering?


Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#6 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted Yesterday, 03:18 AM

Thanks guys, it would appear my understanding of how this works is limited.

 


#1: Stop misunderstanding that and implement things correctly instead of using work-around hacks. This will solve all of your problems immediately.

 

Using a work-around hack is not what I was trying to do and, in fact, that's kind of a contradiction - if I've misunderstood something, it's not really a work-around hack.  My c# application is a multi 'view' (by view I mean a panel/control in c# that can display a rendered image from within my c++ engine) app with at least 3 rendered panels, but the option of more.  The reason I initially thought to use swap chains is purely by research.  Once I'd got my image rendering in a view in my c# client, I resized it and the pixels stretched which I obviously don't want.  When I did some further research, I found that you needed to reset the device when changing the size of the rendering area in order that it didn't have to stretch the image across a wider area.  Resetting the device, in my mind, meant losing, or decoupling then, my resources.  As I have a fair amount loaded into the card and I'd seen other applications manage to resize windows dynamically, I assumed they couldn't be resetting the device and reloading resources constantly whilst the window was resizing, which led me to swap chains.

 

I read that in order to use swap chains (which at this point felt like the right thing to) I need to render to a back buffer retrieved using the swap chain (which I thought was its own back buffer) created for the view/rendering panel/viewport and then once rendering is done, call Present on that swap chain.  What I wasn't aware of is that any additional swap chain created uses the same back buffer.  L Spiro, you mentioned that swap chains have their own back buffers - is that by default because it appears that any additional swap chain I create, uses the same back buffer as the device - which does make sense if the idea is that you are rendering to a specified portion of the back buffer - and that would explain the artifacts I am seeing where one rendered view shows flickering bits of the other and vice versa.

 

So, seeing as I have clearly been mislead by my research or have misunderstood it, let me see if I now understand...:

 

In my c++ engine, I have the ability to render in full screen mode, windowed mode or to render to a passed-in HWND.  I need to create a C# front end which has multiple 'views' in the form of a couple of split containers (front, side, top - style) which will all be rendering the same scene but from different angles.  I also need to cater for a floating tool-like window which could very well be floating over the other rendered views.

 

So from Buckeye's point, I can achieve this by just using the default SwapChain that gets created when creating the device and Presenting to different areas of it (passing in the correct RECT structures to Present()).  If I need to have windows floating over the top, I guess I just render them after the ones underneath?

 


#2: Stop running away from lost devices. The full list of things that can cause the device to be lost is intentionally left unspecified so that people don’t make assumptions as to what can cause it and thus will implement proper handling of such a scenario. It is also trivial to restore resources after a lost device, so there is really no reason not to handle it properly.

 

I'm not running away from anything.  My assumption on this was one of speed after a bit of misled research.  Other apps that do this resizing of multiple views can't possibly be resetting and reloading resources dynamically anytime the mouse is moving whilst resizing - I just took the wrong route to working out how that's done.

 

Thanks



#7 Buckeye   Crossbones+   -  Reputation: 6287

Like
1Likes
Like

Posted Yesterday, 09:39 AM


I have the ability to render in full screen mode, windowed mode or to render to a passed-in HWND. I need to create a C# front end which has multiple 'views' in the form of a couple of split containers (front, side, top - style) which will all be rendering the same scene but from different angles.

 

Just to reiterate, the importance of using technical terms to describe a technical problem cannot be overemphasized. In particular, your topic has a D3D9 tag and you mention your C++ engine, but you're apparently using a lot of C# terms (used in .Net Framework??) mixed in the discussion. You mention "panels" and "views" without distinguishing whether those are related to the C++ engine or the C# "front-end." I'm personally not that familiar with C# or .Net Framework, so I have to make guesses about the terms you're using (by doing a bit of googling), and how you're using the objects. Don't make people guess. If you don't know what a term means, either state that or ask.

 

So, with regard to the rendering "abilities" you mention above, and assuming you're talking about your D3D9/C++ engine:

 

It's likely you render to a backbuffer, probably after setting a viewport (not a "view" or "panel" - a viewport), and transforms using D3DTS_VIEW and D3DTS_PROJECTION. Assuming you don't have a separate routine for doing so, you don't render to a "passed-in HWND." I'm guessing, at some point, you call d3d9Device->Present( some-rect-ptr, some-other-rect-ptr, some-HWND, maybe-some-dirty-region-ptr ). If so, there are requirements for creating the swapchain if any of the pointers are not NULL. N.B., that doesn't render to the window with the handle HWND, it copies data from the backbuffer to the client area of the window, and swaps (or rotates) buffers.

 

The pointers to rectangles in the Present call determine from what area of the backbuffer, and to what portion of the window's client area, the data is copied, before the buffer is swapped.

 

Perhaps that will help you find the problem, but, without a better technical description of what "some strange artifacts" means, and making even more assumptions and guesses, I can't do much more.


Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#8 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted Yesterday, 10:59 AM


Just to reiterate, the importance of using technical terms to describe a technical problem cannot be overemphasized

 

I've worked in the IT industry for 23 years and one of the things that has always bothered me is issues or bugs that are not described properly so I'm disappointed I didn't get my point across well enough.  Sorry for that, I'm not intending anyone to 'guess' parts of my issue, I thought I'd covered everything.

 

I've moved on from the additional swapchains after your comments and I'm now trying to render using viewports.  In simplistic terms and removing C# from the equation, I'm rendering to two viewports (DirectX9 viewports).  One on the left and one on the right.  When I call Present, I call it twice (once after the render to each viewport), not at the end.  The reason I do this is because in my Present call, I'm using an HWND as the hDestWindowOverride parameter.  I am supplying source and dest RECTs.  The source for the left render is the left half of the back buffer and the source for the right is the right side.  The destination RECT is effectively the local client area of the render 'frame', so 0, 0, width, height.  Width being half the size of the back buffer.

 

Both viewports do render the correct views but the artefact is a shimmering/tearing band across the screen horizontally where, on the left, the band shows the contents of the right-hand render and, on the right, it shows the left side.  It's difficult to explain but it looks like when I render each view, the previous view hasn't quite finished rendering so I'm getting a form of tearing.  It looks like when I've rendered the left side, I start on the right side and the CPU has already called Present on the next view before the previous view has been rendered.  If I put a sleep call of 50ms or so before each call to render each frame, just to ensure the GPU would have fully rendered everything, it works perfectly (albeit at 20fps of course).

 

I had a look at the CodeSampler example for rendering to viewports and I'm doing exactly the same as they are other than the fact that I have to call Present after each BeginScene/EndScene rather than at the end (in their example, they call Present with all NULL parameters).

 

Edit:  I think this is the problem.  Calling Present() after rendering one view is swapping the back buffer and the front buffer when I should be calling present after rendering both views.  This would work if I wasn't using HWNDs supplied from C#.  This is why I went down the additional swap chain route as each one will have its own backbuffer and it won't matter the order in which I call Present...


Edited by RobMaddison, Yesterday, 11:11 AM.


#9 Buckeye   Crossbones+   -  Reputation: 6287

Like
1Likes
Like

Posted Yesterday, 02:34 PM


I think this is the problem. Calling Present() after rendering one view is swapping the back buffer and the front buffer when I should be calling present after rendering both views. This would work if I wasn't using HWNDs supplied from C#. This is why I went down the additional swap chain route as each one will have its own backbuffer and it won't matter the order in which I call Present..

 

It's still not clear exactly what you're doing. Perhaps you are trying to render two scenes to the same backbuffer and trying to Present portions of the same backbuffer to two different windows. As previously mentioned, Present copies to one destination window and the backbuffer is swapped. Rendering to separate swapchains could be done, but it seems simpler to use just a single backbuffer, sized for the maximum size needed.

 

I.e., it sounds as if you want to render scenes with different viewports (corresponding to the size of the window the result is to be copied to), and probably from with different view and projection matrices, and then Present to a particular window. IF so, with a single swapchain, you can use the sequence: setup viewport, setup view & projection matrices, BeginScene, draw-stuff, EndScene, Present - where the setup is window specific.

 

E.g.:

SetupForWindow(HWND ...) { set the viewport size and other parameters }
DrawTheScene();
PresentToWindow(HWND ...);

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#10 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted Yesterday, 07:46 PM

It's still not clear exactly what you're doing.


Sorry, I'm not sure how else to describe it. You're right though, I am trying to render two scenes to the same back buffer and that is happening but I have to Present after each render _because_ I am rendering to two separate windows, not rendering two views side by side within the same window.

I'm pretty sure my solution is to use another swapchain with its own backbuffer meaning I can do the following:

Render to swapchain 1
Present swapchain 1
Render to swapchain 2
Present swapchain 2

Rather than:

Render to a portion of the backbuffer
Present that portion of it to an hwnd
Render another portion to the same backbuffer
Present that portion of it to the other hwnd

...which clearly isn't working

#11 Buckeye   Crossbones+   -  Reputation: 6287

Like
1Likes
Like

Posted Yesterday, 09:06 PM

If it works for you, you should be good to go. However, be assured, the single backbuffer approach can be made to work. Although the windows shown below are arranged within the client area of the main window, each is rendered and Presented to using the same routine, just with different parameters as mentioned above.

 

multi-windows.png


Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#12 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted Today, 04:02 AM

Yes that's the visual look I'm going for, but if you can imagine that each of those 3 'panels' (as they're called in c#) had their own HWND that gets passed to the c++ engine from c#. So you can't do an overall present on them after you've rendered the 3 images because you have to present with individual HWND parameters.

I'll try with the additional swapchains again but I have a clearer idea of how to do it now thanks to a few cleared up areas above. I really appreciate your time and help, both

#13 Buckeye   Crossbones+   -  Reputation: 6287

Like
1Likes
Like

Posted Today, 07:36 AM

Just one more try, then I'll leave you to your journey.

 


if you can imagine that each of those 3 'panels' (as they're called in c#) had their own HWND that gets passed to the c++ engine

 

Easy to imagine, because that's exactly what was done above.

 


each is rendered and Presented to using the same routine, just with different parameters as mentioned above. [HWNDs]

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#14 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted Today, 08:55 AM

Easy to imagine, because that's exactly what was


So you call Present 3 times in your above example, each time with a different HWND in parameter 3?

When you call Clear before rendering each of those 3 views too?

#15 L. Spiro   Crossbones+   -  Reputation: 14245

Like
1Likes
Like

Posted Today, 09:31 AM

The original problem you were having with your 2nd render taking too long is likely a sync issue related to writing to a resource that is still being read by the GPU.
In other words, Preset()’ing a surface to the GPU and then immediately starting to write to that same surface for the next window will cause a flush of commands and stall the CPU, hence the lag you experience.


L. Spiro
It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#16 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted Today, 09:44 AM

The original problem you were having with your 2nd render taking too long is likely a sync issue related to writing to a resource that is still being read by the GPU.
In other words, Preset()’ing a surface to the GPU and then immediately starting to write to that same surface for the next window will cause a flush of commands and stall the CPU, hence the lag you experience.


L. Spiro

 

Yes, that's what it seems to be.  And this can be cured with an additional swapchain per extra 'view' with its own backbuffer right?  So I guess, in a round about way, my initial research wasn't as misleading as I thought.



#17 Buckeye   Crossbones+   -  Reputation: 6287

Like
0Likes
Like

Posted Today, 12:23 PM


So you call Present 3 times in your above example, each time with a different HWND in parameter 3? When you call Clear before rendering each of those 3 views too?

 

Yep.

 

1. as needed: set viewport, set view matrix, set projection, set fillmode, set lighting params

2. Clear

3. BeginScene / draw-stuff / Endscene

4. Present( &rect, &rect, HWND, 0 )


Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#18 RobMaddison   Members   -  Reputation: 774

Like
0Likes
Like

Posted Today, 01:55 PM

So you call Present 3 times in your above example, each time with a different HWND in parameter 3? When you call Clear before rendering each of those 3 views too?

 
Yep.
 
1. as needed: set viewport, set view matrix, set projection, set fillmode, set lighting params
2. Clear
3. BeginScene / draw-stuff / Endscene
4. Present( &rect, &rect, HWND, 0 )

Hmm, I've tried practically the same thing.

Can I just ask then, when you call Present using a source and dest rect, on either the default swap chain or an additional one, what actually happens with the front and back buffers? I understand that if its a full present, ie no source and dest rects, it simply switches the buffer pointers around so the back buffer becomes the front buffer but I'm not sure what happens in a Present with region - does the front buffer stay as it is but the region specified is copied to it from the back buffer...? Trying to find documentation but struggling a bit.

#19 Buckeye   Crossbones+   -  Reputation: 6287

Like
0Likes
Like

Posted Today, 03:18 PM


what actually happens with the front and back buffers? ... I'm not sure what happens in a Present with region

 

First: I've not specifically used multiple swapchains, as I used the method described above.

 

Regarding your question: according to the docs, Present flips the surfaces for the front and back buffers. I.e., Present results in a switch of pointers to the next render surface in the swapchain. I think your question is really about what happens to the content of the surfaces following Present. My understanding, again, from the docs, is that maintenance of the contents depends on the D3DSWAPEFFECT flag used when the device/swapchain is created. One of those flags: "D3DSWAPEFFECT_COPY ... the Present operation ... leaves the content of the back buffer unchanged, instead of replacing it with the content of the front buffer as a flip-based Present operation would."

 

Out of curiosity, before you draw, do you set the device's rendertarget to the surface obtained from the swapchain you then use to call Present (assuming you're using swapchain->Present and not device->Present) ?


Edited by Buckeye, Today, 03:20 PM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.






PARTNERS