Copy link to clipboard
Copied
Hi. I am running a custom UI in a separate thread. The custom UI sets a global counter when the user clicks on Update button.
Then I call, in the custom UI thread
AEGP_SuiteHandler suites(sP);
suites.AdvAppSuite2()->PF_RefreshAllWindows();
the sP value is set in GlobalSetup as sP = in_data->pica_basicP;
The plan is that I can use the global counter to update a hidden param in UpdateParameterUI to force render.
However, the app is crashing on the line
suites.AdvAppSuite2()->PF_RefreshAllWindows();
Any ideas ?
Copy link to clipboard
Copied
@shachar I'll post some notes from your replies to similar questions. It would be nice to have an example of a custom UI thread and how it interacts with main Adobe app.
in general, the AE API is not thread safe. there are very few calls that are, and they are noted so in the docs. (such as AEGP_CauseIdleRoutinesToBeCalled)
you can only call AE when AE is actually listening, which means either from the ui thread, render thread, or in some occasions, other threads that are created by the iterations suites, but only while executing that suite call from one of the main threads...
lookup AEGP_CheckoutOrRender_ItemFrame_AsyncManager in the docs. you'll either have to adapt your process to that mechnism, or get your frame data from one of the main thread,s and the do you processing on whatever thread you like without calling AE from it.
AND
there are some very few calls that can by made asynchronously. most of
which are rendering related calls that are indeed multi-threaded but are
all executed during a single render call.
the one exception is AEGP_CauseIdleRoutinesToBeCalled(), which is safe to
call from any thread. all other attempts to talk to AE from a custom thread
instead of one of AE's calls will result in catastrophic crashes...
to do what you describe, you should probably use idle_hook to execute your
changes, and instead of storing an AEGP_EffectRef, you should store your
effect's index on the layer, layer id and comp item id. then during idle
hook you lookup the comp, the layer in the comp and the effect on the
layer, to get a fresh AEGP_EffectRefH for usage during that idle hook call.
all other item refs such as AEGP_LayerH and AEGP_CompH are not guaranteed
to keep beyond the scope of a single AE call. (sometimes they do, but often
they don't)
AND
1. AEGP_CauseIdleRoutinesToBeCalled(). it can be called from the non-main
thread (as long as you acquire it from the main thread), and causes idle
hook to be called ASAP. this can shorten your interactive time lag.
2. create your window as an AE panel (see the "panelator" sample project).
i think panels can interact with AE's API during mouse
down/click/draw/ect...
Copy link to clipboard
Copied
welp, that pretty much sums it up...
i'd just add that it's perfectly fine to do ui work in a separate thread, just do the actual ui update on the designated AE evetns. during said event, access your separete thread's data (probably using some mutex). that's it.
Copy link to clipboard
Copied
To make sure I understood this...
In EntryPointFunc() add,
case PF_Cmd_COMPLETELY_GENERAL:
err = RespondtoAEGP(in_data,
out_data,
params,
output,
extra);
Example of RespondToAEGP
static PF_Err
RespondtoAEGP(
PF_InData* in_data,
PF_OutData* out_data,
PF_ParamDef* params[],
PF_LayerDef* output,
void* extraP)
{
PF_Err err = PF_Err_NONE;
AEGP_SuiteHandler suites(in_data->pica_basicP);
// Get data from extraP if needed, else just use shared static var with mutex
// Make changes to Adobe stuff here
return err;
}
And in the custom UI thread add, use static var sP, previously set in GlobalSetup - sP = in_data->pica_basicP;
AEGP_SuiteHandler suites(sP);
suites.UtilitySuite6()->AEGP_CauseIdleRoutinesToBeCalled();
Is that all correct ?
Copy link to clipboard
Copied
I'm missing something basic here. My breakpoint hits on the custom UI thread at
suites.UtilitySuite6()->AEGP_CauseIdleRoutinesToBeCalled();
but the callback in main thread i.e.
RespondtoAEGP
is not called.
Is there a flag or another variable to set/method to be called? Inside RespondtoAEGP, I set a param and refresh Adobe UI in this manner.
params[HIDDEN_ID]->u.fs_d.value = uiData.clicks % 100ull; // So hidden param is updated
params[HIDDEN_ID]->uu.change_flags = PF_ChangeFlag_CHANGED_VALUE;
ERR(suites.ParamUtilsSuite3()->PF_UpdateParamUI(in_data->effect_ref, HIDDEN_ID, params[HIDDEN_ID]));
my_sequence_dataP seqP = *(my_sequence_dataP*)out_data->sequence_data;
// SequenceResetup may get called after this and before render. Not a good idea as some of our data will be deleted.
// paramflag sets SequenceResetup not to delete sequence data
seqP->paramflag = PF_OutFlag_FORCE_RERENDER;
out_data->out_flags |= PF_OutFlag_FORCE_RERENDER;
Copy link to clipboard
Copied
what is the UI thing you're doing? is it just conditionally changing param visibility, or is it a custom param ui/comp ui?
Copy link to clipboard
Copied
I am reading in from a 5x5 matrix with certain presets. I have an Update button also. When the user clicks Update, the matrix contents are saved into a uiData struct.
I want to force render when the Update is pressed. So in the Update callback I have the
AEGP_CauseIdleRoutinesToBeCalled
However, this is not triggering the
PF_Cmd_COMPLETELY_GENERAL
Once its triggered, its easy as I have a static struct that can be accessed by render thread. For the moment I am not worried about race conditions.
I found another thread with a similar issue -
Copy link to clipboard
Copied
AEGP_CauseIdleRoutinesToBeCalled causes idle_hook to be called, not PF_Cmd_COMPLETELY_GENERAL. am i misundertsnaindg something here?
Copy link to clipboard
Copied
I was using the incorrect methods - IdleHook is clearly the wat to go. I read up more on this and I am still having problems with IdleHook. Essentially, IdleHook is not being called.
I pass this struct to IdleHook.
/* instead of storing an AEGP_EffectRef, you should store your effect's index on the layer, layer id and comp item id. then during idle hook you lookup the comp, the layer in the compand the effect on the layer, to get a fresh AEGP_EffectRefH for usage during that idle hook call.
*/
typedef struct {
AEGP_LayerH layerH; // layer
AEGP_EffectIndex effect_indexL;
AEGP_CompH compH;
} IdleHookInfo_t;
Inside GlobalSetup, I obtain these values
sP = in_data->pica_basicP;
if (in_data->appl_id != 'PrMr') {
// This is only needed for the AEGP suites, which Premiere Pro doesn't support
ERR(suites.UtilitySuite3()->AEGP_RegisterWithAEGP(NULL, Product_MatchName, &S_my_id));
AEGP_SuiteHandler suites(in_data->pica_basicP);
A_long num_effectsL;
A_char effect_nameAC[AEGP_MAX_ITEM_NAME_SIZE] = { '\0' };
AEGP_ItemH active_itemH = NULL;
AEGP_ItemType item_type = AEGP_ItemType_NONE;
AEGP_EffectRefH effectH;
IdleHookInfo_t *hookInfo = new (IdleHookInfo_t);
ERR(suites.ItemSuite8()->AEGP_GetActiveItem(&active_itemH));
if (active_itemH)
ERR(suites.ItemSuite8()->AEGP_GetItemType(active_itemH, &item_type));
if (!err) {
if (AEGP_ItemType_COMP == item_type) {
A_long layer_indexL = 0;
ERR(suites.CompSuite4()->AEGP_GetCompFromItem(active_itemH, &hookInfo->compH));
ERR(suites.LayerSuite5()->AEGP_GetCompLayerByIndex(hookInfo->compH, layer_indexL, &hookInfo->layerH));
ERR(suites.EffectSuite2()->AEGP_GetLayerNumEffects(hookInfo->layerH, &num_effectsL));
for (A_long jL = 0; !err && jL < num_effectsL; jL++) {
ERR(suites.EffectSuite2()->AEGP_GetLayerEffectByIndex(S_my_id, hookInfo->layerH, jL, &effectH));
if (!err && effectH) {
AEGP_InstalledEffectKey key;
ERR(suites.EffectSuite2()->AEGP_GetInstalledKeyFromLayerEffect(effectH, &key));
ERR(suites.EffectSuite2()->AEGP_GetEffectName(key, effect_nameAC));
if (!strcmp(effect_nameAC, "Convolution")) {
hookInfo->effect_indexL = jL;
AEGP_IdleRefcon idleRefcon = reinterpret_cast<AEGP_IdleRefcon>(&hookInfo);
return(suites.RegisterSuite5()->AEGP_RegisterIdleHook(S_my_id, IdleHook, idleRefcon));
}}}}}}
Here is my IdleHook
PF_Err IdleHook(AEGP_GlobalRefcon plugin_refconP, AEGP_IdleRefcon refconP, A_long* sleepL) {
IdleHookInfo_t *hookInfo = reinterpret_cast<IdleHookInfo_t *>(refconP);
AEGP_SuiteHandler suites(sP);
A_Err err = A_Err_NONE;
AEGP_StreamRefH stream_refH;
A_Time timeT = { 0,1 };
AEGP_EffectRefH effectH;
ERR(suites.EffectSuite4()->AEGP_GetLayerEffectByIndex(S_my_id, hookInfo->layerH, hookInfo->effect_indexL, &effectH));
ERR(suites.StreamSuite2()->AEGP_GetNewEffectStreamByIndex(S_my_id, effectH, HIDDEN, &stream_refH));
AEGP_StreamValue2 val = { stream_refH, uiData.clicks % 100ull };
ERR(suites.StreamSuite3()->AEGP_SetStreamValue(S_my_id, stream_refH, &val));
return err;
}