Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Obligatory 'loading resources on a thread' post.  (Read 3660 times)

0 Members and 1 Guest are viewing this topic.

olie258

  • Newbie
  • *
  • Posts: 5
    • View Profile
Obligatory 'loading resources on a thread' post.
« on: January 29, 2013, 10:49:26 pm »
Hello there,

I'm attempting to use a worker thread load textures and subsequently use these textures on the main thread but I'm running into a problem, specifically that these textures do not appear to be rendering.

I've included a minimal example that reproduces the problem below. I wonder if anyone can provide some insight into what may be going wrong here?

I'm using the January 29th JSFML test release on Windows (64 bit) with Intel HD Graphics 4000 integrated graphics.

import java.io.IOException;
import java.nio.file.Paths;
import java.util.concurrent.ConcurrentHashMap;

import org.jsfml.graphics.*;
import org.jsfml.window.*;
import org.jsfml.window.event.Event;

public class Example {
        // Texture cache
        public static final ConcurrentHashMap<String, ConstTexture> cache =
                new ConcurrentHashMap<String, ConstTexture>();
       
        private static final String KEY = "key_string";
               
        public static void main(String[] args) {
                // Initialise window
                RenderWindow renderWindow = new RenderWindow();
                renderWindow.create(new VideoMode(800, 600), "Eg.");
               
                // On another thread, create a Texture from an image file then cache it
                Thread worker = new Thread(new Runnable() {
                        public void run() {
                                Texture texture = new Texture();
                                try {
                                        texture.loadFromFile(Paths.get("canyonlands_large.jpg"));
                                } catch (IOException e) {
                                        e.printStackTrace();
                                }
                                cache.put(KEY, texture);
                        }
                });
               
                worker.start();
                               
                // Wait for the image to be loaded and
                while (worker.isAlive()) {
                        //...Show loading screen...
                }
               
                // Create a sprite using the loaded texture
                Sprite sprite = new Sprite(cache.get(KEY));
               
                // Main loop
                while (renderWindow.isOpen()) {
                        // Handle events
                        for (Event e : renderWindow.pollEvents()) {
                                if (e.type == Event.Type.CLOSED)
                                        renderWindow.close();
                        }
                       
                        // Draw sprite
                        renderWindow.clear(Color.BLACK);
                        renderWindow.draw(sprite);
                        renderWindow.display();
                }
        }
}

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Obligatory 'loading resources on a thread' post.
« Reply #1 on: January 30, 2013, 07:58:44 am »
Oh, finally somebody giving this a try. :D

The problem is that the texture is being created "out of context". The OpenGL context created by your RenderWindow is only active in the main thread, so the solution is to activate it in the worker thread before the texture is being loaded.

This should work, but I have actually never tried it so far:
import org.jsfml.window.Context;
[...]

        // On another thread, create a Texture from an image file then cache it
        Thread worker = new Thread(new Runnable() {
            public void run() {
                Context context = new Context();
                Texture texture = new Texture();
                try {
                    texture.loadFromFile(Paths.get("canyonlands_large.jpg"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                cache.put(KEY, texture);
            }
        });

By creating an instance of the Context class in the worker thread and by keeping it alive (by defining it for the entire run() method), the OpenGL context is activated for that thread and your texture should be created properly.

This should definitely be worth a Wiki article.
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32097
    • View Profile
    • SFML's website
    • Email
Re: Obligatory 'loading resources on a thread' post.
« Reply #2 on: January 30, 2013, 08:11:22 am »
You're not supposed to do that, SFML takes care of creating/activating a context when there's none in the current thread.

Maybe a glFlush is needed after the textures are loaded?
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Obligatory 'loading resources on a thread' post.
« Reply #3 on: January 30, 2013, 08:45:03 am »
I can also confirm that the code initially posted works fine here without any context activation
(Win7 64-bit, Intel HD Graphics), but I did have a similar problem on Debian a few days ago.

I'm not sure about glFlush, but you can't call it from Java directly unless I provide it. Why would that be needed on some systems and not on others?
« Last Edit: January 30, 2013, 08:46:38 am by pdinklag »
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32097
    • View Profile
    • SFML's website
    • Email
Re: Obligatory 'loading resources on a thread' post.
« Reply #4 on: January 30, 2013, 08:50:06 am »
Because it depends on what the driver internally does ;)

Also make sure that your binding uses the latest SFML sources, there was a fix about that for Linux after the RC.
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Obligatory 'loading resources on a thread' post.
« Reply #5 on: January 30, 2013, 09:00:18 am »
I was using a rather old binding version on that Debian I spoke about, so I didn't really bother investigating.
The test release I did yesterday contains the latest SFML 2 snapshot.

So I guess I will need to provide a way for Java users to call glFlush() for cases like this. IMO it would fit best as a static method in the "Context" class (it can't be a class-less definiton like in C), because I'd rather not create GL classes.
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32097
    • View Profile
    • SFML's website
    • Email
Re: Obligatory 'loading resources on a thread' post.
« Reply #6 on: January 30, 2013, 09:04:03 am »
You should test it first, we're not even sure that glFlush is the solution.
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Obligatory 'loading resources on a thread' post.
« Reply #7 on: January 30, 2013, 09:15:59 am »
Hard to do if I can't reproduce it, as said, the code olie258 posted works just fine here. I hope I can reproduce it on those Debian machines at university. I will try that tomorrow.
JSFML - The Java binding to SFML.

olie258

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Obligatory 'loading resources on a thread' post.
« Reply #8 on: January 31, 2013, 12:13:40 am »
Thanks, guys, for the speedy replies. OpenGL is not my area of expertise so I really appreciate the assistance.

pdinklag - if you're able to repro the problem and you make any progress towards solving it I'd be eager to hear about it. Thank you for looking into it.

Even though Laurent has said it's a no-no, I tried out the Context instance solution this evening (just for science), and I was eventually able to get it working by calling setActive(true) then creating the Texture and finally calling setActive(false). Something like:

public void run() {
        Context context = new Context();
       
        try {
                context.setActive(true);
                Texture texture = new Texture();
                texture.loadFromFile(Paths.get("canyonlands_large.jpg"));
                context.setActive(false);
                                       
                cache.put(KEY, texture);

        } catch (Exception e) {
                e.printStackTrace();
        }      
}

Just thought I'd share incase the information is useful in any way.