🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Swap chain rendering to multiple views

Started by
25 comments, last by RobM 9 years, 5 months ago

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?

Advertisement


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.

You don't forget how to play when you grow old; you grow old when you forget how to play.

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.

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


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.

You don't forget how to play when you grow old; you grow old when you forget how to play.

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


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.

You don't forget how to play when you grow old; you grow old when you forget how to play.


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...


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.

You don't forget how to play when you grow old; you grow old when you forget how to play.

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

This topic is closed to new replies.

Advertisement