DX9 RS - Multiple render windows & multiple devices

Discussion area about developing or extending OGRE, adding plugins for it or building applications on it. No newbie questions please, use the Help forum for that.
Post Reply
User avatar
Xavyiy
OGRE Expert User
OGRE Expert User
Posts: 847
Joined: Tue Apr 12, 2005 2:35 pm
Location: Albacete - Spain
x 87

DX9 RS - Multiple render windows & multiple devices

Post by Xavyiy »

Hi everybody,

Yesterday I've been playing with the DX9 render system for fixing some bugs when using multiple monitors (so, multiple d3d9 devices) and multiple render windows (editor-like app). I'm opening a new thread here instead of simply creating a pull-request / JIRA ticket since this "fix" works fine for me but maybe not for others (although I think all apps with multiple render windows -so potentially using a dummy render window- would benefit from this).

Problem + solution:
In Paradise Sandbox, I use a dummy render window to be used as primary render window (holding all resources, ...) and avoid device resets when resizing (and others) the render windows.

Well, that works very well when there is only one device (1 monitor), but it was not working that fine when there were two devices (2 monitors).

The problem was that if the app was in one monitor (placing the app window in one screen) all worked fine, but when the app was dragged to the other monitor, the dummy render window didn't seem to work: resizing render windows (the first one to be created, so it was acting as primary render window) leads to device resets. Moreover these device resets were "weird": if Hydrax was enabled then I got the classical "Cannot reset device!" (Which was very weird because in Hydrax I'm not using anything fancy: manual mesh + some RTTs, nothing which I haven't used in other parts of Paradise Sandbox).

My solution: forcing the DX9 RS to change the device of ALL render windows when one of them was changed to another device. This way, all render windows will be attached to the "second" device and the dummy render window will continue to act as primary render window, avoiding these annoying device resets when resizing, removing/adding render windows, etc.

I guess it's "safe" to assume that usually a multi render window application (editors) will belong always on the same "monitor", but maybe some apps will have multiple render windows in different UI windows and each one will be placed in one monitor. But even if that's the case, I very doubt Windows will allow the same app to be on "different monitors", so I guess internally all render windows of the same app will belong to the same device even if the app has multiple UI windows each one in a different monitor with an Ogre render window. (I haven't looked into this, anyone?).

Here is my "fix": OgreD3D9DeviceManager.cpp - ::selectDevice(...), add the else statement between >>> & <<<:

Code: Select all

						if (validateAllDevices)
						{
							for (uint i = 0; i < mRenderDevices.size(); ++i)
								mRenderDevices[i]->validateFocusWindow();
						}	
					}				
				}
			}
                        // ----------------------------------------->>>
			else // No multi-head
			{
				// Move all existing render windows to the new device (or, alternatively, we could just move the dummy render window assumed it's the first one if multiple render windows are present)
				D3D9RenderWindowList& renderWindows = renderSystem->mRenderWindows;
				D3D9RenderWindow* rw;

				for(Ogre::uint32 k = 0; k < renderWindows.size(); k++)
				{
					rw = renderWindows.at(k);
					
					if (rw != renderWindow)
					{
						if (rw->getDevice())
						{
							rw->getDevice()->detachRenderWindow(rw);
						}

						renderWindowsGroup.push_back(rw);
					}
				}
			}
                       // ------------------------------------- <<<
		}
		
		// Do we want to preserve the FPU mode? Might be useful for scientific apps
		ConfigOptionMap& options = renderSystem->getConfigOptions();
		ConfigOptionMap::iterator opti = options.find("Floating-point mode");
		if (opti != options.end() && opti->second.currentValue == "Consistent")
			extraFlags |= D3DCREATE_FPU_PRESERVE;
I'm not an expert at all in raw DX, but this solution works pretty good for me (despite it's not very elegant).

I hope it will be of help for anyone else having this problem.

Xavier
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: DX9 RS - Multiple render windows & multiple devices

Post by dark_sylinc »

If all your render windows are in the same monitor/device; the problem is that your dummy lives in one of the devices, while the real RenderWindow is living in another device.

When creating the renderwindows (including the dummy ones) pass the same "monitorIndex" config parameter to both RenderWindows to ensure they get created in the same monitor (*).

Then you have to catch all move events so that you move the dummy window along the real window, in case they need migrate to another monitor. This also saves a lot of RAM and VRAM since otherwise Ogre is forced to created two devices and duplicate every resource.

(*) Don't trust on the initial position parameter for the window. Use monitorIndex explicitly. My setup is quite rare: I have a 23' LED (DVI) + 19' LCD (VGA) setup.
My GPU comes with two DVI connectors (and a couple HDMI). Only the first DVI supports sending a VGA signal, so I have to plug the 19' LCD with an adapter there. If I try to plug the adapter in the 2nd connector, the LCD monitor just doesn't receive signal.
Why is this important? Because by default the 19' LCD is monitor 0; and 23' LED is monitor 1. While booting and using generic drivers this is so. However, after installing the AMD drivers, Windows (or the driver) is smart enough to figure out the 23' LED is obviously the main one; and hence becomes monitor 0; while the 19' LCD becomes monitor 1.
Nonetheless occasionally the system "forgets" about that with some applications (particularly what means to be at position (0, 0) ). Most likely this is due to some buggy OS function that doesn't take the flip into account. It wouldn't matter if it weren't that when I ordered to create the dummy window at 0, 0; it would appear in the wrong monitor.
User avatar
Xavyiy
OGRE Expert User
OGRE Expert User
Posts: 847
Joined: Tue Apr 12, 2005 2:35 pm
Location: Albacete - Spain
x 87

Re: DX9 RS - Multiple render windows & multiple devices

Post by Xavyiy »

If all your render windows are in the same monitor/device; the problem is that your dummy lives in one of the devices, while the real RenderWindow is living in another device.
Yes, the problem is that the dummy render windows was in the other device (in my case, the dummy render window was created in the external monitor: it's a VGA output from a laptop - Nvidia 640M, while the app UI was in the other monitor: visible render windows).
When creating the renderwindows (including the dummy ones) pass the same "monitorIndex" config parameter to both RenderWindows to ensure they get created in the same monitor (*).
I didn't know about this 'monitorIndex' config param :), but my main concern is when the user changes the app to the other monitor: D3D9DeviceManager::selectDevice is invoked for the updated render window(s) -vía D3D9Device::validateDisplayMonitor when the monitor has changed-, but not for the dummy (it's not updated), so you say:
Then you have to catch all move events so that you move the dummy window along the real window, in case they need migrate to another monitor.
In this case, it is not simplier/easier to migrate the dummy render windows to the "new device" every time a render window changes the device? Using something like (editing D3D9DeviceManager::selectDevice)

Code: Select all

else // No multi-head
         {
            // Move all existing render windows to the new device (or, alternatively, we could just move the dummy render window assumed it's the first one if multiple render windows are present)
            D3D9RenderWindowList& renderWindows = renderSystem->mRenderWindows;
            D3D9RenderWindow* rw;

            for(Ogre::uint32 k = 0; k < renderWindows.size(); k++)
            {
               rw = renderWindows.at(k);
               
               if (rw != renderWindow && rw->getName() == "DummyRenderWindow") // Or filter by a creation flag which specifies it's the dummy RW
               {
                  if (rw->getDevice())
                  {
                     rw->getDevice()->detachRenderWindow(rw);
                  }

                  renderWindowsGroup.push_back(rw);
               }
            }
         }
This way you could forget about tracking the windows positions since the dummy render window will be always in the same device than the visible render window(s), or am I missing something? (As I said in the first post, I'm not familiar at all with raw DX)

One question, does rw->getDevice()->detachRenderWindow(rw); actually releases render window's resources from the device? If yes, then there shouldn't be any problem about duplicated data in RAM/VRAM.

Edit: If anybody has a Nvidia + Windows + Multi-monitor, could you check if Paradise Sandbox works? (http://paradise-sandbox.com/download.php). I'm saying that because it works pretty well here, but I have some reports of people experimenting issues (laptops). My guess is that they're not using the high performance processor for all monitors (configurable in the Nvidia control panel), but I would like to have more info. Non-nvidia cards use DX9 so this multi-monitor bugfix is not yet released.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: DX9 RS - Multiple render windows & multiple devices

Post by dark_sylinc »

Xavyiy wrote: This way you could forget about tracking the windows positions since the dummy render window will be always in the same device than the visible render window(s), or am I missing something? (As I said in the first post, I'm not familiar at all with raw DX)
If I understood you correctly, the problem is that if the dummy window is migrated to the new device it's still in the wrong monitor, hence two devices. Am I missing something?
Xavyiy wrote: One question, does rw->getDevice()->detachRenderWindow(rw); actually releases render window's resources from the device? If yes, then there shouldn't be any problem about duplicated data in RAM/VRAM.
I would have to look the code; but that's not what I meant.
When you have two devices, EVERYTHING gets allocated twice. A mesh? Then you actually have 2 mesh buffers. A texture? then you actually have 2 textures.
When all windows are moved to the same monitor the RS will automatically destroy unneeded devices. When you have one device, you'll be maximizing memory usage.
Post Reply