ebeckers's picture

Fast texture loading

Hi

I'm looking for the fastest way to load an image into a texture
At the moment I use 2 threads.
Thread 1 uses the C# Image.FromFile class to load the image into memory like this:

Bitmap _bitmap;
BitmapData _bmpData;
int  _texture;
void LoadImage()
{
   _bitmap = new Bitmap(fileName);
   _bmpData = _bitmap.LockBits(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}

Thread 2 is the main thread which contains the renderloop.
It uses the GL.TexImage2D() to copy the image from memory into the texture.

void Render()
{
     if (_bitmap != null)
      {
        GL.GenTextures(1, out _texture);
        GL.BindTexture(TextureTarget.Texture2D, _texture);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, _bmpData.Width, _bmpData.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, _bmpData.Scan0);
        _bitmap.UnlockBits(_bmpData);
        _bitmap.Dispose();
        _bitmap = null;
      }
 
    // render the texture...
}

However sometimes i need to load a couple of new textures and then the calls to GL.TexImage2D() are stalling the renderloop and so the FPS drops. Is there a better/faster way to do this? I read something about PBO's, but i think this is only helpfull if you need to update a texture with new pixel data and does not really improve the first-time loading.

Another option would be multi-threading so textures can be created and loaded into thread 1 while rendering takes place in thread 2. Is there any simple example based on GameWindow which shows how to do multi threading with openTK ?

Regards

Erwin


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kvark's picture

1. Multithreading doesn't help you as stalling GPU pipeline by one thread will stop rendering by another.

2. PBO is really what you need. It's just a way to use async data transfer between GPU & CPU memory for textures.

the Fiddler's picture

You can get multithreading to work by creating a second GraphicsContext and loading the textures there. Contexts are shared by default in OpenTK, so you can use the texture from either context.

You'll have to use SVN from trunk for that to work reliably, however (or wait for 1.0 beta 1). The code is trivial:

// on the second thread:
NativeWindow window = new NativeWindow(); // This is invisible by default, unless you call Visible = true
GraphicsContext context = new GraphicsContext(window, GraphicsMode.Default, ...);
int id = GL.GenTexture();
GL.TexImage2D(...);

Once the loading is complete, you'll have to notify the first thread so that it can use the new texture.

1.0 beta 1 will also contain multithreading examples.

PBOs allow you to load data asynchronously from the same context and also work great - provided your driver supports them (i.e. you can forget about Intel cards).

ebeckers's picture

Hi
Thanks for you answer, i'm currently using the following code you suggested and this seems to work great.
One small problem however is that the 'hidden' window is not hidden. It becomes active in front of the
main window. Any suggestions would be welcome..

/ on the second thread:
NativeWindow window = new NativeWindow(); // This is invisible by default, unless you call Visible = true
GraphicsContext context = new GraphicsContext(window, GraphicsMode.Default, ...);
int id = GL.GenTexture();
GL.TexImage2D(...);
the Fiddler's picture

This has been changed since 0.9.9-3 was released (the window no longer becomes visible automatically, in order to support this use case).

As a temporary workaround, you can set Visible = false after creating the window.

ebeckers's picture

Unfortunaly Visible=false; has no effect. The window stays visible and on top
(Note this is under ubuntu 9.10 and todays svn)

the Fiddler's picture

Should be fixed in SVN rev. 2476 (window is not mapped until Visible = true is called).

You need to call ProcessEvents() for Visible = false; to have any effect.

ebeckers's picture

Works great now
thanks for the quick fix!

However i still have 2 problems left:
1. Fullscreen mode is not working anymore when using shared contexts. If i put my main window in full screen, it stays in windowed mode
2. The shared contexts are not working under windows. Everything seems ok, no errors or exceptions but the textures loaded in thread #2 dont show when used from the renderloop in thread #1

Erwin

the Fiddler's picture

Please file bug reports for those issues.

It would really help if you could test with a debug version of OpenTK.dll and attach the debug output to the bugs. (You can see this in the "application output" window in both Visual Studio and MonoDevelop, or you can create a debug trace listener as described here).

ebeckers's picture

I managed to get the sharedcontexts to work under windows by rearranging the order in which threads&contexts are created
Still fullscreen is a no go with shared contexts.
The debug output below shows that first the main (GameWindow) is created and set to fullscreen
After that the next NativeWindow is created with a new GraphicsContext on another thread
All looks good, however the GameWindow does not run in fullscreen mode

Size: 2304
System:
    Linux
 
 
 
 
Initializing threaded X11: 1.
Display connection: 142606832, Screen count: 1
Detected configuration: Linux / Mono
Initializing threaded X: success.
Creating default GraphicsMode (24, 16, 0, 0, 0, 2, False).
Creating X11GLNative window.
    Display: 142625560, Screen 0, Root window: 423
    Registering atoms.
    Bits per pixel: 24
    Depth: 16
    Display: 142606832, Screen: 0, RootWindow: 423
    Getting FB config.
    Bits per pixel: 24
    Depth: 16
    Falling back to glXChooseVisual.
    Opening render window... Initalizing X11 input driver.
        First keycode: 8, last 255
        6 keysyms per keycode.
    X11GLNative window created successfully (id: 73400322).
GameWindow 73400322 changing WindowState from Normal to Fullscreen.
Making WindowBorder resizable.
Creating GraphicsContext.
    GraphicsMode: Index: 39, Color: 24 (8880), Depth: 24, Stencil: 0, Samples: 0, Accum: 64 (16161616), Buffers: 2, Stereo: False
    IWindowInfo: X11.WindowInfo: Display 142625560, Screen 0, Handle 73400322, Parent: (null)
    GraphicsContextFlags: Default
    Requested version: 1.0
    Creating X11GLContext context: direct, not shared... 
    Creating temporary context to load GLX extensions.
    Loading extensions for OpenTK.Platform.X11.Glx... 2 extensions loaded in 2.308 ms.
    Using legacy context creation... Context created (id: 143888872).
Making context 143888872 current on thread 1 (Display: 142625560, Screen: 0, Window: 73400322)... done!
Loading extensions for OpenTK.Platform.X11.Glx... 2 extensions loaded in 0.0332 ms.
Context supports vsync: True.
Loading extensions for OpenTK.Graphics.OpenGL.GL... 1900 extensions loaded in 173.8574 ms.
linux:create new window
Creating X11GLNative window.
    Display: 147493376, Screen 0, Root window: 423
    Registering atoms.
    Opening render window... Initalizing X11 input driver.
        First keycode: 8, last 255
        6 keysyms per keycode.
    X11GLNative window created successfully (id: 75497474).
linux:create gfx context
Creating GraphicsContext.
    GraphicsMode: Index: 39, Color: 24 (8880), Depth: 24, Stencil: 0, Samples: 0, Accum: 64 (16161616), Buffers: 2, Stereo: False
    IWindowInfo: X11.WindowInfo: Display 147493376, Screen 0, Handle 75497474, Parent: (null)
    GraphicsContextFlags: Default
    Requested version: 1.0
    Creating X11GLContext context: direct, shared with (143888872)... 
    Creating temporary context to load GLX extensions.
    Loading extensions for OpenTK.Platform.X11.Glx... 2 extensions loaded in 0.0467 ms.
    Using legacy context creation... Context created (id: 147715688).
linux:set gfx context
Making context 147715688 current on thread 2 (Display: 147493376, Screen: 0, Window: 75497474)... done!
the Fiddler's picture

Bug report please?

Seeing the issue in the bug tracker is a good motivation for fixing it (not to mention I will likely forget about the problem once this thread leaves the frontpage).