GvNodeMaster::Execute() returns error
-
Hi!
In my plugin, I have a MaterialData that owns a GvNodeMaster, and I would like to update the xpresso calculations in that GvNodeMaster. To this end, in the Message() function of the material data, I try to call Execute() on the GvNodeMaster, as follows:GvCalcError err = node_master->Execute(GeGetCurrentThread());
This, however, always returns the error code GV_CALC_ERR_INIT_FAILED or GV_CALC_ERR_NOT_INITIALIZED. Are there some other steps I need to take before I can call Execute() on the node master?
I guess the broader question here is: How do I correctly force an update on the calculations of a GvNodeMaster?
Best regards
/Filip Malmberg -
Hello @Filip,
Thank you for reaching out to us. As always, it is very difficult to answer questions when we are given no or almost no code. Please share executable code examples when possible and also explain what you are doing.
I am assuming this about 3Delight and that you are trying to implement your own
GraphView
based material system? If that is the case there can be countless reasons why this is failing for you and we cannot really help you without your code.GvNodeMaster::Execute
is a convenience method that wrapsQueryCalculation()
,InitCalculation()
,Calculate()
andFreeCalculation()
. Doing it manually would have the advantage that you know where it fails. See end of posting for a brief code example I wrote.- I had a look at our code base and I found this in the context of Python. I think this is only related to Python (there is some GIL stuff in the method which I have removed in the snippet below), but it might be worth a try (nulling the thread).
// ... // ITEM#315815[python] Execute for GvNodeMaster module doesn't work properly BaseThread* thread = nullptr; // ... err = nodeMaster->Execute(thread); // ...
Cheers,
FerdinandCode
This is untested code I wrote blindly. There might be errors, but the general idea should get accross:
/// A method in your plugin that executes a calculation on #master with #bt and #flags. GvCalcError MyStuff::Execute(GvNodeMaster* const master, BaseThread* const bt, GvCalcFlags flags) { // Check inputs and allocate query and init objects. if (!master || !bt) return GV_CALC_ERR_INVALID_PARAMETER; GvQuery* query = master->AllocQuery(); GvInit* init = master->AllocInit(); bool initCalc = false; // Free things when we leave the scope. finally { if (initCalc) master->FreeCalculation(); if (query) master->FreeQuery(query); if (init) master->FreeInit(init); } // We could not allocate memory for the query or init object. if (!query || !init) { return GV_CALC_ERR_NO_MEMORY; } // Set the query and init objects and then check if we can calc and then init the calc. The naming // of the methods is a bit confusing, but that is the order in which you have to call them. init->cpu_count = 1; init->flags = flags; GvCalcError err = master->QueryCalculation(query, bt); if (err) return err; initCalc = true; err = master->InitCalculation(init, bt); if (err) return err; // Finally, try to calculate the expression. When the calculation fails, try to dig out a more // specific error from the run object. err = master->Calculate(0); if (err) { if (GvRun* run = master->GetRun()) { if (run->GetError() != GV_CALC_ERR_NONE) { err = run->GetError(); } } return err; } return GV_CALC_ERR_NONE; }
-
Here is the code for my MaterialData plugin:
#include "DL_NodeMaterial.h" #include "customgui_matpreview.h" GvNodeMaster* DL_NodeMaterial::GetNodeMaster() { return node_master; } Bool DL_NodeMaterial::Init(GeListNode* node) { node_master = GvGetWorld()->AllocNodeMaster((BaseList2D*)(this->Get())); //Create a terminal node for this material node_master->CreateNode(node_master->GetRoot(), DL_TERMINAL, nullptr, 200,200); DL_SetDefaultMatpreview((BaseMaterial*)node); return node_master != nullptr; } void DL_NodeMaterial::Free(GeListNode* node) { GvGetWorld()->FreeNodeMaster(node_master); } Bool DL_NodeMaterial::Read(GeListNode *node, HyperFile *hf, Int32 level) { return node_master->ReadObject(hf,true); } Bool DL_NodeMaterial::Write(GeListNode *node, HyperFile *hf) { return node_master->WriteObject(hf); } Bool DL_NodeMaterial::CopyTo(NodeData *dest, GeListNode *snode, GeListNode *dnode, COPYFLAGS flags, AliasTrans *trn) { DL_NodeMaterial* dst = (DL_NodeMaterial*)dest; return node_master->CopyTo(dst->GetNodeMaster(), flags, trn); } Bool DL_NodeMaterial::Message(GeListNode *node, Int32 type, void *data){ if (GeIsMainThread()) { ApplicationOutput("Main thread"_s); } else { ApplicationOutput("Not main thread"_s); } GvCalcError err = node_master->Execute(GeGetCurrentThread()) ApplicationOutput("Errorcode: "_s); ApplicationOutput(String::IntToString(err)); if (err == GV_CALC_ERR_INIT_FAILED) { ApplicationOutput("Init failed"_s); } if (err == GV_CALC_ERR_NOT_INITIALIZED) { ApplicationOutput("Not initialized"_s); } return MaterialData::Message(node, type, data); } //Initializes resources for rendering. INITRENDERRESULT DL_NodeMaterial::InitRender(BaseMaterial* mat, const InitRenderStruct& irs) { return INITRENDERRESULT::OK; } void DL_NodeMaterial::CalcSurface(BaseMaterial* mat, VolumeData* vd) { Vector diff, spec; vd->IlluminanceSimple(&diff, &spec, 0, 0, 0); vd->col = 0.8*diff; } Bool RegisterDLNodeMaterial(void) { return RegisterMaterialPlugin(ID_DL_NODEMATERIAL, "DL_NodeMaterial"_s, 0, DL_NodeMaterial::Alloc, "DL_NodeMaterial"_s, 0); }
As you can see, it does not do much besides store and maintain a GvNodeMaster, which is then displayed and edited in a separate GeDialog.
In the Message() method, I try to update the XPRESSO calculations of the stored GvNodeMaster, but without success. I tried your suggestions of passing a null-pointer for the thread, but this made no difference.
I am assuming this about 3Delight and that you are trying to implement your own
GraphView
based material system? If that is the case there can be countless reasons why this is failing for you and we cannot really help you without your code.Yes, correct! I have functioning implementation of a basic node material system. When rendering, I parse the GvNodeMaster attached to a material and generate a corresponding OSL-shader graph for the renderer. This works as intended (and I am quite exited about it! ). That part does not require the actual XPRESSO nodes to be evaluated (Execute() :ed), since I am doing the parsing myself.
The reason I am asking about Execute() and performing the XPRESSO calculations is that it would be really nice to be able to drive shader parameters via the existing Cinema 4d xpresso nodes! I understood from the Redshift docs that this is supported for that renderer, so it appears possible. If not, it is not a dealbreaker for me, but it would be a really nice feature to support.
So, any ideas on why the execution of the XPRESSO graph does not work would be really appreciated! In the final implementation, I am not sure that the graph execution would happen in the Message() function of the MaterialData, this is just my first step of trying to get it to work somewhere in the code.
Cheers
/Filip -
Hey @Filip,
Thank you for sharing your code. I really do not want to be pedantic here, but that is not compileable code as it is missing dependencies and resources. I can of course also just have a look at it, but I will not be able to run your project to see and tell you why it fails exactly (because I can debug against the code base of Cinema 4D).
The thing which jumps to the eye is that you do not make sure that you run
GvNodeMaster::Execute
on the main thread. I particularly find theGeGetCurrentThread
a bit iffy here and at a quick glance, I also cannot find a singular case in our code base where call execute like that. Because yourExecute
call is not guarded by anything (message type) it will also fire ALL the time which is not good.Other than that, I would recommend to do what I pointed out before:
- Do not use
Execute
but implement things yourself to see where it fails. - Try to use nullptr for the thread (and also move the Execute inside of the main thread check).
Since you do your own stuff, this could for example fail in the
DL_TERMINAL
node you implemented in addition to some of the init stuff going wrong.Cheers,
Ferdinand - Do not use
-
@ferdinand said in GvNodeMaster::Execute() returns error:
Since you do your own stuff, this could for example fail in the DL_TERMINAL node you implemented in addition to some of the init stuff going wrong.
I have confirmed that the error seems to come from my own GvOperatorData()-plugins (shader nodes). When I delete all my own nodes in the graph and only use the build in c4d xpresso nodes, then GvNodeMaster updates correctly. I will look into this, but now I know where to look! We can mark this as solved for now.
"I can of course also just have a look at it, but I will not be able to run your project to see and tell you why it fails exactly"
At this stage, I was just looking for general feedback on how and when the Execute() function was supposed to work correctly, i.e., if it needed to be called in a special context or similar. This is also why I did not post detailed code initially. I don´t fully agree that detailed code is always needed to discuss an issue, even if it of course sometimes helps in clarifying things! Anyway, thanks a lot for the good support, it is really helpful and I appreciate the availability of this forum - it really helps!Best regards
/Filip