Version: Adobe After Effects version 23.6.5
System: Microsoft Windows 11 Pro.
Steps to reproduce the problem: this can be reproduced using the attached entry_point file or using the AE's builtin Channel Combiner effect. I will describe the steps to reproduce the problem using the Channel Combiner effect.
- Create a composition with an image
- Add the Channel Combiner effect to it
- Check the parameter Source Options > Use 2nd Layer parameter and add a keyframe at time 00s. Note that the parameter Source Options > Source Layer is now enabled (not grayed out).
- Go to another time (for example 02s) and uncheck the parameter Source Options > Use 2nd Layer. Note that the parameter Source Options > Source Layer is now disabled (grayed out).
- Using the timeline ruler, set the current time to 00s with a left click.
Expected result: Source Options > Use 2nd Layer is checked and Source Options > Source Layer is enabled.
Actual result: Source Options > Use 2nd Layer is checked but Source Options > Source Layer is disabled (grayed out).
Optional informations:
I also created a minimal effect plugin and I noticed while debugging that changing the current time using the timeline ruler does not trigger the entry_point function therefore no PF_Cmd_UPDATE_PARAMS_UI is sent.
I also tried an empty cache approach and still does not work.
#include <AEFX_SuiteHandlerTemplate.h>
#include <AE_EffectCBSuites.h>
#include <AEGP_SuiteHandler.h>
#include <cassert>
#include <stdexcept>
#include <string>
//
// Minimal example with two boolean parameters: Param 1 and Param 2.
// The effect simply copies input to output.
//
// How to reproduce the problem:
// - Add the effect to a composition.
// - Add a keyframe on effect's Param 1 (unchecked).
// - Change the current time to a different time and set Param 1 to checked. Param 2 is now disabled/grayed out.
// - Click on the timeline ruler on param1's first key (where param 1 is not checked).
// - Now param 1 is not checked but param 2 is still disabled/grayed out.
//
namespace
{
const int param_input_layer_id = 0;
const int param_param_1_id = 1;
const int param_param_2_id = 2;
bool is_empty(const PF_LRect* r)
{
return (r->left >= r->right) || (r->top >= r->bottom);
}
void union_rect(const PF_LRect* src, PF_LRect* dst)
{
if (is_empty(dst)) {
*dst = *src;
}
else if (!is_empty(src)) {
dst->left = std::min(dst->left, src->left);
dst->top = std::min(dst->top, src->top);
dst->right = std::max(dst->right, src->right);
dst->bottom = std::max(dst->bottom, src->bottom);
}
}
void clear_param_def(PF_ParamDef& def)
{
A_long _t = sizeof(def);
A_char* _p = (A_char*)&(def);
while (_t--) {
*_p++ = 0;
}
}
void throw_if_err(PF_Err err)
{
if (err != PF_Err_NONE) {
throw std::runtime_error(std::to_string(err));
}
}
bool get_checkbox_value(PF_InData* in_data, PF_ParamIndex index)
{
PF_ParamDef def;
clear_param_def(def);
throw_if_err(in_data->inter.checkout_param(
in_data->effect_ref,
index,
in_data->current_time,
in_data->time_step,
in_data->time_scale,
&def));
assert(def.param_type == PF_Param_CHECKBOX);
bool value = def.u.bd.value;
throw_if_err(in_data->inter.checkin_param(in_data->effect_ref, &def));
return value;
}
}
PF_Err entry_point(
PF_Cmd cmd,
PF_InData* in_data,
PF_OutData* out_data,
PF_ParamDef* params[],
PF_LayerDef* output,
void* extra)
{
PF_Err err = PF_Err_NONE;
try {
switch (cmd) {
case PF_Cmd_GLOBAL_SETUP:
{
const int major_version = 1;
const int minor_version = 1;
const char* name = "my effect";
out_data->my_version = (major_version * 524288 + minor_version * 32768 + 0 * 2048 + 0 * 512);
out_data->out_flags =
PF_OutFlag_DEEP_COLOR_AWARE |
PF_OutFlag_USE_OUTPUT_EXTENT |
PF_OutFlag_SEND_UPDATE_PARAMS_UI |
PF_OutFlag_PIX_INDEPENDENT;
out_data->out_flags2 =
PF_OutFlag2_SUPPORTS_SMART_RENDER |
PF_OutFlag2_FLOAT_COLOR_AWARE |
PF_OutFlag2_SUPPORTS_THREADED_RENDERING;
AEFX_SuiteScoper<AEGP_UtilitySuite6> utilitySuite(in_data, kAEGPUtilitySuite, kAEGPUtilitySuiteVersion6);
AEGP_PluginID aegp_plugin_id;
throw_if_err(utilitySuite->AEGP_RegisterWithAEGP(nullptr, name, &aegp_plugin_id));
break;
}
case PF_Cmd_GLOBAL_SETDOWN:
{
break;
}
case PF_Cmd_ABOUT:
{
break;
}
case PF_Cmd_PARAMS_SETUP:
{
{
PF_ParamDef def;
clear_param_def(def);
const char* param_name = "Param 1";
def.param_type = PF_Param_CHECKBOX;
def.u.bd.u.nameptr = param_name;
def.u.bd.value = false;
def.u.bd.dephault = (PF_Boolean)(def.u.bd.value);
in_data->utils->ansi.strcpy(def.name, param_name);
def.uu.id = param_param_1_id;
def.flags = PF_ParamFlag_SUPERVISE;
def.ui_flags = PF_ParamUIFlags();
throw_if_err(in_data->inter.add_param(in_data->effect_ref, -1, &def));
}
{
PF_ParamDef def;
clear_param_def(def);
const char* param_name = "Param 2";
def.param_type = PF_Param_CHECKBOX;
def.u.bd.u.nameptr = param_name;
def.u.bd.value = false;
def.u.bd.dephault = (PF_Boolean)(def.u.bd.value);
in_data->utils->ansi.strcpy(def.name, param_name);
def.uu.id = param_param_2_id;
def.flags = PF_ParamFlag_SUPERVISE;
def.ui_flags = PF_ParamUIFlags();
throw_if_err(in_data->inter.add_param(in_data->effect_ref, -1, &def));
}
// Layer + param 1 + param 2
out_data->num_params = 3;
break;
}
case PF_Cmd_SEQUENCE_SETUP:
{
break;
}
case PF_Cmd_SEQUENCE_SETDOWN:
{
break;
}
case PF_Cmd_SEQUENCE_RESETUP:// is sent when a project is loaded or when the layer to which it fs applied changes.
{
break;
}
case PF_Cmd_SEQUENCE_FLATTEN://PF_Cmd_SEQUENCE_FLATTEN is sent when the After Effects project is written out to disk.
{
break;
}
case PF_Cmd_GET_FLATTENED_SEQUENCE_DATA:
{
break;
}
case PF_Cmd_FRAME_SETUP:
{
break;
}
case PF_Cmd_FRAME_SETDOWN:
{
break;
}
case PF_Cmd_GPU_DEVICE_SETUP:
{
break;
}
case PF_Cmd_GPU_DEVICE_SETDOWN:
{
break;
}
case PF_Cmd_RENDER:
{
break;
}
case PF_Cmd_SMART_PRE_RENDER:
{
PF_PreRenderExtra* pre_render_extra = reinterpret_cast<PF_PreRenderExtra*>(extra);
PF_RenderRequest req = pre_render_extra->input->output_request;
PF_CheckoutResult in_result;
throw_if_err(pre_render_extra->cb->checkout_layer(
in_data->effect_ref,
param_input_layer_id,
param_input_layer_id,
&req,
in_data->current_time,
in_data->time_step,
in_data->time_scale,
&in_result));
union_rect(&in_result.result_rect, &pre_render_extra->output->result_rect);
union_rect(&in_result.max_result_rect, &pre_render_extra->output->max_result_rect);
break;
}
case PF_Cmd_SMART_RENDER:
{
PF_SmartRenderExtra* smart_render_extra = reinterpret_cast<PF_SmartRenderExtra*>(extra);
PF_EffectWorld* input = nullptr;
PF_EffectWorld* output = nullptr;
throw_if_err(smart_render_extra->cb->checkout_layer_pixels(in_data->effect_ref, param_input_layer_id, &input));
throw_if_err(smart_render_extra->cb->checkout_output(in_data->effect_ref, &output));
assert(input);
assert(output);
A_long linesS = output->extent_hint.bottom - output->extent_hint.top;
throw_if_err(in_data->utils->copy(in_data->effect_ref, input, output, nullptr, nullptr));
break;
}
case PF_Cmd_SMART_RENDER_GPU:/* used when rendering on the GPU */
{
break;
}
case PF_Cmd_USER_CHANGED_PARAM:/* change param value according to other parameter change */
{
break;
}
case PF_Cmd_UPDATE_PARAMS_UI:/* UI fields in paramdefs need to be refreshed according to new values */
{
// Param 1 value.
bool param_1 = get_checkbox_value(in_data, param_param_1_id);
// Disable param 2.
{
PF_ParamDef def = *params[param_param_2_id];
if (param_1) {
def.ui_flags |= PF_PUI_DISABLED;
}
else {
def.ui_flags &= ~PF_PUI_DISABLED;
}
AEFX_SuiteScoper<PF_ParamUtilsSuite3> param_util_suite(in_data, kPFParamUtilsSuite, kPFParamUtilsSuiteVersion3);
throw_if_err(param_util_suite->PF_UpdateParamUI(in_data->effect_ref, param_param_2_id, &def));
}
out_data->out_flags |= PF_OutFlag_REFRESH_UI;
break;
}
case PF_Cmd_DO_DIALOG:/* called after SEQUENCE_SETUP only if effect requests */
{
break;
}
case PF_Cmd_EVENT:/* handle some event. extra contains a ptr to a relevant structure */
{
break;
}
case PF_Cmd_GET_EXTERNAL_DEPENDENCIES: /* new in AE4.1: return text description of things like fonts, etc. in PF_ExtDependenciesExtra */
{
break;
}
case PF_Cmd_COMPLETELY_GENERAL:/* new in AE4.1: Used for completely general effect calls via AEGP, extra has the data sent through AEGP_EffectCallGeneric */
{
break;
}
case PF_Cmd_QUERY_DYNAMIC_FLAGS:
{
break;
}
case PF_Cmd_ARBITRARY_CALLBACK:/* used for arbitrary data, passes PF_ArbParamsExtra * in extra */
{
break;
}
case PF_Cmd_AUDIO_RENDER:/* For Audio Effects */
{
break;
}
case PF_Cmd_AUDIO_SETUP:
{
break;
}
case PF_Cmd_AUDIO_SETDOWN:
{
break;
}
}
}
catch (std::exception& ex) {
out_data->out_flags |= PF_OutFlag_DISPLAY_ERROR_MESSAGE;
in_data->utils->ansi.sprintf(out_data->return_msg, ex.what());
}
catch (...) {
out_data->out_flags |= PF_OutFlag_DISPLAY_ERROR_MESSAGE;
in_data->utils->ansi.sprintf(out_data->return_msg, "undefined exception occured");
}
return err;
}