Feat/plugin support, allow third_party feat(spine) to create plugin for creator. - #306
Tianze-Chen wants to merge 4 commits into
Conversation
|
@cocos-robot run test cases |
Code Size Check Report
Interface Check Report! WARNING this pull request has changed these public interfaces:
@@ -2681,8 +2681,107 @@
protected _applyFontTexture(): void;
protected changeMaterialForDefine(): void;
}
/**
+ * @en A segment of the mesh: a range of indices drawn with one texture+material.
+ * @zh 网格的一个片段:一段索引,用同一纹理+材质绘制。
+ */
+ export interface UIMeshSegment {
+ indexOffset: number;
+ indexCount: number;
+ texture: Texture2D | null;
+ material: renderer.MaterialInstance | null;
+ }
+ /**
+ * @en Pre-baked mesh data for one frame.
+ * @zh 一帧的预烘焙网格数据。
+ * vertexStride: 24 (single-color V3F_T2F_C4B) or 28 (two-color V3F_T2F_C4B_C4B).
+ */
+ export interface UIMeshData {
+ vertexCount: number;
+ vertexStride: number;
+ vertexData: Uint8Array;
+ indexCount: number;
+ indexData: Uint8Array;
+ segments: UIMeshSegment[];
+ }
+ /**
+ * @en A generic 2D mesh renderer that consumes pre-baked vertex/index data.
+ * The data provider (e.g. a spine plugin) fills setMeshData every frame; this
+ * component handles buffer allocation, batching and submission.
+ * @zh 通用 2D 网格渲染器,消费预烘焙的顶点/索引数据。数据提供方(如 spine 插件)
+ * 每帧调用 setMeshData,本组件负责缓冲分配、合批与提交。
+ */
+ export class UIMesh extends UIRenderer {
+ protected _enableBatch: boolean;
+ /**
+ * @en Whether the incoming color data is premultiplied-alpha. Declares how
+ * cascaded opacity is folded into the vertices (alpha byte only vs
+ * RGBA + dark RGB) and the blend factors of the builtin material.
+ * @zh 输入颜色数据是否为预乘 alpha 格式。决定级联不透明度折算方式
+ * (仅 alpha 字节 vs RGBA + dark RGB)及内置材质混合因子。
+ */
+ protected _premultipliedAlpha: boolean;
+ protected _meshData: UIMeshData | null;
+ protected _useTint: boolean;
+ constructor();
+ /**
+ * @en Feeds the pre-baked mesh data for the current frame.
+ * @zh 喂入当前帧的预烘焙网格数据。
+ */
+ setMeshData(data: UIMeshData): void;
+ /**
+ * @en Whether to enable sprite batching.
+ * @zh 是否启用合批。
+ */
+ get enableBatch(): boolean;
+ set enableBatch(value: boolean);
+ /**
+ * @en Whether the input color data is premultiplied-alpha.
+ * @zh 输入颜色数据是否为预乘 alpha 格式。
+ */
+ get premultipliedAlpha(): boolean;
+ set premultipliedAlpha(value: boolean);
+ /**
+ * Notifies subclasses that the declared data format changed. The property
+ * itself is owned here (single source of truth); data producers — e.g. a
+ * spine plugin whose C++ side premultiplies vertex colors — override this
+ * to forward the format to their baker instead of redeclaring the field.
+ */
+ protected onPremultipliedAlphaChanged(): void;
+ onLoad(): void;
+ protected _updateColor(): void;
+ /**
+ * JSB staleness poll. The native draw-info path re-runs _prepareBuffers
+ * (which bakes the world transform and folds cascaded opacity into the
+ * vertex bytes) only when this renderer is marked dirty — and neither node
+ * transforms nor UIOpacity mark middleware renderers (TRANSFORM_CHANGED
+ * fires only on the changed node, UIOpacity writes localOpacity with no
+ * event). Poll the captured inputs and re-mark on drift. Web runs
+ * _prepareBuffers unconditionally through fillBuffers, so skip there.
+ * Subclasses overriding update should call super.update(dt).
+ */
+ update(dt: number): void;
+ protected _flushAssembler(): void;
+ updateRenderer(): void;
+ protected _render(batcher: any): void;
+ /**
+ * The builtin material is the spine effect (default-spine-material): the
+ * stock ui-sprite-material has no USE_LOCAL variant, so node-local input
+ * through the component material would render stuck at the origin. The
+ * spine effect carries both USE_LOCAL and TWO_COLORED macros; segment
+ * materials supplied via setMeshData are the provider's own contract.
+ */
+ protected _updateBuiltinMaterial(): Material;
+ /**
+ * Keeps the builtin material instance in step with the declared vertex
+ * space / data format: USE_LOCAL for the GPU take-over mode, TWO_COLORED
+ * for two-color data, and blend factors matching the alpha format.
+ */
+ updateMaterial(): void;
+ protected createRenderEntity(): __private._cocos_2d_renderer_render_entity__RenderEntity;
+ }
+ /**
* @en
* The Mask Component.
*
* @zh
@@ -61397,8 +61496,34 @@
stop(): void;
}
/**
* @en
+ * The engine's packaged cross-platform WebAssembly interface (pal/wasm).
+ *
+ * Re-exported under the public `cc.wasm` namespace so extension/game code can
+ * load its own `.wasm` files through the same platform-adaptive path the engine
+ * uses internally for box2d / physx / spine / webgpu:
+ *
+ * - web: fetch the `.wasm` bytes, then `WebAssembly.instantiate`;
+ * - mini-game: resolve the path into `cocos-js/` and delegate to the platform's
+ * `CCWebAssembly.instantiate` (which accepts a file path, never
+ * raw bytes — this is why embedded-base64 wasm fails there);
+ * - native: read the file from `src/cocos-js/` via `fileUtils`.
+ *
+ * The `wasmUrl` argument is a bare file name (e.g. `'foo.wasm'`) whose file is
+ * expected to land in the build output's `cocos-js/` directory.
+ * @zh
+ * 引擎封装好的跨平台 WebAssembly 接口(pal/wasm),通过 `cc.wasm` 命名空间公开,
+ * 供扩展/游戏代码用与引擎内部一致的路径加载自己的 `.wasm`。
+ */
+ export const wasm: {
+ instantiateWasm: typeof __private._pal_wasm__instantiateWasm;
+ fetchBuffer: typeof __private._pal_wasm__fetchBuffer;
+ fetchUrl: typeof __private._pal_wasm__fetchUrl;
+ ensureWasmModuleReady: typeof __private._pal_wasm__ensureWasmModuleReady;
+ };
+ /**
+ * @en
* WebView component, used to display web pages in the game.
* Since different platforms have different authorizations, APIs, and control methods for WebView components, there is no unified standard yet.
* So currently only Web, iOS, and Android platforms are supported.
* @zh
@@ -63657,8 +63782,12 @@
export class _cocos_2d_renderer_static_vb_accessor__StaticVBAccessor extends _cocos_2d_renderer_buffer_accessor__BufferAccessor {
static IB_SCALE: number;
static ID_COUNT: number;
get id(): number;
+ /** Per-chunk capacity; allocateChunk refuses requests above these. */
+ get maxVertexCount(): number;
+ /** Per-chunk index capacity; allocateChunk refuses requests above these. */
+ get maxIndexCount(): number;
constructor(device: gfx.Device, attributes: gfx.Attribute[], vCount?: number, iCount?: number);
destroy(): void;
reset(): void;
getVertexBuffer(bid: number): Float32Array;
@@ -76065,8 +76194,33 @@
enable(): void;
disable(noPause?: boolean): void;
syncMatrix(): void;
}
+ /**
+ * The first parameter of standard `Webassembly.instantiate` interface is the arraybuffer of wasm.
+ * But the implementation on some platforms is not standard.
+ * So here we provide a more commonly used interface, whose first parameter is the url of wasm.
+ *
+ * @param wasmUrl the url of wasm, this should be a url relative from build output chunk.
+ * @param importObject the standard `WebAssembly.Imports` instance
+ */
+ export function _pal_wasm__instantiateWasm(wasmUrl: string, importObject: WebAssembly.Imports): Promise<WebAssembly.WebAssemblyInstantiatedSource>;
+ /**
+ * Fetch binary data from wasm url or js mem url.
+ * NOTE: This method should only use to instantiate asm.js compiled with `-O2` options,
+ * because not all platforms support instantiate wasm by wasm binary.
+ * eg. WeChat can only instantiate wasm by wasm url.
+ *
+ * @param binaryUrl the url of wasm or js mem, this should be a url relative from build output chunk.
+ */
+ export function _pal_wasm__fetchBuffer(binaryUrl: string): Promise<ArrayBuffer>;
+ export function _pal_wasm__fetchUrl(binaryUrl: string): Promise<string>;
+ /**
+ * Sometimes we need to put wasm modules in subpackage to reduce code size.
+ * In this case we need to ensure that the wasm modules is ready before we import them.
+ * Please remember to invoke this method before we import wasm modules.
+ */
+ export function _pal_wasm__ensureWasmModuleReady(): Promise<void>;
export enum _cocos_web_view_web_view_enums__WebViewEventType {
/**
* @en None.
* @zh 无。
|
|
@Tianze-Chen, Please check the result of
Task Details
|
|
@Tianze-Chen, Please check the result of
Task Details |
New panel (panels/formats.js + editor/trim.js): pick the formats you need; applying moves decoder sources between runtime/ and trimmed/ (move, not copy, .meta travels so UUIDs survive), regenerates codecs.ts/index.ts, records the choice in editor/build/trim.json, and rewrites native/cc_plugin.json platforms so trimmed formats drop out of native builds too. Build hooks and main.js read trim.json to skip staging/copying the ~89KB animated-webp.wasm when WebP is trimmed. main.js also aligns disk state with the saved profile on load and warns when the extension is installed globally (shared runtime/ across projects). WebP ships off by default, and the committed tree matches: sources parked in trimmed/, codecs.ts without the webp entry, trim.json webp:false, cc_plugin.json platforms:[]. Reason: its off-native backend needs an engine that exports cc.wasm (cocos/cocos4#306) and no stable release has that yet, so defaulting it on would ship ~113KB nothing can load. Cocos 3.8 treats every mounted script as a bundle entry with no tree-shaking of unreferenced scripts, so physically moving the files is the only way trimming actually shrinks packages; README documents the mechanism, the WebP engine requirement, and the payload table. Co-Authored-By: Claude Code <noreply@anthropic.com>
d0de543 to
bbe76ed
Compare
|
@cocos-robot run test cases |
|
@Tianze-Chen, Please check the result of
Task Details
|
|
@Tianze-Chen, Please check the result of
Task Details |
bofeng-song
left a comment
There was a problem hiding this comment.
针对 UIMesh 的四处问题提出行内建议;以下基于代码静态审查,尚未运行外部 Spine 插件验证。
df4c951 to
cf45a35
Compare
- Add UIMesh component consuming pre-baked vertex/index/segment data, enabling custom renderers (e.g. spine plugin) to batch through the 2D batcher. - Export UIMesh from the 2d components index. - Validate setMeshData input at the boundary (stride, buffer lengths, index values, segment ranges, accessor caps) via engine-standard errorIDs 9010-9017, documented in EngineErrorMap.md. - Expose StaticVBAccessor per-chunk caps as maxVertexCount/maxIndexCount getters and clamp the 10% reserve to them, so meshes that fit (e.g. 30000 vertices) allocate instead of silently failing. - Add tests/ui/ui-mesh.test.ts covering the 32767 cap boundary, setMeshData validation, and accessor lifetime. Also takes over node transform and cascaded opacity: vertices are now always node-local — UIMesh applies the node's world matrix (unbatched via the per-draw GPU transform the 2D batcher already maintains for middleware, batched by baking into the shared chunk copy so draws still merge) and folds the cascaded UIOpacity into the copied vertex colors itself. The legacy switches (USE_LOCAL / useLocalData / RenderEntity.setUseLocal) are all derived internally; consumers never touch them. A JSB staleness poll re-prepares the native draw infos in the same frame on transform/opacity changes, and a premultipliedAlpha format declaration drives the opacity fold and the builtin material's blend factors. Follow-ups folded in: the opacity fold reads the light color at byte offset 20 (the pos*3f + uv*2f header is 20 bytes), not 16; and UIMesh notifies data producers through the new onPremultipliedAlphaChanged() hook so subclasses (e.g. the spine plugin) forward the declared format to their baker instead of redeclaring the field. Co-Authored-By: Claude Code <noreply@anthropic.com>
- Default the built-in Spine feature (spine-3.8) to off in feature cropping, so it is only included when enabled in the editor. - Recognize scale as a valid spine atlas page attribute in the texture inspector. - Skip incomplete platform dirs when bundling runtime adapters. Co-Authored-By: Claude <noreply@anthropic.com> (cherry picked from commit 7f7922aaf42ff1857f0a6d38970a75d303032f57)
- Add exports/webassembly.ts re-exporting pal/wasm (instantiateWasm, fetchBuffer, fetchUrl, ensureWasmModuleReady) as the public cc.wasm namespace, so extensions/game code can load their own .wasm files through the engine's platform-adaptive path. - Expose it as a croppable feature rather than an unconditional export from exports/base.ts: cc.config.json declares a `webassembly` feature and editor/engine-features/render-config.json adds the panel entry (default on, required), so it appears under Project Settings -> Feature Cropping. The label/description are plain text on purpose — i18n:ENGINE.* keys live in the editor package and a custom engine cannot add them. (cherry picked from commit 92003ab4a21b6ee753cf3ba2ca0f4767658e9cdc) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The generic plugin discovery chain (cc_gen_plugin_cmake_hook -> plugins_parser.js -> Pre-AutoLoadPlulgins.cmake -> find_package -> plugin_registry) already exists and is what native device builds use; the simulator never invoked it. Wire it in behind an opt-in cache variable, with no plugin names or plugin logic in the engine — the plugin set is always just the scan result: - templates/cmake/common.cmake: cc_gen_plugin_cmake_hook()'s scan roots (CC_PLUGIN_PATH) may now be pre-set by the caller; the native-build project-layout defaults are unchanged when nobody pre-defines it - simulator runtime-src CMakeLists.txt: -DSIMULATOR_PLUGIN_SCAN_DIRS="<dir> [;<dir>]" scans those roots for cc_plugin.json through the same chain as device builds (CC_PROJECT_DIR points into the build tree so the generated hook lands there, satisfying the parser's output-dir invariant); unset keeps the stock plugin-less simulator, and a cache variable means plain `cmake --build` reuses the configured scan - simulator Game.cpp: call cc_load_all_plugins() after init, mirroring BaseGame::init(), so registered plugins' JSB bindings exist before any script runs (no-op when no plugins are linked) - libsimulator protobuf config.h: skip the <hash_map>/<hash_set> defines on MSVC >= 1950 (headers removed); stubs fall back to the std::map emulation path Verified end-to-end: a project extensions/ tree scanned at configure time produces a simulator exe whose plugins register their JSB bindings at startup (observed live in the simulator's JS context). Co-Authored-By: Claude Code <noreply@anthropic.com>
cf45a35 to
5717377
Compare
|
@cocos-robot run test cases |
bofeng-song
left a comment
There was a problem hiding this comment.
复查最新提交:容量预留上限和 Accessor 随 Batcher 重建的修复已落实;仍有三处渲染问题,见行内评论。以下基于渲染链路静态审查及抽取 PR 实际方法的最小复现,其中原生共享缓冲生命周期使用模拟,尚未运行外部 Spine 插件或真机画面验证。
| if (JSB) meshBuffer.indexOffset = meshBuffer.sharedBuffer[2]; | ||
| const startIndex = meshBuffer.indexOffset; | ||
| const chunkOffset = rd.chunk.vertexOffset; | ||
| const offsetIndices = new Uint16Array(ic); | ||
| new Uint8Array(offsetIndices.buffer).set(data.indexData.subarray(0, ic * 2)); | ||
| for (let i = 0; i < ic; i++) offsetIndices[i] += chunkOffset; | ||
| rd.chunk.vertexAccessor.appendIndices(rd.chunk.bufferId, offsetIndices); |
There was a problem hiding this comment.
[P1] 原生共享索引缓冲重置后,只更新 dirty 网格会覆盖静止网格的索引。
UIRendererManager 只对 dirty renderer 调用 updateRenderer(),而原生 Batcher2d::uploadBuffers() 每帧会 reset mesh buffer 的索引偏移。假设 A、B 共用同一个 buffer:第一帧 A 的索引在 0、B 的索引在 3;第二帧 A 没有新数据、变换或透明度变化,只有 B 更新,那么这里同步到重置后的偏移 0,B 会覆盖 A 的索引,但 A 保留的 draw info 仍指向 0。A 因而绘制 B 的顶点,可能产生错图或重影,关闭合批也不能避免共享缓冲中的覆盖。
用本 PR 的 _prepareBuffers() 配合模拟原生 reset 的最小复现,A 的 [0,1,2] 在第二帧变成了 B 的 [3,4,5]。建议将每帧索引提交与顶点 dirty 更新分离:每帧为所有参与绘制的网格重建索引及 draw-info 偏移,或改为稳定的独立索引区间。请补充两个 UIMesh 中一个暂停、另一个持续更新的原生跨帧用例。
| for (let i = 0; i < vertexCount; i++) { | ||
| const o = i * strideF; | ||
| const x = floats[o]; | ||
| const y = floats[o + 1]; | ||
| floats[o] = m00 * x + m04 * y + m12; | ||
| floats[o + 1] = m01 * x + m05 * y + m13; |
There was a problem hiding this comment.
[P2] 合批路径只应用二维仿射变换,仍与非合批的完整节点矩阵不一致。
这里仅更新 X/Y,保留原始 Z,也没有处理输入 Z 对 X/Y 的贡献。例如局部点 (1,2,3),节点或父节点沿 Z 平移 10:非合批的 GPU 路径得到 (1,2,13),当前合批方法仍得到 (1,2,3)。绕 X/Y 轴旋转时也会丢失相应的三维变换;纯 XY 平移/缩放、绕 Z 轴旋转则不受影响。
建议使用完整世界矩阵变换 XYZ,并同步更新 update() 中的变换检测(目前也只检查六个二维矩阵分量),保证暂停动画时修改 Z 位移或 X/Y 旋转仍能刷新。请补充节点及父节点存在这些变换时,合批开关前后结果一致的用例。
| private _computeCascadedOpacity (): number { | ||
| let opacity = this._color.a / 255; | ||
| for (let node: Node | null = this.node; node; node = node.parent) { | ||
| opacity *= node._uiProps.localOpacity; | ||
| } | ||
| return opacity; |
There was a problem hiding this comment.
[P2] 级联透明度漏乘祖先 UI 渲染组件的 color.a。
这里计算的是自身 color.a × 各级 localOpacity,但 Batcher2D.walk() 会在每一级额外乘入该节点 UI 渲染组件的 color.a / 255。例如父节点有 Sprite,Sprite.color.a = 128,子节点 UIMesh 的顶点 alpha、自身 color.a 均为 255,所有 localOpacity 都为 1:标准 UI 级联结果约为 0.502,而此方法返回 1,UIMesh 仍完全不透明。
父节点的 UIOpacity.opacity = 128 已被本次修复覆盖;遗漏的是父级 Sprite/Label 等渲染组件的颜色 alpha。建议与 Batcher2D 的级联规则保持一致,并确保 JSB 的变化检测也包含该因素;增加修改父级组件 color.a(含动画暂停)的测试。
Re: #
Changelog
Continuous Integration
This pull request:
Compatibility Check
This pull request: