PDA

View Full Version : Multiple rendering contexts problems



Jason666
11-09-2004, 05:22 PM
Working on a 3D editor type app with 4 views (left top etc).

At the moment, when the app is first run the contexts are created and the rendering does work and show as expected in the 4 areas of the app.

Now for the problems;

At this stage I am rendering a simple cube in each view for testing purposes. I added some code to get the cube to rotate. If I only activate (wglmakecurrent) one of the contexts, then the rotation works as expected. If I activate all 4 of the contexts the cube does not rotate.

Same thing if I try and change the location of the cube. In one window it works, but once all 4 contexts are activated it fails (actually it only updates the last activated context).

Any ideas?

Code for display is basically

//first view
wglMakeCurrent(cameraDC,cameraRC);
<render opengl stuff here>
//second view
wglMakeCurrent(leftDC,leftRC);
<render opengl stuff here>
etc
etc for the other 2 views

The contexts are created when the app first starts up and are not deleted.

Writing this in Delphi if that helps at all.

Any help or ideas will be greatly appreciated.

Thanks,
Jason.

rgpc
11-09-2004, 05:43 PM
I think we can be fairly sure that wglMakeCurrent() is working (Doom wouldn't run if it didn't), and you haven't shown us any of your other code - so I doubt anyone could help you.

There's probably a very good chance of the problem being in the code you use to render your mesh.

Jason666
11-09-2004, 05:58 PM
OK, some more snippets that may help (I hope).

Creating contexts when app starts;

//setup pixel format descriptor
FillChar(PFD,SizeOf(PFD),0);
with PFD do
begin
nSize := SizeOf(PFD); // Size of this structure
nVersion := 1; // Version of this structure
dwFlags := PFD_SUPPORT_OPENGL; // Support OpenGL calls
dwFlags := dwFlags or PFD_DRAW_TO_BITMAP; // Draw into a bitmap
iPixelType := PFD_TYPE_RGBA; // RGBA color mode
cColorBits := 24;
cDepthBits := 24; // Size of depth buffer
iLayerType := PFD_MAIN_PLANE; // Draw in main plane
end;

cameraDC:=camerabitmap.canvas.handle;
PFI:=ChoosePixelFormat(cameraDC, @PFD);
SetPixelFormat(cameraDC, PFI, @PFD);
cameraRC:=wglCreateContext(cameraDC);
wglMakeCurrent(cameraDC,cameraRC);
cameraaspect:=camerabitmap.width/camerabitmap.height;
glenable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
gluPerspective(camerafovy,cameraaspect,cameraznear ,camerazfar);
glViewport(0, 0, camerabitmap.Width, camerabitmap.Height);

Above is repeated for the other 3 contexts. Same code with names changed.

And for the "update display" code;

wglMakeCurrent(cameraDC,cameraRC);
glenable(GL_COLOR_MATERIAL);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
gluLookAt(eyex,eyey,eyez,lookatx,lookaty,lookatz,0 ,1,0);
glRotatef(strtofloat(xrot.text),1,0,0);
glRotatef(strtofloat(yrot.text),0,1,0);
glRotatef(strtofloat(zrot.text),0,0,1);
glbegin(gl_quads);CubeGL(0,0,0,80);glend;
glflush;
cameraimage.canvas.draw(0,0,camerabitmap);

The above is only for the main camera view, but the code for the other views looks identical (except for the context names).

The cube does get rendered, it just doesn't rotate if all 4 views are being displayed. It is like OpenGL is ignoring the rotate commands for some reason.

Rendering a single view works as expected. Rendering the 4 views at the same time (well one after another) causes the problem.

Any other ideas?

Thanks,
Jason.

LogicalError
11-10-2004, 12:31 AM
i'm just guessing here.
but maybe you need to invalidate the windows before rendering the bitmap?
otherwise windows thinks the window doesn't need to be updated.

besides, why are you rendering to bitmaps and not directly to the window?

skynet
11-10-2004, 03:22 AM
I suggest, you check the return value of wglMakeCurrent for possibly occured errors.

Jason666
11-10-2004, 11:27 AM
Thanks for the suggestions.

Invaldating the window didn't make a difference. Exact same result.

Tracking the return value of wglmakecurrent and the various context handles was more interesting.

When app first starts and displays are updated (which all 4 do display correctly);

CAMERA: wglmakecurrent = True getlasterror = 8 cameradc = 838928627 getcurrentdc = 838928627
FRONT: wglmakecurrent = True getlasterror = 8 frontdc = 721488187 getcurrentdc = 721488187
LEFT: wglmakecurrent = True getlasterror = 8 leftdc = 117508407 getcurrentdc = 117508407
TOP: wglmakecurrent = True getlasterror = 8 topdc = 83953890 getcurrentdc = 83953890

If I refresh all the displays again (which uses the exact same code as when app is first run), results are;

CAMERA: wglmakecurrent = False getlasterror = 6 cameradc = 838928627 getcurrentdc = 83953890
FRONT: wglmakecurrent = False getlasterror = 2000 frontdc = 721488187 getcurrentdc = 0
LEFT: wglmakecurrent = False getlasterror = 6 leftdc = 117508407 getcurrentdc = 83953890
TOP: wglmakecurrent = False getlasterror = 2000 topdc = 83953890 getcurrentdc = 0

For the third (and all subsequent refreshes) the results are;

CAMERA: wglmakecurrent = False getlasterror = 6 cameradc = 838928627 getcurrentdc = 0
FRONT: wglmakecurrent = False getlasterror = 2000 frontdc = 721488187 getcurrentdc = 0
LEFT: wglmakecurrent = False getlasterror = 6 leftdc = 117508407 getcurrentdc = 0
TOP: wglmakecurrent = False getlasterror = 2000 topdc = 83953890 getcurrentdc = 0

How can I find out what getlasterror values of 6 and 2000 mean?

Any other suggestions? This should be a simple problem to fix.

Thanks,
Jason.

Jason666
11-10-2004, 11:52 AM
OK, turns out that the displays I thought were working but not rotating were not working. The last update to the bitmap was just being redisplayed without being changed by OpenGL. Cleared all the bitmaps and filled them with a fuschia color, so if the OpenGL calls do not update the bitmap correctly then it will be shown as a solid pink square.

Now what happens...

First run - all displays fine - return codes;

CAMERA: wglmakecurrent = True getlasterror = 8 cameradc = 3036744299 wglgetcurrentdc = 3036744299
FRONT: wglmakecurrent = True getlasterror = 8 frontdc = 1426131632 wglgetcurrentdc = 1426131632
LEFT: wglmakecurrent = True getlasterror = 8 leftdc = 234949323 wglgetcurrentdc = 234949323
TOP: wglmakecurrent = True getlasterror = 8 topdc = 2550205167 wglgetcurrentdc = 2550205167

Second refresh - only the "top" view is updated - all the other views are fuschia - return codes

CAMERA: wglmakecurrent = False getlasterror = 6 cameradc = 3036744299 wglgetcurrentdc = 2550205167
FRONT: wglmakecurrent = False getlasterror = 6 frontdc = 1426131632 wglgetcurrentdc = 2550205167
LEFT: wglmakecurrent = False getlasterror = 6 leftdc = 234949323 wglgetcurrentdc = 2550205167
TOP: wglmakecurrent = False getlasterror = 6 topdc = 2550205167 wglgetcurrentdc = 2550205167

This is strange. If the wglmakecurrent call returned false, then why does it work and update the view?

Regards,
Jason.

Jason666
11-10-2004, 12:14 PM
Maybe this will help too. I remarked out all the OpenGL rendering code and left only the wglmakecurrent calls. So code structure when updating is now just (variables used to track return values etc);

cb:=wglMakeCurrent(cameraDC,cameraRC);
cle:=getlasterror;
ccdc:=wglgetcurrentdc;
lb:=wglMakeCurrent(leftDC, leftRC);
lle:=getlasterror;
lcdc:=wglgetcurrentdc;
fb:=wglMakeCurrent(frontDC, frontRC);
fle:=getlasterror;
fcdc:=wglgetcurrentdc;
tb:=wglMakeCurrent(topDC, topRC);
tle:=getlasterror;
tcdc:=wglgetcurrentdc;

No OpenGL rendering calls. Results are the same as the last run, ie

First display results;
CAMERA: wglmakecurrent = True getlasterror = 8 cameradc = 4261480786 wglgetcurrentdc = 4261480786
FRONT: wglmakecurrent = True getlasterror = 8 frontdc = 3288402361 wglgetcurrentdc = 3288402361
LEFT: wglmakecurrent = True getlasterror = 8 leftdc = 2181106456 wglgetcurrentdc = 2181106456
TOP: wglmakecurrent = True getlasterror = 8 topdc = 872483203 wglgetcurrentdc = 872483203

Second and all further results;
CAMERA: wglmakecurrent = False getlasterror = 6 cameradc = 4261480786 wglgetcurrentdc = 872483203
FRONT: wglmakecurrent = False getlasterror = 6 frontdc = 3288402361 wglgetcurrentdc = 872483203
LEFT: wglmakecurrent = False getlasterror = 6 leftdc = 2181106456 wglgetcurrentdc = 872483203
TOP: wglmakecurrent = False getlasterror = 6 topdc = 872483203 wglgetcurrentdc = 872483203

So from this, it looks like at the end of the first display the currentdc is locked into the dc for the top view.

Don't know what else to try.

Jason.

IronicResearch
11-15-2004, 04:08 PM
Typically the technique of keeping separate GLRCs open per window DC is intended for multiple rendering threads. For non-multi-threaded rendering you will probably need to close each GLRC before going on to the next one. ie,

wglMakeCurrent(cameraDC,cameraRC);
// draw and update...
wglMakeCurrent(cameraDC,NULL);

wglMakeCurrent(topDC,topRC);
// draw and update...
wglMakeCurrent(topDC,NULL);

Otherwise the GL driver may be using cached info on what it thinks is the current GLRC and keeping that GL command pipe open for the corresponding window DC until it detects some change.

Jason666
11-21-2004, 12:34 PM
I tried to add a specific wglmakecurrent(cameradc,NULL) calls and it didn't make a difference.

Elsewhere I saw info that when you make a wglmakecurrent call to another DC/RC it automatically "deselects" the current context anyway.

Is there any info somewhere about the return values of wglmakecurrent and getlasterror?

When the app first runs and all 4 displays update correctly the 4 wglmakecurrent calls return true, and they have a getlasterror value of 8.

When the displays are updated after the frist run (using exact same code as when the app starts) wglmakecurrent returns false and getlasterror returns 2000. BUT, one of the windows does still update correctly.

So when tracking the DCs RCs etc;

First run when all 4 windows show correctly;
C: wglmakecurrent = True getlasterror = 8 rc = 65536 dc = 4261480657 wglgetcurrentdc = 4261480657
F: wglmakecurrent = True getlasterror = 8 rc = 65538 dc = 3875606279 wglgetcurrentdc = 3875606279
L: wglmakecurrent = True getlasterror = 8 rc = 65537 dc = 167841436 wglgetcurrentdc = 167841436
T: wglmakecurrent = True getlasterror = 8 rc = 65539 dc = 1627457575 wglgetcurrentdc = 1627457575

Second and all subsequent runs;
C: wglmakecurrent = False getlasterror = 2000 rc = 65536 dc = 4261480657 wglgetcurrentdc = 1627457575
F: wglmakecurrent = False getlasterror = 2000 rc = 65538 dc = 3875606279 wglgetcurrentdc = 1627457575
L: wglmakecurrent = False getlasterror = 2000 rc = 65537 dc = 167841436 wglgetcurrentdc = 1627457575
T: wglmakecurrent = False getlasterror = 2000 rc = 65539 dc = 1627457575 wglgetcurrentdc = 1627457575

And the only window that shows is the one that has the right DC.

Sometimes after mutliple refreshes the wglgetcurrentdc will change to one of the other DCs and another of the windows will update correctly.

Very strange? Any indepth doco on the getlasterror values for wglmakecurrent?

Thanks,
Jason.

Jason666
11-21-2004, 04:36 PM
OK, narrowed it down a bit more.

1. If I move the display code out of the program startup (form creation) code the first display update of all 4 windows works perfectly all returning a 0 for get last error.

2. Second time the update display code is called the return code from getlasterror is 7D0 (hex) and windows reports this means "The pixel format is invalid".

The offscreen bitmaps and the pixelformatdescriptor are both set to 24 bit and are not changed in any part of the program.

Anyone have any ideas on why the pixel format would be invalid after a second display update (exact same code that works the first time it is called)?

Thanks,
Jason.

Jason666
11-21-2004, 04:45 PM
Hmmm, getting closer. Two final questions (I hope).

The 4 displays now work correctly if I call SetPixelFormat before each wglmakecurrent call.

Are there any dangers with doing this?

Also, now the getlasterror returns "Not enough storage is available to process this command", but seeing as the displays are now working maybe I shouldn't be concerned?

Maybe all this rambling may help another Delphi user in the future.

Jason.

skynet
11-21-2004, 10:56 PM
You must not call SetPixelFormat more than once per window, consult MSDN about it.

V-man
11-22-2004, 04:26 AM
Originally posted by skynet:
You must not call SetPixelFormat more than once per window, consult MSDN about it.I've seen somebody do it and it works. He was using the render to bitmap flag in SetPixelFormat.

Jason666, What is your renderer? glGetString(GL_RENDERER)

Jason666
11-22-2004, 11:48 AM
I saw the warning about SetPixelFormat in MSDN, but in this case I am not rendering to a Window, and the call doesn't seem to cause any issues with the app (even after thousands of repeated calls to the update display routine). What symptoms would I see if multiple calls to SetPixelFormat caused a problem?

glgetstring(GL_RENDERER) returns "GDI Generic".

Thanks again,
Jason.

l_belev
11-23-2004, 12:12 AM
why are you using different rendering contexts in first place? It is better to use single when possible, and in your case they are essentially identical and you re-set all the states all the time anyway. Generally the context switching is a heavy operation for the driver, and cumbersome for your app. Also you better not set the pixel format multiple times. Even if it's harmless, it does not help either.

rgpc
11-23-2004, 12:25 AM
Originally posted by Jason666:
glgetstring(GL_RENDERER) returns "GDI Generic".
This is most likely because you are rendering to a bitmap instead of windows. I would guess that you will not have any extensions greater than v1.1 available to you because "GDI_GENERIC" is running in software - so you wouldn't be getting any HW T&L.

You'd be much better off just rendering directly to the window(s).

zeckensack
11-23-2004, 08:49 AM
Jason666,
please make sure that your window class (the structure you pass to RegisterClass) has the CS_OWNDC style, and a meaningful hInstance.

Jason666
11-23-2004, 06:19 PM
l_belev: If I only use one RC, it does not render to the viewports correctly. This is rendering to bitmaps, not direct to windows. The viewports are 4 seperate TImage components from Delphi, so I prefer to render to a bitmap and then have the ability to draw (BitBlt) to the TImages.

rgpc: No worries without hardware acceleration. Generic software mode is fast enough for my needs in this app. Even if I did render direct to the windows it wouldn't be accelerated anyway right? In my past OpenGL apps, to get hardware acceleration I had to use fullscreen mode, so smaller sub-windows would not be accelerated anyway, right?

zeckensack: That C stuff has no meaning to a Delphi app. Well it may, but it is well hidden in the background, so I never have needed to manually worry about the window api creation stuff.

Thanks to everyone who replied with suggestions. Now that the 4 displays render OK, I can start on the real workings of the app.

Jason.

zeckensack
11-24-2004, 03:40 AM
Originally posted by Jason666:
zeckensack: That C stuff has no meaning to a Delphi app. Well it may, but it is well hidden in the background, so I never have needed to manually worry about the window api creation stuff.Well, I've never worked with Delphi, but I hope this gets handled correctly.
A "standard" window is not entirely fit for OpenGL rendering. Things may start to break when it receives a WM_PAINT message (and it inevitably will at some point).

Maybe Tom Nuydens, our local Delphi guru, can tackle this.

Aeluned
11-24-2004, 09:48 AM
l_belev: If I only use one RC, it does not render to the viewports correctly.
what do you mean? how is it not correct? I think you should be investigating that. l_belev is right, you shouldn't be using multiple render targets for all of the reasons he mentioned (and more importantly it's not needed here). If all you're trying to do is render multiple views all you need to do is render the scene once for each view and change the viewport and camera position/rotation for each view.

I don't think you're going about this the right way.



so smaller sub-windows would not be accelerated anyway, right?
I don't think that's true.

rgpc
11-24-2004, 03:16 PM
Originally posted by Jason666:
In my past OpenGL apps, to get hardware acceleration I had to use fullscreen mode, so smaller sub-windows would not be accelerated anyway, right?
That was true in the days of Voodoo2's. It hasn't been the case for quite some time though.

I would recommend you look at some of the examples on delphi 3d (http://www.delphi3d.net) and see how Tom does it. I would expect that with Delphi you should be able to override the WM_PAINT event to overcome any problems that message may create.

l_belev
11-24-2004, 04:17 PM
Even if you render into several separate bitmaps and/or windows, you still can use only one context. There is no necessity for one-to-one correspondence between the contexts and the drawables (windows/bitmaps/pbuffers) if they are compatible (have identical pixel formats). The rendering context is exactly the aggregate of all the rendering states - this and nothing else. It is not bound to a particular drawable - you can use one context with many drawables and many contexts with single drawable (only not simultaneously).

Jason666
11-24-2004, 05:07 PM
Unless I use a unique DC and RC for the calls

cameraRC:=wglCreateContext(cameradc);
leftRC:=wglCreateContext(leftDC);
etc

Then the windows show up blank or 3 blank and one that half renders the image with incorrect colors.

I will have a look at Delphi3D and see if I can get any ideas/hints from there.

If it is possible to get accelerated displays in non full-screen windows I am definately interested.

Thanks for the pointers,
Jason.

V-man
11-24-2004, 05:15 PM
Originally posted by Jason666:
zeckensack: That C stuff has no meaning to a Delphi app. Well it may, but it is well hidden in the background, so I never have needed to manually worry about the window api creation stuff.
It's not C stuff. It's standard Windows programming. There is a difference between programming language and a API.

You absolutly need it for Win9x generation.
For WinNT generation, I don't know, but I would recommend putting it in.

There isn't much point in rendering to a bitmap anymore, if ever. You can copy the framebuffer to a texture, and render the scene with a texture mapped quad.