Is it possible to import BSL shaders from core thread?


#1

So continuing my work on the qml scenegraph adaptation plugin for bs::framework, I’ve encountered an issue with a function that should prepare a material.

void VertexColorMaterial::preparePipeline(PipelineState *pipelineState)
{
auto shader = std::static_pointer_cast<bs::ct::Shader>(bs::gImporter()._import("vertexcolor.bsl")->getCore());
pipelineState->shaders.material = bs::ct::Material::create(shader);
}

produces:

/Source/Foundation/bsfCore/CoreThread/BsCoreObject.cpp:64: virtual void bs::CoreObject::initialize(): Assertion `BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && 
"Cannot initialize sim thread object from core thread."' failed.

Right from the beginning, I’ve decided that all used bsf objects should be core-thread ones, since the QT plugin will work in concert with bsf’s ct::RendererExtension plugin.

I do realize that ct::GpuProgram exists, but it’d require use of API-specific shader sources.

I’ve also did try to use importAsync in hopes of shader import operation getting put on the proper thread, and just doing a brain-dead blockUntilComplete, it seems that importAsync might have a bug ?

No sync data is available. Cannot block until AsyncOp is complete.
                 in void bs::AsyncOp::blockUntilComplete() const [...Source/Foundation/bsfUtility/Threading/BsAsyncOp.cpp:34]

Why do I think we have a bug there.


#2

Yep AsyncOp::blockUntilComplete is bugged, I’m aware of that issue.

It’s not possible to import shaders directly on the core thread. But if you take a look at RendererMaterial it is used for transparently loading up shaders for use by the renderer (and therefore on the core thread). It’s only available in the RenderBeast plugin but it should be possible to come up with your own version, or perhaps make RendererMaterial a part of the core (I’ve been meaning to make renderer more extensible anyway).


#3

Well looking at the codebase, the BsRendererMaterial.h is located under bsfEngine/Renderer/ :slight_smile:

As for loading bsl shaders from the core thread, I’ve looked at RendererMaterial::RendererMaterial() mMetaData.shader which is filled by RendererMaterialManager::initOnCore, but initOnCore is done by:
gCoreThread().queueCommand(std::bind(&RendererMaterialManager::initOnCore, shaders), CTQF_InternalQueue); so in essence, it still needs to be done from non-core thread.


#4

Ah nice I guess I already moved it. Yes, shader import cannot be done from the core thread but RendererMaterial hides that from you.


#5

So, let me see if my understanding is valid :slight_smile:

I have a bsf plugin called QuickGUI that is split into 2 parts ( non-core QMLManager and ct::QMLRenderer ), now, I add all default materials the QML renderer will need using RendererMaterial

namespace bs::ct {
class QmlVertexColorMat : public RendererMaterial<QmlVertexColorMat>
{
    RMAT_DEF("vertexcolor.bsl")
public:
    QmlVertexColorMat();
private:
    GpuParamMat4 mMVP;
    GpuParamFloat mElementOpacity;
};
}

Then Qt-side scene-graph plugin ( which is running on core thread ) has to access QMLManager, and retrieve required materials from it.

I think that should work ?


#6

You shouldn’t need to use an intermediate class to retrieve the RendererMaterial. You can just do QmlVertexColorMat::get().


#7

Huh, even though the first instantiation in the whole program will be done from the ct ?

It seems that all those material shaders are registered in static calls to RendererMaterialManager::_registerMaterial and when RendererMaterialManager is constructed for the first time ( non-core thread ) it will call BuiltinResources::getShader on them.

Now the plugins are loaded after the RendererMaterialManager::startUp() has been ran, so the static initializers ran by shared library/plugins are not affecting RendererMaterialManager constructor call, and so the getShader on BuiltinResources will not get called, and thus the bsl shader will not be compiled ?


#8

Indeed. You could edit START_UP_DESC and add a list of plugins to load on start-up (see START_UP_DESC::importers which does the same currently). This way the plugin would get loaded before RendererMaterialManager::startUp. Or modify the manager to allow new materials to be added after start-up.