javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Posted
2005-01-11 08:06:12 » |
|
----------------------------------------------------------- SHORT VERSION: ----------------------------------------------------------- From the docs NOTE: The Pbuffer will have its own context that shares display lists and textures with the Display context (if it is created). How can I share textures and display lists between PBuffers without creating a Display() ?----------------------------------------------------------- LONG VERSION: ----------------------------------------------------------- I'm going on with my SGL (Simple Graphics Library) which is more or less a simple lwjgl based Java2D replacement. It already draws polys and animated images (textured quads) with affinetransforms, alpha compositing etc. It will handle java2d shapes soon. SGL makes wide use of PBuffers. The speed is fantastic and the API is really simple. Here's a sample: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| SGLBitmap sb0 = new SGLBitmap(512,256,0); SGLBitmap sb1 = new SGLBitmap(220,200,0); SGLImage si = new SGLImage(bufferedimage); int loops = 1000; sb0.startTimer(); for (int i=0; i<loops; i++) { sb0.setTransform(AffineTransform.getRotateInstance(-i/10f,100,50)); sb0.setColor(new Color(220,11,11,10)); sb0.fillRect(0,0,180,120); sb0.setColor(Color.white); sb0.drawRect(0,0,190,130); sb0.setColor(new Color(220,240,150,12)); sb0.fillRect(20,20,110,120); sb0.drawImage(si,30,10); sb1.setColor(Color.blue); sb1.fillRect(0,0,80,20); sb1.setColor(Color.yellow); sb1.drawRect(0,0,90,30); sb1.drawImage(si,0,0); } float t = sb0.endTimer(); |
Now I discovered that the PBuffers constructed without a Display do not share textures and display lists and that's a problem as they must be shared between SGLBitmaps (each SGLBitmap holds a PBuffer). From the docs NOTE: The Pbuffer will have its own context that shares display lists and textures with the Display context (if it is created).
As SGL will work both onscreen and offscreen, I would need to share display lists and textures without creating a Display(). Maybe the LWJGL Pbuffers constructor would need a boolean switch in order to allow the sharing of display lists and textures.
|
|
|
|
princec
|
 |
«
Reply #1 - Posted
2005-01-11 09:55:16 » |
|
I ought to point out that display lists are broken in a number of cheapass GL drivers. Cas 
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #2 - Posted
2005-01-11 11:07:23 » |
|
No problems with glists or poor drivers. I'll have a fallback-to-java2d mode in case of problems.
The main question remains the same: how can I share textures and display lists between PBuffers without creating a Display() ?
I suppose this involves a little change in LWJGL, isn't it ? I think it would be a nice feature.
|
|
|
|
Games published by our own members! Check 'em out!
|
|
elias
|
 |
«
Reply #3 - Posted
2005-01-11 11:10:30 » |
|
*POOF* - wish granted. You can now specify a Pbuffer to share with in the Pbuffer constructor.
- elias
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #4 - Posted
2005-01-11 11:21:19 » |
|
FABULOUS  Where can I download the new magic ?
|
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #6 - Posted
2005-01-11 12:38:40 » |
|
Thanks Elias,
I'll give it a run. I hope to see the new feature in the official distribution soon.
Cheers,
Mik
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #7 - Posted
2005-01-13 06:36:08 » |
|
Hi Elias, it seems that the new Pbuffer shared contex does not work. here's my initialization code: Code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
static Pbuffer sc_pbuffer = null; if (sc_pbuffer == null) { sc_pbuffer = new Pbuffer(1, 1, new PixelFormat(8, 24, 0, samples), null, null); sc_pbuffer.makeCurrent(); } pbuffer = new Pbuffer(width, height, new PixelFormat(8, 24, 0, samples), null, sc_pbuffer); pbuffer.makeCurrent(); |
Mik
|
|
|
|
elias
|
 |
«
Reply #8 - Posted
2005-01-13 11:07:36 » |
|
I need more than that. Is there an error message? If not, are the textures not shared? If they aren't, does it work if you create a Display first?
- elias
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #9 - Posted
2005-01-13 11:35:00 » |
|
Using the "new" Pbuffer shared context:
- No error messages. - Textures are not shared.
Using the "usual" Display shared context:
- No error messages. - Textures shared correctly. - Error message if I try to create a Pbuffer with another Pbuffer context after initializing the Display (ok: this works as expected). The message: org.lwjgl.LWJGLException: Could not share buffer context. at org.lwjgl.opengl.Win32Display.nCreatePbuffer(Native Method) at org.lwjgl.opengl.Win32Display.createPbuffer(Win32Display.java:115) at org.lwjgl.opengl.Pbuffer.createPbuffer(Pbuffer.java:187)
Mik
|
|
|
|
Games published by our own members! Check 'em out!
|
|
elias
|
 |
«
Reply #10 - Posted
2005-01-13 11:51:29 » |
|
Strange. However, from the docs for wglShareLists it seems that only new objects are shared. Have you tried this sequence:
1. Create a pbuffer 2. Create another pbuffer shared with the first 3. Create a texture in pbuffer1 and render it from pbuffer2.
Does that work?
- elias
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #11 - Posted
2005-01-13 12:05:37 » |
|
That's exacly what I'm doing:
- create sc_Pbuffer with a null shared pbuffer reference.
- create Pbuffer0 with a reference to sc_Pbuffer - create textures - create Pbuffer1 with a reference to sc_Pbuffer - make Pbuffer0 current - draw textures - make Pbuffer1 current - draw textures
the textures only appear in Pbuffer0
|
|
|
|
elias
|
 |
«
Reply #12 - Posted
2005-01-14 05:39:54 » |
|
That's exacly what I'm doing:
- create sc_Pbuffer with a null shared pbuffer reference.
- create Pbuffer0 with a reference to sc_Pbuffer - create textures - create Pbuffer1 with a reference to sc_Pbuffer - make Pbuffer0 current - draw textures - make Pbuffer1 current - draw textures
the textures only appear in Pbuffer0
Not quite. You need to try to create textures _after_ both pbuffer0 and pbuffer1 has been created (and shared). Better yet, try to limit the case to only pbuffer0 and pbuffer1, leaving out the sc_pbuffer. - elias
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #13 - Posted
2005-01-14 05:54:02 » |
|
I did almost any kind of try. It doesn't work.
The only way to make it work is to open a Display first, which is of course not a solution (well at least for me).
|
|
|
|
Spasi
|
 |
«
Reply #14 - Posted
2005-01-14 07:42:58 » |
|
You need to try to create textures _after_ both pbuffer0 and pbuffer1 has been created (and shared). Wow, are you sure? I didn't know that. Is that the case with normal framebuffer-pbuffer sharing too?
|
|
|
|
elias
|
 |
«
Reply #15 - Posted
2005-01-14 08:24:38 » |
|
I'm not sure, I'm just trying to narrow down the possible causes.
- elias
|
|
|
|
elias
|
 |
«
Reply #16 - Posted
2005-01-14 09:05:27 » |
|
Javazoid: As an alternative implementation, have you considered having one single pbuffer and a texture for each image? If you draw on one image at a time it will have the same cost. If you draw on multiple images, they will have to be drawn to the pbuffer first (and the old one transferred to its texture).
- elias
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #17 - Posted
2005-01-14 09:11:48 » |
|
I have several Pbuffers wrapped into SGLBitmaps. they have different sizes and I can draw textures (SGLImages) to one of them at unpredictable times.
SGLImages themselves can be allocated everywhere in the code and still they can be traced into any allocated SGLBitmap. This makes the sharedcontext so important.
|
|
|
|
elias
|
 |
«
Reply #18 - Posted
2005-01-14 10:02:47 » |
|
Yes, I understand that. What I was trying to say is why not make the SGLBitmaps textures too and only use one pbuffer (and therefore one context)? When a SGLBitmap is being used, it need to draw itself onto the pbuffer first if it is not already there. The old SGLImage on the pbuffer will need to be flushed to its texture. And finally, if some SGLBitmap is created that is larger than the current pbuffer, flush it and re-create a pbuffer of the right size. The OpenGL drivers are probably more tuned to multiple textures than to multiple pbuffers anyway.
- elias
|
|
|
|
elias
|
 |
«
Reply #19 - Posted
2005-01-14 10:09:41 » |
|
Oh, and remember that Pbuffer contents can be lost, while textures are always preserved.
- elias
|
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #21 - Posted
2005-01-14 11:28:15 » |
|
I'm going to send you the sources of my implementation. It's pretty straighforward. Send me an email address at mik@classx.it I'm very new to OpenGL sorry. Anyway you are suggesting me to enable render-to-texture on my Pbuffers ? That's what I've got: - create a Pbuffer (one static instance). - SGLBitmap holds a texture which is the one that must be bound to the Pbuffer. - SGLImage holds a texture. This one is constructed my making the PBuffer current. - SGLBitmap.drawImage(SGLImage) will draw the texture to the Pbuffer. - I can grab the SGLBitmap texture pixels through glGetTexImage() from the texture bound to the Pbuffer. Mmmh, some nice things: - I couldn't care less if the PBuffer loses its contents. - no context switch. Very good for speed. the bad: I have to rewrite everything. Q: if the design above is right, can you tell me how glGetTexImage() perform compared to glReadPixels() ? Mik PS your http://odense.kollegienet.dk/~naur/lwjgl_14012005.zip WORKS very well! MANY THANKS!
|
|
|
|
princec
|
 |
«
Reply #22 - Posted
2005-01-14 11:31:40 » |
|
GetTexImage = very very fast, copies from VRAM to VRAM. ReadPixels = very very slow, reads the wrong way across the AGP buffer, aaaagh! Cas 
|
|
|
|
elias
|
 |
«
Reply #23 - Posted
2005-01-14 11:45:16 » |
|
That's more or less right I think. To clarify, I'd probably make a SGLBitmap and SGLImage the same, say, SGLGraphics. It would work something like this:
- As an initialization, create the singleton pbuffer and make it current. There's no context switches from now on, since that single pbuffer is always used. - The "active" SGLGraphics is stored as a singleton variable along with the pbuffer. This is for caching. - When a WGLGraphics is constructed, it is assigned a texture id. - SGLGraphics.flush() uses either render-to-texture or simply glCopyTexImage2D to copy the contents of the pbuffer to its texture. Render-to-texture is very fast, but even glCopyTexImage2D should be much faster than glReadPixels or glGetTexImage(). - SGLGraphics.makeActive() uses a quad or something to draw the entire SGLGraphics texture onto the pbuffer, ready for changes. Additionally, it sets the active SGLGraphics to itself. - SGLGraphics.drawSomething() does the following: 1. if (this != active_sglgraphics) { active_sglgraphics.flush(); makeActive(); } 2. Draw the desired texture or primitive.
To actually get at the pixel data from a SGLGraphics you'll need to use glGetTexImage.
Hope that makes it clear.
- elias
|
|
|
|
elias
|
 |
«
Reply #24 - Posted
2005-01-14 11:50:37 » |
|
GetTexImage = very very fast, copies from VRAM to VRAM. ReadPixels = very very slow, reads the wrong way across the AGP buffer, aaaagh! Cas  Cas is slightly wrong here. GetTexImage and ReadPixels are probably equally fast, depending on whether the texture is cached in system ram or not. It's CopyTexImage2D that is (potentially) VRAM to VRAM and therefore fast. - elias
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #25 - Posted
2005-01-14 12:02:34 » |
|
Thanks for the hints. What happens if the several SGBraphics have different sizes ? I would be forced to re-allocate the PBuffer, right ?
|
|
|
|
Spasi
|
 |
«
Reply #26 - Posted
2005-01-14 12:20:22 » |
|
I'd like to add that render-to-texture is not an option in such a system, because:
A) When pbuffer contents get lost, the texture contents will be lost too. render-to-texture is more appropriate for dynamic textures that get refreshed each frame. B) How can multiple textures be created from a single render-to-texture pbuffer? Only by making it huge and packing multiple images. Not very nice.
So, CopyTexImage should be the best option and more than fast enough too.
|
|
|
|
princec
|
 |
«
Reply #27 - Posted
2005-01-14 12:50:18 » |
|
Thanks for the correction smartass  Cas 
|
|
|
|
Spasi
|
 |
«
Reply #28 - Posted
2005-01-14 17:22:54 » |
|
Elias, your last change broke getPbufferCaps, it always returns 0. Pbuffers work great if you ignore it though.
|
|
|
|
javazoid
Junior Devvie  
Where's Flender?
|
 |
«
Reply #29 - Posted
2005-01-15 08:19:25 » |
|
Elias,
I implemented the SGLGraphics as suggested. There was a little design problem in your description. No problem, I fixed it today.
I benchmarked both implementations with 20000 loops of misc graphics:
2 bitmap/graphics switches per loop
SGLGraphics gives: Time s. 11.46202 Fps. 1744.8932
SGLBitmap: Time s. 9.126782 Fps. 2191.3528
4 bitmap/graphics switches per loop
SGLGraphics gives: Time s. 22.803085 Fps. 877.0743
SGLBitmap: Time s. 14.629756 Fps. 1367.0768
From the numbers the SGLBitmap seems to be quite better. The reason is the number of graphics copys from/to the pbuffer needed from SGLGraphics.
Even more, with SGLGraphics, when the pbuffer loses it contents, even the textures in its context go away. This forces me to reallocate every texture in the "new" pbuffer (including the SGLGraphics backbuffer).
From the SGLBitmap side, when the pbuffer gets lost I can recreate it and ask SGLImages to re-create their textures at drawImage() time.
Anyway, the Pbuffers shared context works as expected and this is a great feature.
However Pbuffer flushing is still a problem. For this reason I would suggest what I call a "missing feature": create an invisible Display i.e. by putting a boolean in the constructor and a pair of hide()/show() methods.
This would be useful in order to share the Display context between subsequent Pbuffer allocations in order to keep the textures when the Pbuffers get lost. Nice, isn't it ?
|
|
|
|
|