Multi-thread loading

Hi, everyone.
I currently writing background loader for my assets(scripts, images, videos, etc.) and I start to reading engine sources. I decide to use multicontext feature of GL. I getted _mainWindow from GLViewImpl, but I found that my second context should destroyed before glfwTerminate. I start search where I can get callback or something that indicates to me that context is destroing. And I found that _mainWindow is not destroying via glfwDestroyWindow. Is that ok(my version is 3.7)? And how can I get callback for that event(destroying window)?

Do you actually need to go this far?

Could you just use std::thread to do your pre-loading, perhaps starting early on in AppDelegate?

Edit: If you show a main menu that needs user input that might also buy you some time for pre-loading to be further along as the user might take a second or more to make a decision.

I want to loading my scenes (it’s written using our company script system) by chunks. If I going from one chapter to another, I wont upload those scripts into RAM, and upload textures for next scene in background. In front user should have loading animation and game tips. That’s why I need shareable GL context. Cause our game have about 1GB of assets(textures, videos, animations, sounds).

But second question is still without answer. Is that safe terminating window without destroying?

(Maybe my engish can be a bad. I apologize about that.

I don’t know the answer to your question. Perhaps @ricardo has some thoughts on if terminating the window without destroying is safe.

Well, I will be wait like a Hachiko )

regarding multithreaded loading, please take a look at the “cpp-tests -> texture tests”…we have some examples regarding how to load textures in a different thread… in fact we have an Async API to do that.

I don’t think we have an event for “window destroyed”… but perhaps GLFW has one.

2 Likes

How about this code?

GLViewImpl::~GLViewImpl()
{
    CCLOGINFO("deallocing GLViewImpl: %p", this);
    GLFWEventHandler::setGLViewImpl(nullptr);
    glfwTerminate();
}

Is this safe? Or here should be something like this:

GLViewImpl::~GLViewImpl()
{
    CCLOGINFO("deallocing GLViewImpl: %p", this);
    GLFWEventHandler::setGLViewImpl(nullptr);
    glfwDestroyWindow(_mainWindow);
    glfwTerminate();
}

I wouldn’t know… do you think it should include the glfwDestroyWindow ? If so, please submit a pull request with that change (and the use case), and I’ll merge it. thanks.

IMO this is not necessary, but it’s more clear for understanding what happens in engine.

Also I looked for Async example in tests. But it’s still blocking my main thread when texture is ready to set(it’s a little bit anoingh our customers, cause we targeting to low-end PC). So I changed some codes in GLViewImpl to test feature.

bool GLViewImpl::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor)
{
    setViewName(viewName);
...

    glfwWindowHint(GLFW_VISIBLE, GL_TRUE);
    _mainWindow = glfwCreateWindow(needWidth, neeHeight, _viewName.c_str(), _monitor, nullptr);
    glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
    _slaveWindow = glfwCreateWindow(1, 1, (_viewName + "_slave").c_str(), 0, _mainWindow);
...
}

In my loading scene I doing something like this. This function starting in new std::thread.

void
cLoadingScene::loadingFunction()
{
//Getting from GLViewImpl slave window
    auto neededContext = cApplication::getInstance()->getSlaveWindowContext();
    glfwMakeContextCurrent(neededContext)

    cSceneManager& SceneManager = cSceneManager::getInstance();
    SceneManager.loadScenesFile("locations.xml");
    SceneManager.loadLocationRoots();
//here code start loading (like in sync variant) texture and creating sprites(actually it's our classes inherited from Sprites)
    SceneManager.getSceneByID("")->_layer->loadData();

    _loadingFinished = true;
}

But unfortunelly it’s crashes when first sprite is loading his texture. After some investigation I found that glfwMakeContextCurrent not actually setting context as current for thread. glfwGetCurrentContext(); after set return null. Pointer to slave window is not null. Also I tested slave window as main render window and it’s works perfectly.
I’m stuck and can’t get what is wrong with this code :frowning:

1 Like

Sorry, I’m not familiar with GLFW… but what exactly are you trying to do? load textures in another thread?
You don’t need GLFW to do that… just use pure openGL calls, like what TextureCache is doing.

If I try to upload textures(even with pure calls) with another thread, first of all I should set GLWindow for that thread (achived usually by in calling thread glfwMakeContextCurrent(neededContext) ).

I think that you should forget about the glfw calls. Just use TextureCache's async calls, or if you need something different, take a look at how TextureCache works, and clone it and modify it.

I can’t just forget it )) It’s real chalenge for me now.
So after some investigation I found next thing.

If I doing something like that in GLViewImpl::initWithRect:

std::function<void(void*)> pFunc = [](void* glWindow)
    {

        std::thread thread([glWindow]()
        {
        auto context = glfwCreateWindow(1, 1, "", 0, static_cast<GLFWwindow*>(glWindow));
        glfwMakeContextCurrent(context);
        context = glfwGetCurrentContext();

        });
    };
    pFunc(_mainWindow);
    glfwMakeContextCurrent(_mainWindow);

it’s working well. Thread started new context setted as current for that thread.
But if move pFunc to outside (let’s say in AppDelegate class) an pass this pointer to function in the init(body of function stays same):

if (beforeFinalInitCallback)
    {
        (*beforeFinalInitCallback)(_mainWindow);
    }
    glfwMakeContextCurrent(_mainWindow);

I will get nullptr in auto context = glfwCreateWindow(1, 1, "", 0, static_cast<GLFWwindow*>(glWindow)); line.

Even if I remove thread wrapping (all thread code will be invoked in main thread, I thought this is sync problem) I still get nullptr. And it’s so weird.

I’m working on solution with TextureCache, but those nullptr’s do not give me the rest(I don’t like unresolved problems :frowning: )

anyone? please :weary: