afterpresets 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +9 -6
  2. package/native-runtime/CMakeLists.txt +82 -0
  3. package/native-runtime/README.md +40 -0
  4. package/native-runtime/THIRD_PARTY_NOTICES.md +14 -0
  5. package/native-runtime/include/ap_native.h +223 -0
  6. package/native-runtime/src/apply.cpp +199 -0
  7. package/native-runtime/src/crypto.cpp +225 -0
  8. package/native-runtime/src/crypto.hpp +51 -0
  9. package/native-runtime/src/dialog.cpp +62 -0
  10. package/native-runtime/src/dialog.hpp +35 -0
  11. package/native-runtime/src/engine.cpp +909 -0
  12. package/native-runtime/src/engine.hpp +232 -0
  13. package/native-runtime/src/fsutil.cpp +291 -0
  14. package/native-runtime/src/fsutil.hpp +57 -0
  15. package/native-runtime/src/json.cpp +326 -0
  16. package/native-runtime/src/json.hpp +70 -0
  17. package/native-runtime/src/platform.hpp +83 -0
  18. package/native-runtime/src/platform_common.cpp +60 -0
  19. package/native-runtime/src/platform_mac.mm +314 -0
  20. package/native-runtime/src/platform_win.cpp +277 -0
  21. package/native-runtime/src/runtime.cpp +473 -0
  22. package/native-runtime/src/util.cpp +169 -0
  23. package/native-runtime/src/util.hpp +32 -0
  24. package/native-runtime/src/zip.cpp +243 -0
  25. package/native-runtime/src/zip.hpp +58 -0
  26. package/native-runtime/third_party/monocypher/LICENCE.md +167 -0
  27. package/native-runtime/third_party/monocypher/monocypher-ed25519.c +500 -0
  28. package/native-runtime/third_party/monocypher/monocypher-ed25519.h +140 -0
  29. package/native-runtime/third_party/monocypher/monocypher.c +2956 -0
  30. package/native-runtime/third_party/monocypher/monocypher.h +321 -0
  31. package/native-runtime/third_party/tinf/LICENSE +22 -0
  32. package/native-runtime/third_party/tinf/crc32.c +57 -0
  33. package/native-runtime/third_party/tinf/tinf.h +140 -0
  34. package/native-runtime/third_party/tinf/tinflate.c +636 -0
  35. package/native-runtime/vendor-files.json +39 -0
  36. package/package.json +7 -4
  37. package/runtime/docs/cep/host-after-effects.md +3 -3
  38. package/runtime/docs/cep/host-premiere.md +4 -4
  39. package/runtime/docs/cep/host.md +10 -2
  40. package/runtime/docs/common/README.md +1 -1
  41. package/runtime/docs/native/README.md +44 -0
  42. package/runtime/docs/native/debugging.md +140 -0
  43. package/runtime/docs/native/rules.md +402 -0
  44. package/runtime/docs/native/testing.md +154 -0
  45. package/runtime/package.json +1 -1
  46. package/runtime/scripts/ae-sdk-docs/config.js +102 -0
  47. package/runtime/scripts/ae-sdk-docs/convert.js +430 -0
  48. package/runtime/scripts/ae-sdk-docs/index.js +314 -0
  49. package/runtime/scripts/ae-sdk-docs/source.js +205 -0
  50. package/runtime/scripts/ae-sdk-docs/split.js +170 -0
  51. package/runtime/scripts/ae-sdk-docs.js +72 -0
  52. package/runtime/src/host.js +1 -1
  53. package/src/agent/cli-group.js +9 -0
  54. package/src/agent/extendscript.js +10 -0
  55. package/src/agent/host-target.js +1 -0
  56. package/src/agent/panels.js +1 -0
  57. package/src/agent/png.js +1 -0
  58. package/src/agent/run-tool.js +3 -0
  59. package/src/{mcp → agent}/snapshot.js +2 -2
  60. package/src/agent/tool-error.js +1 -0
  61. package/src/agent/tools/capture.js +3 -0
  62. package/src/agent/tools/host.js +1 -0
  63. package/src/agent/tools/native.js +1 -0
  64. package/src/agent/tools/panel.js +7 -0
  65. package/src/agent/tools.js +2 -0
  66. package/src/api/error-messages.js +1 -1
  67. package/src/build/build.js +9 -8
  68. package/src/check/check.js +3 -3
  69. package/src/check/native.js +14 -0
  70. package/src/check/pricing.js +1 -1
  71. package/src/cli.js +3 -3
  72. package/src/commands/build.js +8 -2
  73. package/src/commands/check.js +10 -2
  74. package/src/commands/comp.js +6 -0
  75. package/src/commands/dev.js +29 -2
  76. package/src/commands/docs.js +5 -1
  77. package/src/commands/host.js +1 -0
  78. package/src/commands/init.js +12 -7
  79. package/src/commands/panel.js +1 -0
  80. package/src/commands/push.js +9 -2
  81. package/src/commands/releases.js +8 -3
  82. package/src/commands/sdk.js +14 -0
  83. package/src/commands/testers.js +1 -1
  84. package/src/commands/upgrade.js +2 -2
  85. package/src/commands/whoami.js +1 -1
  86. package/src/dev/bridge.js +8 -0
  87. package/src/dev/native.js +3 -0
  88. package/src/dev/session.js +5 -4
  89. package/src/native/build.js +27 -0
  90. package/src/native/cmake/CMakeLists.txt +88 -0
  91. package/src/native/cmake/zig-windows.cmake +19 -0
  92. package/src/native/effect.js +3 -0
  93. package/src/native/install.js +8 -0
  94. package/src/native/installers.js +4 -0
  95. package/src/native/pipl.js +20 -0
  96. package/src/native/restart.js +2 -0
  97. package/src/native/sdk.js +8 -0
  98. package/src/project/docs.js +3 -3
  99. package/src/project/project.js +2 -2
  100. package/src/project/template.js +5 -4
  101. package/src/prompt.js +1 -1
  102. package/src/release/confirm.js +5 -0
  103. package/src/release/native.js +3 -0
  104. package/src/release/publish.js +1 -1
  105. package/src/system/run-command.js +1 -1
  106. package/src/targets.js +1 -1
  107. package/template/cep/AGENTS.md +102 -164
  108. package/template/cep/README.md +7 -3
  109. package/template/cep/docs/code_guide.md +67 -0
  110. package/template/cep/docs/initial_setup.md +63 -42
  111. package/template/cep/docs/release_checklist.md +25 -0
  112. package/template/cep/gitignore +2 -1
  113. package/template/cep/src/features/update-screen/update-screen.js +1 -1
  114. package/template/hosts/AGENTS.two-apps.md +12 -0
  115. package/template/native/AGENTS.md +106 -0
  116. package/template/native/README.md +71 -0
  117. package/template/native/docs/code_guide.md +72 -0
  118. package/template/native/docs/initial_setup.md +154 -0
  119. package/template/native/docs/release_checklist.md +25 -0
  120. package/template/native/effect.json +16 -0
  121. package/template/native/gitignore +9 -0
  122. package/template/native/package.json +14 -0
  123. package/template/native/src/Effect.cpp +224 -0
  124. package/template/native/src/License.cpp +95 -0
  125. package/template/native/src/License.h +29 -0
  126. package/template/native/src/Render.h +68 -0
  127. package/src/commands/mcp.js +0 -7
  128. package/src/mcp/extendscript.js +0 -9
  129. package/src/mcp/panels.js +0 -1
  130. package/src/mcp/protocol.js +0 -4
  131. package/src/mcp/server.js +0 -2
  132. package/src/mcp/tools/account.js +0 -3
  133. package/src/mcp/tools/host.js +0 -1
  134. package/src/mcp/tools/panel.js +0 -7
  135. package/src/mcp/tools/project.js +0 -40
  136. package/src/mcp/tools/releases.js +0 -29
  137. package/template/cep/.mcp.json +0 -8
  138. /package/src/{mcp → agent}/capture.js +0 -0
  139. /package/src/{mcp → agent}/redact.js +0 -0
@@ -23,8 +23,8 @@ try {
23
23
  }
24
24
  ```
25
25
 
26
- The MCP `host_eval` tool already wraps each call in an undo group named
27
- "AfterPresets MCP".
26
+ `ap host eval` already wraps each call in an undo group named
27
+ "AfterPresets agent".
28
28
 
29
29
  ## The active composition
30
30
 
@@ -54,4 +54,4 @@ APIs and return `{ ok: false, message }` when they are missing.
54
54
 
55
55
  `ap check --types` checks `jsx/` against the After Effects 26.0 declarations
56
56
  from types-for-adobe and lists likely mistakes. Hints can be wrong; confirm
57
- with the MCP `host_eval` tool before changing working code.
57
+ with `ap host eval` before changing working code.
@@ -14,8 +14,8 @@ makes sense in Premiere Pro in `jsx/ppro/`.
14
14
  Premiere Pro's ExtendScript has no `beginUndoGroup`. Every change a script
15
15
  makes (a clip moved, a marker added, a property set) is its own step in
16
16
  Edit → Undo. Keep scripted edits small, do the checks before the first change,
17
- and tell the user how many undo steps an action adds. The MCP `host_eval`
18
- tool cannot group them either.
17
+ and tell the user how many undo steps an action adds. `ap host eval`
18
+ cannot group them either.
19
19
 
20
20
  ## The active sequence
21
21
 
@@ -68,7 +68,7 @@ Frame-accurate maths: convert with the sequence's timebase
68
68
 
69
69
  - `app.project` is the active project. Premiere Pro can have several projects
70
70
  open (`app.projects`, `numProjects`).
71
- - Save with `app.project.save()`. The MCP `host_scratch_project` tool creates a
71
+ - Save with `app.project.save()`. `ap host scratch` creates a
72
72
  new project in the temp folder and leaves the user's project open.
73
73
 
74
74
  ## Versions
@@ -81,4 +81,4 @@ return `{ ok: false, message }` when they are missing.
81
81
 
82
82
  `ap check --types` checks `jsx/` against the Premiere Pro 24.0 declarations
83
83
  from types-for-adobe (the newest set; newer APIs may be missing) and lists
84
- likely mistakes. Hints can be wrong; confirm with the MCP `host_eval` tool.
84
+ likely mistakes. Hints can be wrong; confirm with `ap host eval`.
@@ -33,6 +33,14 @@ An extension for both apps can define the same function in `jsx/aeft/` and
33
33
  code; each app loads its own version. `ap check` warns about a folder for an
34
34
  app the project does not target.
35
35
 
36
+ `AP.host.app` (also `ctx.host.app`) is the app running the panel: `"AEFT"`
37
+ (After Effects) or `"PPRO"` (Premiere Pro). Use it for wording that differs
38
+ between the apps, or to hide a feature that only one app has:
39
+
40
+ ```js
41
+ var noun = ctx.host.app === "PPRO" ? "sequence" : "composition";
42
+ ```
43
+
36
44
  - There is no list to keep: the CLI joins every `.jsx` file in alphabetical
37
45
  path order, with JSON support (json2) first, embeds the result in every
38
46
  panel's `main.js` (readable in `ap dev`, compiled to JSXBIN for releases),
@@ -123,6 +131,6 @@ restarts, so never rely on a function being absent.
123
131
  ## Testing
124
132
 
125
133
  Call the function from the live panel and check the result and the project
126
- state. Use a disposable project (the MCP `host_scratch_project` tool) for
127
- anything that changes the project. With the MCP tools, `host_eval` runs in the
134
+ state. Use a disposable project (`ap host scratch`) for
135
+ anything that changes the project. `ap host eval` runs in the
128
136
  app that runs the connected panel.
@@ -17,7 +17,7 @@ screen, style, and word the user sees.
17
17
  It gives you:
18
18
 
19
19
  - `AP.boot` to start the panel.
20
- - Licensing, off by default. Paid extensions must turn it on.
20
+ - Licensing, off in `AP.boot` by default (the starter project turns it on). Paid extensions must keep it on.
21
21
  - Signed updates with changelogs.
22
22
  - `AP.host.eval` to call ExtendScript functions in the app (After Effects or Premiere Pro).
23
23
  - `AP.panels` and `AP.events` for extensions with more than one panel.
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: AfterPresets native effect reference
3
+ description: Which file to read before writing or changing a C++ After Effects effect plugin built with AfterPresets.
4
+ ---
5
+
6
+ # AfterPresets native effect reference
7
+
8
+ These files are for the AI coding agent that builds an After Effects effect
9
+ plugin (C++, Adobe's effect SDK) with AfterPresets. An effect is a new entry
10
+ in the Effect menu that renders pixels, like a glow or a blur.
11
+
12
+ ## The rule
13
+
14
+ **Read [rules.md](rules.md) before you write or change any effect code, and
15
+ follow [testing.md](testing.md) before you say a change is done.** Then read
16
+ the SDK topic files for your task (index: [sdk/README.md](sdk/README.md)). Do
17
+ not guess struct fields, flag names, selectors or call order. If a file and
18
+ your memory disagree, the file wins. If the SDK guide and the SDK headers in
19
+ the project disagree, the headers win.
20
+
21
+ ## Files
22
+
23
+ | File | Read it before you |
24
+ |---|---|
25
+ | [rules.md](rules.md) | write or change any effect code: the hard rules, each with why and an example |
26
+ | [testing.md](testing.md) | say "done": build, apply the effect in After Effects, render frames and look at them |
27
+ | [debugging.md](debugging.md) | chase a crash, an effect that does not load, wrong pixels, or a slow render |
28
+ | [sdk/README.md](sdk/README.md) | use any part of the After Effects SDK: tasks mapped to small topic files |
29
+
30
+ ## About sdk/
31
+
32
+ `sdk/` is Adobe's After Effects C++ SDK guide, cut into small topic files. It
33
+ is generated on this computer from a pinned version of the community copy at
34
+ https://github.com/docsforadobe/after-effects-plugin-guide. AfterPresets does
35
+ not ship it: the guide is copyright Adobe. Never copy its text into the
36
+ product, its listing or its source files. Search it with:
37
+
38
+ ```sh
39
+ grep -rn "PF_OutFlag2_SUPPORTS_THREADED_RENDERING" docs/afterpresets/sdk/
40
+ ```
41
+
42
+ Adobe's SDK itself (headers, samples, `Util/` helpers) is not redistributed
43
+ either. The project gets it from Adobe; never commit it to a public
44
+ repository.
@@ -0,0 +1,140 @@
1
+ ---
2
+ title: Debugging a native effect
3
+ description: Find out why an effect does not load, crashes, renders wrong pixels or is slow, without an IDE - scripting checks, binary checks, crash reports, logs and debugger limits.
4
+ ---
5
+
6
+ # Debugging a native effect
7
+
8
+ You have no IDE and usually no debugger attached. Work from evidence you can
9
+ collect yourself: scripting queries through `ap host eval`, frames from
10
+ `ap comp capture`, the built binary, crash reports and logs. Change one thing at
11
+ a time and test again ([testing.md](testing.md)).
12
+
13
+ ## The effect does not appear or does not apply
14
+
15
+ 1. **Is it loaded?** Ask After Effects for its effect list:
16
+
17
+ ```js
18
+ var out = [];
19
+ for (var i = 0; i < app.effects.length; i++) {
20
+ var e = app.effects[i];
21
+ if (/ACME/.test(e.matchName)) out.push(e.matchName + " | " + e.displayName + " | " + e.category);
22
+ }
23
+ return out.join("\n") || "not loaded";
24
+ ```
25
+
26
+ Listed, but `addProperty` fails: you are using a different match name.
27
+ Not listed: After Effects did not load the binary; go on.
28
+ 2. **Was After Effects restarted** after the binary was installed? Plugins
29
+ load at launch only.
30
+ 3. **Is it where After Effects looks?** See
31
+ [sdk/where-installers-should-put-plug-ins.md](sdk/where-installers-should-put-plug-ins.md).
32
+ Folders whose names end in parentheses, or start with `¬` (macOS) or `~`
33
+ (Windows), are not scanned. `ap dev` installs into
34
+ `/Library/Application Support/Adobe/Common/Plug-ins/7.0/MediaCore/AfterPresets Dev/<bundleId>/`,
35
+ a folder made once with administrator rights and then owned by the user;
36
+ buyers' copies go to `MediaCore/AfterPresets/<bundleId>/`. Any other
37
+ write to the shared MediaCore folder or the After Effects `Plug-ins`
38
+ folder needs administrator rights: ask the user, never use `sudo`
39
+ yourself.
40
+ 4. **Check the binary** (macOS):
41
+
42
+ ```sh
43
+ lipo -info Effect.plugin/Contents/MacOS/Effect # arm64 and x86_64 for a universal build
44
+ nm -gU Effect.plugin/Contents/MacOS/Effect # the entry point must be exported
45
+ codesign -dv Effect.plugin # must be signed (ad-hoc is enough to develop)
46
+ xattr -l Effect.plugin # com.apple.quarantine set by downloads
47
+ ```
48
+
49
+ macOS 15 and later refuse unsigned plugins; sign development builds
50
+ ad-hoc with `codesign --force --deep --sign - Effect.plugin`, and do not
51
+ touch the bundle after signing. The PiPL needs an entry point for every
52
+ architecture you build (`CodeMacARM64`, `CodeMacIntel64`, `CodeWin64X86`).
53
+ 5. **Check the PiPL against GlobalSetup**: flags (rule 2 in
54
+ [rules.md](rules.md)), match name, version. A mismatch warning appears when
55
+ the effect is applied; ask the user to read you any dialog.
56
+ 6. **Read After Effects' plugin loading log.** It is rewritten at every
57
+ launch: `~/Library/Preferences/Adobe/After Effects/<version>/Plugin Loading.log`
58
+ (for example `26.2`). Search it for your bundle name:
59
+
60
+ ```sh
61
+ grep -n -i "MyEffect" ~/Library/Preferences/Adobe/After\ Effects/*/Plugin\ Loading.log
62
+ ```
63
+
64
+ The line "No loaders recognized this plugin … Ignore" is normal for After
65
+ Effects effects (it comes from the shared MediaCore loader). A missing
66
+ line means After Effects never saw the file (wrong folder, not
67
+ restarted); a load error points at the binary (step 4) or the PiPL
68
+ (step 5).
69
+
70
+ Whether a plugin loads also depends on how After Effects itself is signed
71
+ (hardened runtime, library validation). A result on one computer, for
72
+ example one with a re-signed or developer copy of After Effects, may not
73
+ match a customer's computer.
74
+
75
+ ## After Effects crashes
76
+
77
+ - macOS writes a crash report to `~/Library/Logs/DiagnosticReports/`
78
+ (`Adobe After Effects*.ips`). Find the crashed thread and look for frames in
79
+ your binary. Build development versions with debug symbols (`-g`) and keep
80
+ the `.dSYM`, so addresses map to functions and lines (`atos -o
81
+ Effect.plugin.dSYM/Contents/Resources/DWARF/Effect -arch arm64 -l <load
82
+ address> <address>`).
83
+ - Usual causes: reading past a row (`rowbytes`, wrong pixel type for the
84
+ depth), an exception leaving the entry point (rule 15), a world used after
85
+ `PF_DisposeWorld`, writing shared state from several render threads
86
+ (rule 11), unbalanced parameter checkouts (rule 10).
87
+ - After a crash, After Effects may show a recovery dialog at the next launch.
88
+ Ask the user to answer it; do not script around it.
89
+
90
+ ## Wrong pixels
91
+
92
+ - Capture the same frame at 8, 16 and 32 bpc. Wrong in one depth only: that
93
+ depth's code path or its maximum channel value (32768 for 16 bpc).
94
+ - Bright random colours, rainbows, black blotches: integer overflow; clamp
95
+ before converting.
96
+ - Wrong only at half resolution or with an effect before yours: downsample
97
+ factors or buffer origin ([rules.md](rules.md), rule 9).
98
+ - To see an internal value, temporarily write it into the output (for example
99
+ a red channel proportional to the value) and capture the frame. Remove it
100
+ afterwards.
101
+ - Unchanged after a rebuild: After Effects still runs the old binary
102
+ (restart), or you looked at a cached frame (`ap comp capture` clears caches by
103
+ default; keep it that way).
104
+
105
+ ## Slow renders
106
+
107
+ `ap comp capture` reports a render time per frame, measured after clearing the
108
+ caches. Compare before and after a change on the same frames. Typical fixes:
109
+ precompute tables once (GlobalSetup or SequenceSetup), avoid `pow`/`sqrt`/
110
+ division per pixel, fuse passes, process only the requested area, and enable
111
+ multi-frame rendering when the effect is thread-safe (rule 11).
112
+
113
+ ## Logs
114
+
115
+ The AfterPresets native runtime (`ap_native`) writes a small log for each
116
+ plugin: `~/Library/Logs/AfterPresets/<bundleId>.log` on macOS (rotated at
117
+ 1 MB; `ap_log_path()` returns the exact path). Each After Effects start adds
118
+ a `start <bundleId> <version> build=...` line, then licence and update
119
+ events. Read it with `tail -n 50`. It is safe to read: tokens and URL query
120
+ strings are scrubbed.
121
+
122
+ To log from effect code, call `ap_log(rt, level, "message")`: it never
123
+ blocks and does no file I/O on the calling thread, so it is allowed in
124
+ render code, but never log from inside the per-pixel loop. Never log licence
125
+ tokens, account data or user paths, and remove temporary logging before
126
+ release.
127
+
128
+ ## Debuggers
129
+
130
+ Attaching `lldb` to a release build of After Effects 26.5 or later on macOS is
131
+ refused because of how the application is signed. Adobe's workaround (a
132
+ re-signed copy, or developer mode for beta builds) needs the user and
133
+ administrator rights: see [sdk/debugging-ae-macos.md](sdk/debugging-ae-macos.md).
134
+ Prefer the evidence above; propose a debugger session to the user only when
135
+ it is the only way forward.
136
+
137
+ Holding Cmd+Opt+Shift (macOS) or Ctrl+Alt+Shift (Windows) while After Effects
138
+ starts resets its preferences, which clears settings a plugin may have
139
+ stored. Only the user should do this, after being told it resets all their
140
+ preferences.
@@ -0,0 +1,402 @@
1
+ ---
2
+ title: Rules for native effects
3
+ description: The hard rules and classic mistakes of After Effects effect plugins (PiPL, flags, match names, parameters, bit depths, Smart Render, multi-frame rendering, sequence data, memory, errors), each with why and an example.
4
+ ---
5
+
6
+ # Rules for native effects
7
+
8
+ Read this before writing or changing effect code. Each rule says why it
9
+ exists and shows the right shape. Details are in the SDK topic files
10
+ ([sdk/README.md](sdk/README.md)); `grep` them for any name used here.
11
+
12
+ ## Identity and registration
13
+
14
+ ### 1. The match name is permanent, unique and prefixed
15
+
16
+ The match name (`AE_Effect_Match_Name` in the PiPL, and the `matchName` given
17
+ to `PF_REGISTER_EFFECT*` if the project registers in code) is the effect's
18
+ identity. After Effects stores it in every project, preset and template that
19
+ uses the effect. Give it a vendor prefix, keep it ASCII, and never change it
20
+ after the first release. The display name and category may change; the match
21
+ name may not.
22
+
23
+ Why: rename it and every saved project shows "missing effect". Two vendors
24
+ with the same match name collide, and one of the effects does not load.
25
+
26
+ ```c
27
+ AE_Effect_Match_Name { "ACME Soft Glow" }, // never changes
28
+ Name { "Soft Glow" }, // display name, up to 47 characters
29
+ Category { "ACME" }, // spelled the same in every effect
30
+ ```
31
+
32
+ A category spelled two ways ("ACME" and "ACME ") makes two folders in the
33
+ Effect menu.
34
+
35
+ ### 2. The PiPL flags equal the GlobalSetup flags
36
+
37
+ `AE_Effect_Global_OutFlags` and `AE_Effect_Global_OutFlags_2` in the PiPL must
38
+ be exactly the values `PF_Cmd_GLOBAL_SETUP` puts in `out_data->out_flags` and
39
+ `out_data->out_flags2`. Every time you add or remove a flag (Smart Render,
40
+ float, multi-frame rendering, `I_DO_DIALOG`, GPU...), change both.
41
+
42
+ Why: After Effects reads the PiPL without running the plugin and reads the
43
+ flags again at GlobalSetup. When they differ it warns the user when the effect
44
+ is applied, and features silently follow the wrong set. This is the most
45
+ common native-plugin mistake.
46
+
47
+ ```cpp
48
+ // GlobalSetup
49
+ out_data->out_flags = PF_OutFlag_DEEP_COLOR_AWARE | PF_OutFlag_PIX_INDEPENDENT;
50
+ out_data->out_flags2 = PF_OutFlag2_SUPPORTS_SMART_RENDER
51
+ | PF_OutFlag2_FLOAT_COLOR_AWARE
52
+ | PF_OutFlag2_SUPPORTS_THREADED_RENDERING;
53
+ ```
54
+
55
+ The PiPL holds the same two numbers in hex. Keep both in one place: in an
56
+ AfterPresets project the flags live only in `effect.json`, and the build
57
+ generates both the PiPL and `ap_effect_config.h`, whose macros GlobalSetup
58
+ uses; `ap check` compares the built PiPL with `effect.json`. Never type flag
59
+ values by hand, and never edit a generated file.
60
+
61
+ ### 3. Versions agree and only go up
62
+
63
+ `out_data->my_version` (set with `PF_VERSION(...)` in GlobalSetup) and
64
+ `AE_Effect_Version` in the PiPL describe the same version: generate both from
65
+ one header. Raise it for every release. `AE_Effect_Spec_Version` is the SDK
66
+ version the effect was built with: use the SDK's `PF_AE_PLUG_IN_VERSION` /
67
+ `PF_AE_PLUG_IN_SUBVERS` constants, never hand-typed numbers.
68
+
69
+ Why: users and support read the version in the Effects panel and in "About".
70
+ Multi-frame rendering needs an effect built against SDK 13.25 or later
71
+ (March 2021 SDK or newer).
72
+
73
+ ```cpp
74
+ out_data->my_version = PF_VERSION(MAJOR_VERSION, MINOR_VERSION, BUG_VERSION,
75
+ STAGE_VERSION, BUILD_VERSION);
76
+ ```
77
+
78
+ ### 4. One effect per binary, one entry point
79
+
80
+ Ship one PiPL and one effect per `.plugin` / `.aex`. Export only the entry
81
+ point (`EffectMain`, plus the SDK's `PluginDataEntryFunction*` when the effect
82
+ also registers in code with `PF_REGISTER_EFFECT*`); hide every other symbol.
83
+
84
+ Why: Premiere Pro and other hosts do not support several effects in one
85
+ binary, and an update then has to replace all of them. Exported symbols from
86
+ two plugins that share a library clash at load time (see
87
+ [sdk/symbol-export.md](sdk/symbol-export.md)).
88
+
89
+ ## Parameters
90
+
91
+ ### 5. Parameter disk IDs never change, never get reused
92
+
93
+ Each parameter has two numbers: its index (its position in the Effect
94
+ Controls, the order you call `PF_ADD_*`) and its disk ID (`def.uu.id`, saved
95
+ in projects). Keep two enums. Disk IDs start at 1. Add new IDs only at the
96
+ end. Never delete, reorder or reuse an ID, even when you remove the
97
+ parameter.
98
+
99
+ Why: projects store values by disk ID. Reorder the IDs and a saved project
100
+ loads "Radius" into "Intensity". Without IDs, After Effects numbers
101
+ parameters by position, so inserting one shifts every later value.
102
+
103
+ ```cpp
104
+ enum { GLOW_INPUT = 0, GLOW_RADIUS, GLOW_INTENSITY, GLOW_COLOR, GLOW_NUM_PARAMS };
105
+ enum { RADIUS_DISK_ID = 1, INTENSITY_DISK_ID, COLOR_DISK_ID /* add new IDs here only */ };
106
+ ```
107
+
108
+ Changing a parameter's default also needs care: give it a new disk ID, or set
109
+ `PF_ParamFlag_USE_VALUE_FOR_OLD_PROJECTS`, so old projects keep the value they
110
+ were made with. See [sdk/changing-parameter-orders.md](sdk/changing-parameter-orders.md).
111
+
112
+ ### 6. Clear every definition, count every parameter
113
+
114
+ Call `AEFX_CLR_STRUCT(def)` before every `PF_ADD_*`, and set
115
+ `out_data->num_params` to the number of parameters including the input layer
116
+ (index 0).
117
+
118
+ Why: a definition left over from the previous parameter carries flags and
119
+ ranges into the next one. A wrong `num_params` makes the effect fail to apply
120
+ or read garbage.
121
+
122
+ ```cpp
123
+ PF_ParamDef def;
124
+ AEFX_CLR_STRUCT(def);
125
+ PF_ADD_FLOAT_SLIDERX("Radius", 0, 1000, 0, 200, 25, PF_Precision_TENTHS, 0, 0, RADIUS_DISK_ID);
126
+ AEFX_CLR_STRUCT(def);
127
+ PF_ADD_FLOAT_SLIDERX("Intensity", 0, 400, 0, 200, 100, PF_Precision_TENTHS, 0, 0, INTENSITY_DISK_ID);
128
+ out_data->num_params = GLOW_NUM_PARAMS;
129
+ ```
130
+
131
+ ### 7. Read each parameter through its own union member
132
+
133
+ | Parameter | Read |
134
+ |---|---|
135
+ | Float slider (`PF_ADD_FLOAT_SLIDERX`) | `u.fs_d.value` (double) |
136
+ | Slider (int) | `u.sd.value` |
137
+ | Fixed slider, percent | `u.fd.value` (`PF_Fixed`, 16.16: use `FIX2FLT`) |
138
+ | Angle | `u.ad.value` (`PF_Fixed` degrees) |
139
+ | Checkbox | `u.bd.value` |
140
+ | Popup | `u.pd.value` (1-based) |
141
+ | Color | `u.cd.value` (`PF_Pixel`, 8 bpc; use `PF_ColorParamSuite1` for float) |
142
+ | Point | `u.td.x_value`, `u.td.y_value` (`PF_Fixed` pixels; use `PF_PointParamSuite1` for float) |
143
+ | Layer | `u.ld` (checked out, see rule 10) |
144
+
145
+ Why: the union does not complain. Reading a percent parameter through `sd`
146
+ gives silently wrong numbers. Check the type in
147
+ [sdk/parameters.md](sdk/parameters.md) before reading.
148
+
149
+ `PF_ADD_POINT` defaults are a percentage of the layer (50, 50 is the
150
+ centre), not pixels; the value you read back is in pixels of the layer. The
151
+ values you get are already scaled for downsampling and origin offsets; see
152
+ [sdk/parameters.md](sdk/parameters.md#point-parameter-origin).
153
+
154
+ ## Pixels and bit depths
155
+
156
+ ### 8. Handle every bit depth you declare, and declare what you handle
157
+
158
+ - 8 bpc: `PF_Pixel`, channels 0-255. Always supported.
159
+ - 16 bpc: `PF_Pixel16`, channels **0-32768** (`PF_MAX_CHAN16`), not 65535 and
160
+ not 32767. Declare with `PF_OutFlag_DEEP_COLOR_AWARE`.
161
+ - 32 bpc: `PF_PixelFloat`, nominally 0.0-1.0, but values below 0 and above 1
162
+ are valid (overbright) and must not be clamped. Declare with
163
+ `PF_OutFlag2_FLOAT_COLOR_AWARE`, which requires Smart Render
164
+ (`PF_OutFlag2_SUPPORTS_SMART_RENDER`).
165
+
166
+ Why: without the flags, After Effects converts to a lower depth and shows a
167
+ warning badge on the effect. With the flags but a missing code path, 16/32 bpc
168
+ projects render garbage. Using 32767 or 65535 for white makes 16-bit output
169
+ slightly too dark or wrap around.
170
+
171
+ Write one kernel and instantiate it per depth; clamp before converting to an
172
+ integer type:
173
+
174
+ ```cpp
175
+ template <typename P, int MaxChan> struct Px; // P = PF_Pixel8, PF_Pixel16 or PF_PixelFloat
176
+ // 8: MaxChan 255 16: MaxChan PF_MAX_CHAN16 (32768) 32: float, no clamp
177
+ static inline A_u_short to16(float v) {
178
+ v = v < 0.f ? 0.f : (v > 1.f ? 1.f : v);
179
+ return (A_u_short)(v * PF_MAX_CHAN16 + 0.5f);
180
+ }
181
+ ```
182
+
183
+ In Smart Render, pick the path from the world's pixel format
184
+ (`PF_WorldSuite2::PF_GetPixelFormat`: `ARGB32`, `ARGB64`, `ARGB128`) and use
185
+ the matching iterate suite (`Iterate8Suite`, `Iterate16Suite`,
186
+ `IterateFloatSuite`). Pixel order is ARGB in After Effects.
187
+
188
+ ### 9. Never assume the size, origin or stride of a buffer
189
+
190
+ - Use `rowbytes` to step between rows; it is not `width * sizeof(pixel)`.
191
+ - Scale distances that come from the user in pixels by
192
+ `in_data->downsample_x` / `downsample_y` (a half-resolution preview has half
193
+ the pixels).
194
+ - Process only the area you are asked for (`extent_hint`, or the Smart
195
+ Render request rects) and use the buffer's own width, height and origin.
196
+ - Pixel aspect ratio may not be 1 ([sdk/pixel-aspect-ratio.md](sdk/pixel-aspect-ratio.md)).
197
+
198
+ Why: hard-coded coordinates or `width * 4` work at full resolution in one test
199
+ comp and break at half resolution, on cropped layers and with other effects
200
+ upstream.
201
+
202
+ ```cpp
203
+ float radius_px = (float)(radius * in_data->downsample_x.num / in_data->downsample_x.den);
204
+ PF_Pixel8 *row = (PF_Pixel8 *)((char *)world->data + y * world->rowbytes);
205
+ ```
206
+
207
+ To draw outside the layer (glows, shadows), expand the output properly: in
208
+ Smart Render return a larger `result_rect` / `max_result_rect` from PreRender;
209
+ without Smart Render set `PF_OutFlag_I_EXPAND_BUFFER` and give the new output
210
+ width, height and origin in `PF_Cmd_FRAME_SETUP`. Point parameters then
211
+ arrive shifted by the origin; see
212
+ [sdk/pf-indata.md](sdk/pf-indata.md#point-controls-and-buffer-expansion).
213
+
214
+ ## Smart Render
215
+
216
+ ### 10. Smart Render is all or nothing
217
+
218
+ With `PF_OutFlag2_SUPPORTS_SMART_RENDER`, handle both `PF_Cmd_SMART_PRE_RENDER`
219
+ and `PF_Cmd_SMART_RENDER`:
220
+
221
+ - PreRender: check out the input layer with a render request
222
+ (`checkout_layer`), and return the union of the result rects.
223
+ - SmartRender: use the checked-out input world
224
+ (`checkout_layer_pixels`) and output (`checkout_output`), never
225
+ `params[0]->u.ld`. Check out every parameter you read with
226
+ `PF_CHECKOUT_PARAM` and check every one back in with `PF_CHECKIN_PARAM`,
227
+ also on error paths.
228
+ - Keep `PF_Cmd_RENDER` working too: Premiere Pro never sends the Smart Render
229
+ selectors.
230
+
231
+ Why: setting the flag without both selectors gives errors when the effect is
232
+ applied. Reading `params[0]` in SmartRender, or unbalanced checkouts, gives
233
+ "bad parameter passed to effect callback" errors.
234
+
235
+ ```cpp
236
+ PF_ParamDef radius;
237
+ AEFX_CLR_STRUCT(radius);
238
+ ERR(PF_CHECKOUT_PARAM(in_data, GLOW_RADIUS, in_data->current_time,
239
+ in_data->time_step, in_data->time_scale, &radius));
240
+ /* ... render ... */
241
+ ERR2(PF_CHECKIN_PARAM(in_data, &radius)); // always, even if err is set
242
+ ```
243
+
244
+ ## Multi-frame rendering and threads
245
+
246
+ ### 11. Only claim thread safety you have
247
+
248
+ Set `PF_OutFlag2_SUPPORTS_THREADED_RENDERING` (After Effects 2022+) only when
249
+ all of these hold:
250
+
251
+ - No global or static variable is written after GlobalSetup (constant tables
252
+ built once in GlobalSetup are fine).
253
+ - `in_data->global_data` is written only in `PF_Cmd_GLOBAL_SETUP` and
254
+ `PF_Cmd_GLOBAL_SETDOWN`.
255
+ - `sequence_data` is never written at render time or in
256
+ `PF_Cmd_UPDATE_PARAMS_UI`; it is read at render time through
257
+ `PF_EffectSequenceDataSuite1` (`PF_GetConstSequenceData`).
258
+ - No lock (mutex, gate) is held while calling back into After Effects (any
259
+ suite call, any checkout).
260
+ - Rendering is deterministic: no `rand()`, no time-of-day, no state carried
261
+ from the previous frame. Seed noise from parameters, time and position.
262
+
263
+ `PF_Cmd_SEQUENCE_SETUP`, `SEQUENCE_RESETUP`, `SEQUENCE_SETDOWN`,
264
+ `SMART_PRE_RENDER`, `RENDER` and `SMART_RENDER` can run on several threads at
265
+ once, at the same time as UI selectors.
266
+
267
+ Why: without the flag, After Effects renders the effect one frame at a time
268
+ and shows a "not optimised for multi-frame rendering" warning. With the flag
269
+ but shared state, frames render differently, flicker, or crash, only on busy
270
+ machines. Non-deterministic renders also break the frame cache.
271
+
272
+ ```cpp
273
+ // wrong: one instance's value leaks into another instance's render
274
+ static float g_radius;
275
+ // right: read parameters in the render call (or pass them via pre_render_data)
276
+ ```
277
+
278
+ If an effect must cache expensive work across frames, use the Compute Cache
279
+ ([sdk/compute-cache-api.md](sdk/compute-cache-api.md)), not sequence data.
280
+
281
+ ### 12. Never block a render or UI thread
282
+
283
+ No network, file I/O, dialogs, sleeps or long waits inside render selectors
284
+ or `PF_Cmd_UPDATE_PARAMS_UI`. Load files and tables once (GlobalSetup or
285
+ SequenceSetup). Licence and update work runs in the AfterPresets native
286
+ runtime off the render thread; render code only reads its cached state
287
+ with `ap_is_unlocked(rt)` (lock-free, safe on any thread, never waits).
288
+
289
+ Why: a render that waits on the network freezes previews for every user who
290
+ is offline or slow, and can deadlock with multi-frame rendering.
291
+
292
+ ## Sequence data
293
+
294
+ ### 13. Flatten sequence data, and say so
295
+
296
+ Sequence data is saved in the project. If it contains pointers or handles,
297
+ flatten it: set `PF_OutFlag_SEQUENCE_DATA_NEEDS_FLATTENING` and
298
+ `PF_OutFlag2_SUPPORTS_GET_FLATTENED_SEQUENCE_DATA`, handle
299
+ `PF_Cmd_SEQUENCE_FLATTEN`, `PF_Cmd_SEQUENCE_RESETUP` and
300
+ `PF_Cmd_GET_FLATTENED_SEQUENCE_DATA`. Put a version number and a
301
+ flat/unflat marker at the same offset in both layouts, and write numbers in
302
+ one fixed byte order. Resize the handle only in the selectors listed in
303
+ [sdk/global-sequence-frame-data.md](sdk/global-sequence-frame-data.md#resizing-sequence-data).
304
+
305
+ Why: pointers saved in a project are garbage when it reopens, on the other
306
+ OS, or in the other app. Without the version field, an update cannot read
307
+ projects saved by the previous version.
308
+
309
+ ```cpp
310
+ typedef struct { A_u_long version; A_Boolean flat; char message[256]; PF_FpLong amount; } SeqFlat;
311
+ ```
312
+
313
+ Prefer parameters (including hidden ones) over sequence data: they are saved,
314
+ keyframable and thread-safe without extra work.
315
+
316
+ ## Memory and errors
317
+
318
+ ### 14. Allocate through After Effects and free what you allocate
319
+
320
+ Large buffers come from After Effects: `PF_HandleSuite1` for memory,
321
+ `PF_WorldSuite2` (`PF_NewWorld` / `PF_DisposeWorld`) for image buffers. Free
322
+ every world you create with `PF_DisposeWorld` (or `PF_DISPOSE_WORLD`).
323
+ `AEFX_CLR_STRUCT` only zeroes a struct; it frees nothing.
324
+
325
+ Why: After Effects frees its caches when your allocations need memory; plain
326
+ `malloc` competes with it and causes swapping or crashes during RAM preview.
327
+ A world disposed with `AEFX_CLR_STRUCT` leaks a full frame every render.
328
+
329
+ ### 15. Return every error, never throw out of the entry point
330
+
331
+ Return a `PF_Err` from every call and pass on any error a callback returns,
332
+ including `PF_Interrupt_CANCEL` (stop the frame) and `PF_Err_OUT_OF_MEMORY`.
333
+ Use the SDK's `ERR()` / `ERR2()` macros. Catch every C++ exception inside the
334
+ entry point and turn it into an error. To show a message, set
335
+ `PF_OutFlag_DISPLAY_ERROR_MESSAGE` and `out_data->return_msg`.
336
+
337
+ Why: a swallowed cancel caches a half-rendered frame; an exception that
338
+ crosses into After Effects crashes it (and behaves differently on Apple
339
+ silicon, see [sdk/apple-silicon-support.md](sdk/apple-silicon-support.md)).
340
+
341
+ ```cpp
342
+ extern "C" DllExport PF_Err EffectMain(PF_Cmd cmd, PF_InData *in_data, PF_OutData *out_data,
343
+ PF_ParamDef *params[], PF_LayerDef *output, void *extra) {
344
+ PF_Err err = PF_Err_NONE;
345
+ try {
346
+ switch (cmd) { /* ... */ }
347
+ } catch (const std::bad_alloc &) {
348
+ err = PF_Err_OUT_OF_MEMORY;
349
+ } catch (...) {
350
+ err = PF_Err_INTERNAL_STRUCT_DAMAGED;
351
+ }
352
+ return err;
353
+ }
354
+ ```
355
+
356
+ ## Hosts and builds
357
+
358
+ ### 16. Premiere Pro is a different host
359
+
360
+ The same binary loads in Premiere Pro from the shared MediaCore folder.
361
+ Check `in_data->appl_id` (`'PrMr'` is Premiere Pro). Premiere never sends
362
+ Smart Render selectors, uses other pixel formats (BGRA/VUYA, declared through
363
+ `PF_PixelFormatSuite1`) and lacks some suites. Until Premiere support is in
364
+ scope, test only in After Effects and say so. See
365
+ [sdk/ppro.md](sdk/ppro.md).
366
+
367
+ ### 17. Build clean
368
+
369
+ Compile without warnings and keep warnings on. Fix narrowing casts (they are
370
+ the source of wrap-around colours) instead of hiding them. Debug and release
371
+ builds use the same include paths, defines and struct alignment.
372
+
373
+ ## AfterPresets rules
374
+
375
+ ### 18. Licensing, logs and secrets
376
+
377
+ - Licensing and updates come only from the AfterPresets native runtime. Do
378
+ not write your own licence checks, serials or network calls.
379
+ - Never log or print licence tokens, proofs, account data or file paths that
380
+ contain the user's name.
381
+ - Do not put Adobe SDK files or SDK guide text in the product, its listing or
382
+ a public repository.
383
+
384
+ ## Things tutorials get wrong
385
+
386
+ Common in video tutorials and old samples. Do not copy:
387
+
388
+ | Tutorial habit | Do instead |
389
+ |---|---|
390
+ | White in 16 bpc is 32767 (or 65535) | `PF_MAX_CHAN16` = 32768 |
391
+ | "Free" a world with `AEFX_CLEAR_STRUCT` | `PF_DisposeWorld` (rule 14) |
392
+ | Pass values between calls in global variables | Read parameters per render, `pre_render_data`, sequence data (rule 11) |
393
+ | `rand()` per frame | Seed from parameters, time and position (rule 11) |
394
+ | Read a file (LUT) inside render | Load once in GlobalSetup / SequenceSetup (rule 12) |
395
+ | Hard-coded pixel coordinates | Downsample factors and buffer extents (rule 9) |
396
+ | `PF_ADD_POINT` default in pixels (1920, 1080) | Percent of the layer (50, 50 = centre) (rule 7) |
397
+ | "Treat warnings as errors: No" | Fix the casts (rule 17) |
398
+ | Read a param through the wrong union member | Rule 7 table |
399
+ | `xcrun altool` to notarise | Retired by Apple; `xcrun notarytool` |
400
+ | Paste out_flags hex from the debugger into the PiPL by hand | One source for both (rule 2) |
401
+ | "Nothing changed" after a rebuild | Stale frame cache: purge caches before judging a render ([testing.md](testing.md)) |
402
+ | Build into Program Files with an admin IDE | Build into the project; install with a separate step |