@kurot/core 1.0.28 → 2.0.1
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.
- package/CHANGELOG.md +1053 -0
- package/README.md +22 -2
- package/dist/kurot/player/canvas/CanvasRenderer.d.ts.map +1 -1
- package/dist/kurot/player/canvas/CanvasRenderer.js +7 -2
- package/dist/kurot/player/canvas/CanvasRenderer.js.map +1 -1
- package/dist/kurot/resource/ResourceConfig.d.ts +7 -1
- package/dist/kurot/resource/ResourceConfig.d.ts.map +1 -1
- package/dist/kurot/resource/ResourceConfig.js +26 -9
- package/dist/kurot/resource/ResourceConfig.js.map +1 -1
- package/dist/kurot/resource/index.d.ts +1 -1
- package/dist/kurot/resource/index.d.ts.map +1 -1
- package/dist/kurot/text/LineBreaks.d.ts +17 -0
- package/dist/kurot/text/LineBreaks.d.ts.map +1 -0
- package/dist/kurot/text/LineBreaks.js +37 -0
- package/dist/kurot/text/LineBreaks.js.map +1 -0
- package/dist/kurot/text/TextField.d.ts.map +1 -1
- package/dist/kurot/text/TextField.js +11 -118
- package/dist/kurot/text/TextField.js.map +1 -1
- package/dist/kurot/text/TextLineLayout.d.ts +18 -0
- package/dist/kurot/text/TextLineLayout.d.ts.map +1 -0
- package/dist/kurot/text/TextLineLayout.js +141 -0
- package/dist/kurot/text/TextLineLayout.js.map +1 -0
- package/dist/kurot/text/WordWrap.d.ts +5 -7
- package/dist/kurot/text/WordWrap.d.ts.map +1 -1
- package/dist/kurot/text/WordWrap.js +5 -7
- package/dist/kurot/text/WordWrap.js.map +1 -1
- package/docs/ai-context.md +226 -0
- package/docs/architecture.md +371 -0
- package/docs/filters.md +232 -0
- package/docs/resource.md +333 -0
- package/docs/text-layout.md +59 -0
- package/package.json +6 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# @kurot/core — AI context map
|
|
2
|
+
|
|
3
|
+
Read this before exploring `src/`. It is a compressed map of the package so an
|
|
4
|
+
agent unfamiliar with Kurot does not need to re-derive the architecture from
|
|
5
|
+
scratch on every session. Treat the package source and its `src/index.ts`
|
|
6
|
+
barrel as the authority for current behavior and exports.
|
|
7
|
+
|
|
8
|
+
Package identity: `@kurot/core@2.0.1`. It provides Kurot's scene graph,
|
|
9
|
+
events, rendering, text, resource, network and media runtime. Rendering uses a
|
|
10
|
+
flat `InstructionSet + RenderPipe` pipeline. ES2022 / evergreen browsers only
|
|
11
|
+
with `strict: true`. Two
|
|
12
|
+
rendering backends: WebGL (primary, two-phase build/execute, InstructionSet-
|
|
13
|
+
driven) and Canvas 2D (fallback, direct scene-graph traversal every frame — no
|
|
14
|
+
InstructionSet, no dirty-flag optimization).
|
|
15
|
+
|
|
16
|
+
Source root: `src/kurot/`. Public API: `src/index.ts` re-exports each
|
|
17
|
+
subfolder's `index.ts` — see §4 for the full grouped list before assuming
|
|
18
|
+
something isn't exported.
|
|
19
|
+
|
|
20
|
+
## 1. Directory map
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/kurot/
|
|
24
|
+
├── display/ Scene graph: DisplayObject → DisplayObjectContainer → Sprite/Stage,
|
|
25
|
+
│ Bitmap, Shape, Mesh, Graphics (flat GraphicsCommand[], not a node tree),
|
|
26
|
+
│ texture/ (BitmapData, Texture, RenderTexture, SpriteSheet).
|
|
27
|
+
│ Defines the retained tree the renderer reads from; does not render itself.
|
|
28
|
+
├── player/ Game loop + both render backends. Player, createPlayer(), SystemTicker/
|
|
29
|
+
│ ticker singleton, ScreenAdapter, TouchHandler. Backend-neutral abstractions
|
|
30
|
+
│ live here: RenderPipe / RenderContext / RenderBuffer interfaces (the latter
|
|
31
|
+
│ two are internal), InstructionSet, and pipes/ (Bitmap/Graphics/Mesh/Text/
|
|
32
|
+
│ Filter/Mask/Particle). The WebGL instruction renderer consumes the pipes.
|
|
33
|
+
│ ├── webgl/ WebGLRenderer, WebGLRenderContext, WebGLRenderBuffer/Target,
|
|
34
|
+
│ │ WebGLVertexArrayObject, WebGLDrawCmdManager, MultiTextureBatcher,
|
|
35
|
+
│ │ shaders/ (ShaderLib GLSL 1.00, ShaderLib2 GLSL 3.00). The WebGL-only execute path.
|
|
36
|
+
│ └── canvas/ CanvasRenderer (fallback AND a dependency of the WebGL path — see §2),
|
|
37
|
+
│ DisplayList (offscreen cache backing cacheAsBitmap), CanvasBuffer.
|
|
38
|
+
├── events/ Event, EventDispatcher (capture/bubble, once()), EventPhase,
|
|
39
|
+
│ 8 concrete subclasses (TouchEvent, TimerEvent, ProgressEvent, etc.).
|
|
40
|
+
├── geom/ Matrix, Point, Rectangle + shared*/create()/release() object pools.
|
|
41
|
+
├── filters/ Filter base + BlurFilter, GlowFilter, DropShadowFilter, ColorMatrixFilter,
|
|
42
|
+
│ CustomFilter, MultiPassFilter, BloomFilter. GPU execution in player/webgl/;
|
|
43
|
+
│ push/pop scene integration in player/pipes/.
|
|
44
|
+
├── text/ TextField, BitmapText/BitmapFont, StageText (DOM overlay, INPUT mode only),
|
|
45
|
+
│ HtmlTextParser, InputController, TextMeasurer, LineBreaks, TextLineLayout, WordWrap.
|
|
46
|
+
├── resource/ Resource class + `resource` singleton, ResourceLoader, analyzers/
|
|
47
|
+
│ (Image/Json/Text/Sound/Sheet). Async, resource.json-driven (RES-compatible).
|
|
48
|
+
├── net/ HttpRequest, ImageLoader. Low-level; resource/analyzers build on these.
|
|
49
|
+
├── media/ Sound (+SoundChannel), Video. Web Audio + HTMLAudioElement fallback.
|
|
50
|
+
├── system/ Capabilities (static). Must be _init()'d — createPlayer() does this for you.
|
|
51
|
+
├── utils/ ByteArray, Timer, Logger, FontManager, DebugLog, Base64Util, NumberUtils.
|
|
52
|
+
│ No engine-wide numeric object identity API is provided.
|
|
53
|
+
├── localStorage/ Plain functions (getItem/setItem/removeItem/clear), exported as a namespace.
|
|
54
|
+
└── external/ ExternalInterface — bridge to window.* host callbacks.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The dev-only benchmark page, runtime, adapters, vendor baseline, and tests are
|
|
58
|
+
colocated under `examples/benchmark/`; none are exported from `index.ts`.
|
|
59
|
+
|
|
60
|
+
## 2. Non-obvious current behavior
|
|
61
|
+
|
|
62
|
+
- `DisplayObject.matrix` getter returns a **clone**, not a live reference.
|
|
63
|
+
Mutating the returned matrix does nothing — assign it back or use `$setMatrix`.
|
|
64
|
+
- `getChildAt`/`removeChildAt` return `undefined` on out-of-bounds instead of
|
|
65
|
+
throwing. `removeChildren()` returns `void`, not the removed array.
|
|
66
|
+
- `blendMode` string values are Canvas 2D composite-operation names;
|
|
67
|
+
the normal value is `"source-over"`.
|
|
68
|
+
- `cacheAsBitmap` is a pure alias for `cacheAsTexture(true)` — there is no
|
|
69
|
+
separate legacy path. It **always** rasterizes to an offscreen Canvas 2D
|
|
70
|
+
surface first (`DisplayList`), then optionally re-uploads as a GL texture.
|
|
71
|
+
It never means "cache directly to a GPU framebuffer."
|
|
72
|
+
- `DisplayObjectContainer.isRenderGroup` has **zero visual effect on Canvas 2D
|
|
73
|
+
rendering**. It's a WebGL-only optimization hint that isolates a subtree
|
|
74
|
+
into its own `InstructionSet` — toggling it never changes pixels, only which
|
|
75
|
+
instruction set absorbs rebuilds.
|
|
76
|
+
- `CanvasRenderer` is not purely a fallback. The WebGL backend depends on it
|
|
77
|
+
internally to rasterize Graphics/Text to offscreen canvases before texture
|
|
78
|
+
upload, and to snapshot `RenderTexture`. Don't reason about it as dead code
|
|
79
|
+
when WebGL is active.
|
|
80
|
+
- The engine is **single-Player by design**. `Player`'s constructor wires
|
|
81
|
+
static hook fields directly onto `DisplayObject`/`DisplayObjectContainer`
|
|
82
|
+
(`$onStructureChange`, etc.) — there is no listener registry. A second
|
|
83
|
+
`Player` instance clobbers the first one's hooks.
|
|
84
|
+
- The `WebGLRenderBuffer.release(WebGLRenderBuffer.create(...))` line at the
|
|
85
|
+
end of `WebGLRenderer.render()` looks like dead code but isn't — it resets
|
|
86
|
+
the GL viewport/projection after an in-frame offscreen activation (filter,
|
|
87
|
+
mask, cacheAsBitmap). It's marked "DO NOT delete" in the source for a reason.
|
|
88
|
+
- `RenderPipe.destroyRenderable()` (immediate GPU resource release) is **not**
|
|
89
|
+
called automatically on removal from stage, because `$onRemoveFromStage`
|
|
90
|
+
also fires for temporary removals (e.g. virtualized lists). `GraphicsPipe`
|
|
91
|
+
and `TextPipe` instead rely on a `FinalizationRegistry` to `gl.deleteTexture()`
|
|
92
|
+
when the JS object is actually garbage-collected.
|
|
93
|
+
- `Resource` deliberately does **not** extend `EventDispatcher` — it has its
|
|
94
|
+
own `on`/`off`/`onProgress` API. Don't assume every "event-ish" class in
|
|
95
|
+
this codebase shares one dispatch shape.
|
|
96
|
+
- `resource.onProgress(cb)` listeners are permanent (never auto-removed); use
|
|
97
|
+
`loadGroup()`'s own `onProgress` param for one-off tracking. Concurrent
|
|
98
|
+
`loadGroup()` calls serialize through one internal `ResourceLoader`, they do
|
|
99
|
+
not run in parallel.
|
|
100
|
+
- `Base64Util.encode()` takes an `ArrayBuffer`, not a `string`
|
|
101
|
+
(`encode(new TextEncoder().encode(str))`).
|
|
102
|
+
- `Capabilities` must be initialized (`Capabilities._init()`) before use.
|
|
103
|
+
`createPlayer()` does this automatically; constructing `Player` directly
|
|
104
|
+
bypasses it and leaves capability queries stale.
|
|
105
|
+
- `StageText` (a real DOM `<input>` overlaid on the canvas) is only used for
|
|
106
|
+
`TextFieldType.INPUT` — the rest of `TextField` is fully canvas/GPU-drawn.
|
|
107
|
+
- GPU filter arrays execute in order. `CustomFilter.from()` takes explicit
|
|
108
|
+
WebGL 1/2 sources with numeric uniforms and auxiliary full-image BitmapData
|
|
109
|
+
bindings. `MultiPassFilter` retains earlier/original inputs for an acyclic
|
|
110
|
+
pass graph; `BloomFilter` is an LDR extraction/blur/composite example.
|
|
111
|
+
See `docs/filters.md` for exact UV, alpha, resolution and resource contracts.
|
|
112
|
+
- `BlurFilter.quality` controls pass pairs (1–16); large radii downsample to
|
|
113
|
+
stay within the existing 32-physical-pixel shader tier. Glow/DropShadow keep
|
|
114
|
+
their existing fixed-sample shader and do not use their quality metadata.
|
|
115
|
+
- GPU filters are skipped by Canvas 2D, including the Canvas capture used by
|
|
116
|
+
cacheAsTexture and RenderTexture. Do not cache GPU effects through those APIs.
|
|
117
|
+
- Filter setters invalidate weakly attached users. Direct uniform/binding edits
|
|
118
|
+
need `filter.invalidate()`. Auxiliary canvas/video edits additionally need
|
|
119
|
+
`BitmapData.invalidate(source)` for upload refresh.
|
|
120
|
+
- Intermediate filter targets share a 16-entry / 64 MiB idle pool. Active targets,
|
|
121
|
+
subtree captures and auxiliary uploads are separate. Player.destroy releases
|
|
122
|
+
idle effect targets and auxiliary uploads. Shader programs are cached per GL
|
|
123
|
+
context and source; compile/link failures throw and abort the failed frame.
|
|
124
|
+
- `Video` extends `Bitmap` and is directly renderable. It swaps between poster
|
|
125
|
+
and video textures, invalidates `BitmapData` on each available video frame,
|
|
126
|
+
and uses `requestAnimationFrame` only when `requestVideoFrameCallback` is
|
|
127
|
+
unavailable.
|
|
128
|
+
- hitTest / lookup APIs return `undefined`, never `null`, throughout — check
|
|
129
|
+
`=== undefined`.
|
|
130
|
+
|
|
131
|
+
## 3. Domain-specific terminology
|
|
132
|
+
|
|
133
|
+
| Term | Definition | Where defined |
|
|
134
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
135
|
+
| `InstructionSet` | Flat, reusable array of `Instruction` objects representing one frame's draw ops for a subtree, replacing scene-graph traversal at execute time. Tracks `structureDirty`, `dirtyRenderables`, and a `renderableIndex: Map<DisplayObject, number\|number[]>` for O(1) lookup during incremental patches. | `player/InstructionSet.ts` |
|
|
136
|
+
| `RenderPipe` | Interface implemented once per display-object category (`BitmapPipe`, `GraphicsPipe`, `MeshPipe`, `TextPipe`, `FilterPipe`, `MaskPipe`, `ParticlePipe`). It defines `addToInstructionSet()` (build), `updateRenderable()` (patch), and optional `destroyRenderable()` cleanup. The current renderer does not invoke cleanup merely because an object leaves the stage. Instructions are dispatched by `renderPipeId`. | `player/RenderPipe.ts`, implementations in `player/pipes/` |
|
|
137
|
+
| `structureDirty` vs `renderDirty` | `structureDirty` (on `InstructionSet`): topology changed (child added/removed/reordered, filter/mask changed) → full rebuild. `renderDirty` (on `DisplayObject`, propagated via `$renderDirtyUp()`): only visual data changed (position/alpha/tint/texture) on an object whose instruction-list slot is still valid → patch only, no rebuild. | Propagated in `display/DisplayObject.ts` (`$markDirty()`, `$cacheDirtyUp()`, `$renderDirtyUp()`); consumed in `player/webgl/WebGLRenderer.ts` `render()` |
|
|
138
|
+
| `RenderGroup` | A `DisplayObjectContainer` with `isRenderGroup = true`. Gets its own independent `InstructionSet` (tracked via `WeakMap`/`WeakRef` in `WebGLRenderer`). Structural changes inside the group never force the parent to rebuild — the parent set holds a single `renderGroup` instruction pointing at the child set. | `isRenderGroup` field: `display/DisplayObjectContainer.ts`; group handling: `player/webgl/WebGLRenderer.ts` (`_buildRenderGroup()`, `markStructureDirty()`, `markRenderableDirty()`) |
|
|
139
|
+
| `cacheAsTexture` vs `cacheAsBitmap` | `cacheAsBitmap` is a pure alias for `cacheAsTexture(true/false)`. `cacheAsTexture(options)` accepts `{resolution?, scaleMode?}` and creates a `DisplayList` (offscreen Canvas 2D buffer), reused across frames until dirty. On WebGL it's drawn as a single `displayListCache` instruction; rasterization still goes through `CanvasRenderer`, then the result is uploaded as a GL texture. | `display/DisplayObject.ts`; `player/canvas/DisplayList.ts`; `player/webgl/WebGLRenderer.ts` (`_executeDisplayListCache()`) |
|
|
140
|
+
| `DrawCmdManager` (`WebGLDrawCmdManager`) | Queue of `DrawCmd` records (12 types: TEXTURE, RECT, PUSH*MASK, POP_MASK, BLEND, RESIZE_TARGET, CLEAR_COLOR, ACT_BUFFER, ENABLE_SCISSOR, DISABLE_SCISSOR, SMOOTHING, MULTI_TEXTURE) sitting between `RenderPipe`s and actual GL calls. Auto-merges consecutive compatible commands. `WebGLRenderContext._flush()` walks it once per frame. This is the real batching layer — distinct from `MultiTextureBatcher`, which only assigns texture \_slots* for the `MULTI_TEXTURE` command. | `player/webgl/WebGLDrawCmdManager.ts` |
|
|
141
|
+
| `WebGLRenderContext` lifecycle | Constructed directly by `Player` (no factory/singleton). Auto-selects `webgl2` (ShaderLib2, GLSL ES 3.00) falling back to `webgl` (ShaderLib, GLSL ES 1.00). Listens for `webglcontextlost`/`webglcontextrestored`; on restore, re-creates GPU buffers, clears the shader cache, invalidates tracked `BitmapData.webGLTexture` refs, and tells `WebGLRenderer` to force a full instruction rebuild. | `player/webgl/WebGLRenderContext.ts` |
|
|
142
|
+
| `Player` vs `Stage` | `Stage` is a `DisplayObjectContainer` subclass: root of the scene graph, holds `stageWidth`/`stageHeight`/`scaleMode`/`orientation`/`frameRate` (proxies to the global `ticker`). No rendering logic itself. `Player` owns the `<canvas>`, constructs the WebGL-or-Canvas2D renderer, registers with `ticker` on `start()`, and calls `renderer.render(stage, buffer, matrix)` every tick. One `Player` per app — see §2. | `display/Stage.ts`, `player/Player.ts` |
|
|
143
|
+
| Logical size vs render resolution | `ScreenAdapter` keeps Stage and touch coordinates in logical units while sizing the Canvas backing store independently from its CSS size. `KurotOptions.resolution` defaults to `min(devicePixelRatio, 2)` and `ScreenAdapter.resolution` can change it at runtime. `Player` supplies the logical-to-physical root matrix; text, automatic display-list caches, and effect buffers inherit the resulting renderer resolution unless explicitly overridden. | `player/ScreenAdapter.ts`, `player/Player.ts`, `player/pipes/TextPipe.ts`, `player/canvas/DisplayList.ts` |
|
|
144
|
+
| GPU texture GC | `GraphicsPipe`/`TextPipe` register cached textures through `RenderContext.registerTextureForGC()`. The WebGL context owns the module-level `FinalizationRegistry`; explicit destruction unregisters the token and deletes the texture immediately. | `player/RenderContext.ts`, `player/pipes/GraphicsPipe.ts`, `player/pipes/TextPipe.ts`, `player/webgl/WebGLRenderContext.ts` |
|
|
145
|
+
|
|
146
|
+
## 4. Public API surface (`src/index.ts`)
|
|
147
|
+
|
|
148
|
+
Re-export order: `events`, `geom`, `utils`, `display`, `net`, `filters`,
|
|
149
|
+
`media`, `player`, `text`, `system`, then `localStorage` (namespaced),
|
|
150
|
+
`ExternalInterface` (named), `resource`.
|
|
151
|
+
|
|
152
|
+
- **Events**: `Event`, `EventMap` (type), `EventPhase`, `IEventDispatcher`, `EventDispatcher`, `FocusEvent`, `HTTPStatusEvent`, `IOErrorEvent`, `ProgressEvent`, `StageOrientationEvent`, `TextEvent`, `TimerEvent`, `TouchEvent`.
|
|
153
|
+
- **Geom**: `Point`/`sharedPoint`, `Rectangle`/`sharedRectangle`, `Matrix`/`sharedMatrix`.
|
|
154
|
+
- **Utils**: `NumberUtils`, `Base64Util`, `toColorString`, `Logger`/`LogLevel`, `Timer`/`TimerEvents`, `ByteArray`/`Endian`, `registerFontMapping`/`cacheFontResource`, `DebugLog`.
|
|
155
|
+
- **Display**: enums (`BitmapFillMode`, `BlendMode`/`blendModeToNumber`/`numberToBlendMode`, `CapsStyle`, `GradientType`, `JointStyle`, `OrientationMode`, `StageScaleMode`); `DisplayObject`/`RenderMode`/`RenderObjectType`/`DisplayObjectEvents`/`CacheAsTextureOptions`; `DisplayObjectContainer`, `Stage`, `Graphics`/`setGraphicsHitTest`, `Shape`, `Sprite`, `Bitmap`/`setBitmapPixelHitTest`, `Mesh`; textures: `BitmapData`/`CompressedTextureData`, `Texture`/`textureScaleFactor`, `RenderTexture`, `SpriteSheet`; `PathCommandType`/`GraphicsCommand` (type).
|
|
156
|
+
- **Net**: `HttpMethod`/`HttpMethodType`, `HttpResponseType`/`HttpResponseTypeType`, `HttpRequest`/`HttpRequestEvents`, `ImageLoader`/`ImageLoaderEvents`.
|
|
157
|
+
- **Filters**: `Filter`, `BlurFilter`, `ColorMatrixFilter`, `GlowFilter`, `DropShadowFilter`, `CustomFilter`, `MultiPassFilter`, `BloomFilter`; types `CustomFilterOptions`, `CustomFilterUniform`, `FilterProgramSource`, `FilterTexture`, `FilterPass`, `FilterPassInput`, `BloomFilterOptions`.
|
|
158
|
+
- **Media**: `Sound`/`SoundType`/`SoundEvents`, `SoundChannel`, `Video`.
|
|
159
|
+
- **Player**: `Player`, `createPlayer`/`KurotApp`/`KurotOptions`; ticker: `SystemTicker`, `ticker`, `getTimer`, `setupLifecycle`, `START_TIME`, `invalidateRenderFlag`/`setInvalidateRenderFlag`, `requestRenderingFlag`/`setRequestRenderingFlag`, `Renderable`; rendering: `InstructionSet`/`Instruction`, `RenderPipe` (type), `CanvasBuffer`, `hitTestBuffer`, `CanvasRenderer`, `DisplayList`; input/layout: `TouchHandler`, `ScreenAdapter`/`StageDisplaySize`; WebGL: `WebGLRenderer`, `WebGLRenderContext`, `WebGLRenderBuffer`, `WebGLRenderTarget`, `WebGLVertexArrayObject`, `WebGLDrawCmdManager`, `WebGLProgram`, `ShaderLib`, `checkWebGLSupport`, `MultiTextureBatcher`.
|
|
160
|
+
- `RenderContext` / `RenderBuffer` (`player/RenderContext.ts`, `player/RenderBuffer.ts`) are internal backend-neutral contracts and are not re-exported.
|
|
161
|
+
- **Text**: `HorizontalAlign`, `VerticalAlign`, `TextFieldType`, `TextFieldInputType`; types `ITextStyle`, `ITextElement`, `IWTextElement`, `ILineElement`, `IHitTextElement`; `HtmlTextParser`, `BitmapFont`, `BitmapText`, `measureText`/`getFontString`, `TextField`, `StageText`, `InputController`, `tokenize`/`splitGraphemes`.
|
|
162
|
+
- **System**: `Capabilities`.
|
|
163
|
+
- **localStorage**: namespace object — `import { localStorage } from '@kurot/core'`, then `localStorage.getItem(...)`.
|
|
164
|
+
- **External**: `ExternalInterface` (named, not namespaced).
|
|
165
|
+
- **Resource**: `Resource`/`resource` (shared instance), `ProgressCallback`, `ResourceEventListener`, `ResourceItem`, `ResourceType`, `ResourceConfig`/`ResourceConfigData`/`ResourceConfigEntry`, `ResourceLoader`, `ResourceEventType`/`ResourceEvent`, `AnalyzerBase`, `ImageAnalyzer`, `JsonAnalyzer`, `TextAnalyzer`, `SoundAnalyzer`, `SheetAnalyzer`.
|
|
166
|
+
|
|
167
|
+
`examples/benchmark/` is dev-only tooling and is **not** exported from `index.ts`.
|
|
168
|
+
|
|
169
|
+
## 5. Current API constraints
|
|
170
|
+
|
|
171
|
+
- Object identity uses object references; there is no `hashCode` API.
|
|
172
|
+
- Import the resource singleton as `resource`; `Resource` can also be
|
|
173
|
+
instantiated directly.
|
|
174
|
+
- One `Player` owns the runtime hooks for a page. Constructing another player
|
|
175
|
+
replaces those static hooks.
|
|
176
|
+
- `Player` constructs its own `WebGLRenderContext`; there is no context
|
|
177
|
+
singleton.
|
|
178
|
+
- `$`-prefixed members are engine internals even when TypeScript visibility
|
|
179
|
+
permits package-level access.
|
|
180
|
+
- `Base64Util.encode()` accepts `ArrayBuffer`.
|
|
181
|
+
- Pixel reads are provided by `RenderTexture.getPixel32()`; the corresponding
|
|
182
|
+
base `Texture` read/export methods throw unsupported-operation errors.
|
|
183
|
+
- Hit-test and lookup misses return `undefined`.
|
|
184
|
+
- The supported runtime target is the browser; there is no native-wrapper
|
|
185
|
+
backend.
|
|
186
|
+
- Event sources can specialize `EventDispatcher<EventMap>` for typed listener
|
|
187
|
+
callbacks; untyped callers use the base `Event` overload.
|
|
188
|
+
- `MultiTextureBatcher.MAX_TEXTURES` is currently fixed at 8.
|
|
189
|
+
|
|
190
|
+
## 6. Task → file map
|
|
191
|
+
|
|
192
|
+
| I want to... | Look at |
|
|
193
|
+
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| Add a new DisplayObject subclass | `display/DisplayObject.ts`, model it on `display/Shape.ts` |
|
|
195
|
+
| Add a new filter | `docs/filters.md`, `filters/CustomFilter.ts`, `filters/MultiPassFilter.ts`; execution in `player/webgl/WebGLFilterSystem.ts`, scene integration in `player/pipes/FilterPipe.ts` |
|
|
196
|
+
| Change how instructions are built/executed | `player/webgl/WebGLRenderer.ts`, `player/InstructionSet.ts` |
|
|
197
|
+
| Add a new resource type/parser | `resource/analyzers/`, register in `Resource.ts` |
|
|
198
|
+
| Debug a texture-batching issue | `player/webgl/MultiTextureBatcher.ts`, `player/webgl/WebGLDrawCmdManager.ts` |
|
|
199
|
+
| Change text layout/wrapping | `text/LineBreaks.ts`, `text/TextLineLayout.ts`, `text/TextMeasurer.ts`; `docs/text-layout.md` |
|
|
200
|
+
| Understand dirty-flag propagation | `display/DisplayObject.ts` (`$markDirty`, `$cacheDirtyUp`, `$renderDirtyUp`) |
|
|
201
|
+
| Run perf tests | `examples/benchmark/`, `pnpm benchmark`; automated Kurot/PixiJS/Egret comparison via `pnpm benchmark:compare` |
|
|
202
|
+
|
|
203
|
+
### Mesh geometry and batching
|
|
204
|
+
|
|
205
|
+
Mesh vertices are local positions; texture trim offsets do not shift geometry.
|
|
206
|
+
UVs are relative to the unrotated texture region; both backends map atlas
|
|
207
|
+
rotation during sampling. Resize geometry using scaleX/scaleY. After mutating
|
|
208
|
+
vertices, UVs, or indices, call updateVertices(). WebGL keeps Mesh indices
|
|
209
|
+
separate from quad indices and splits oversized meshes into ordered batches
|
|
210
|
+
with local index remapping (player/webgl/split-mesh.ts).
|
|
211
|
+
|
|
212
|
+
## Text layout in 2.0.1
|
|
213
|
+
|
|
214
|
+
Word-wrapped text uses Unicode 17.0 UAX #14, dictionary tailoring for SA scripts
|
|
215
|
+
and alphabetic overflow tailoring. Style runs never introduce break positions.
|
|
216
|
+
Automatic-wrap spaces count in source offsets but not painted width. Core's
|
|
217
|
+
Canvas renderer advances input indices by line.charNum, including hidden spaces
|
|
218
|
+
and CRLF; do not derive the next line's source offset from painted text lengths.
|
|
219
|
+
See [text layout](text-layout.md).
|
|
220
|
+
|
|
221
|
+
## Resource configuration change in 2.0.0
|
|
222
|
+
|
|
223
|
+
`ResourceConfigEntry.subkeys` is an object map, and Core rejects legacy strings
|
|
224
|
+
before mutating the resource registry. See
|
|
225
|
+
[the resource-default contract](../../ui-document/docs/resource-nine-slice.md)
|
|
226
|
+
for conversion and compilation boundaries.
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# @kurot/core 架构文档
|
|
2
|
+
|
|
3
|
+
> 当前版本:2.0.0。逐条变更记录见 [CHANGELOG.md](../CHANGELOG.md)。
|
|
4
|
+
> 面向 AI 智能体的速查文档见 [ai-context.md](./ai-context.md)(目录地图、反直觉行为清单、术语表、任务→文件速查表)。本文档面向人类读者,讲设计动机与内部机制,两份文档不重复内容,互相引用。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 一、项目概述
|
|
9
|
+
|
|
10
|
+
`@kurot/core` 是 Kurot 引擎的核心运行时,提供显示对象、渲染、事件、几何、文本、资源、网络与媒体能力。对外 API 沿用 Egret 的 `DisplayObject`/事件模型,渲染内核则重写为 Pixi.js 8 风格的扁平 "InstructionSet + RenderPipe" 管线。
|
|
11
|
+
|
|
12
|
+
| 维度 | 说明 |
|
|
13
|
+
| -------- | ----------------------------------------------- |
|
|
14
|
+
| 模块系统 | ES Module(`@kurot/core` npm 包) |
|
|
15
|
+
| 类型系统 | `strict: true`,全量类型安全,零 `any` |
|
|
16
|
+
| 编译目标 | ES2022,仅支持现代浏览器 |
|
|
17
|
+
| 渲染架构 | InstructionSet 指令驱动(借鉴 Pixi.js 8) |
|
|
18
|
+
| 批处理 | 多纹理批处理(8 张/批) |
|
|
19
|
+
| 包管理 | `@kurot/core` workspace 包,pnpm |
|
|
20
|
+
| 资源管理 | Resource 完整资源生命周期 |
|
|
21
|
+
| WebGL | WebGL 1 + WebGL 2 双后端,运行时自动选择 |
|
|
22
|
+
|
|
23
|
+
与 Egret 的差异、Breaking Changes 及迁移方式见根目录 [egret-migration.md](../../../docs/egret-migration.md)。
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 二、模块结构
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
packages/core/src/kurot/
|
|
31
|
+
├── display/ # 显示对象层(场景图)
|
|
32
|
+
│ ├── DisplayObject.ts # 基类,含 renderDirty/cacheDirty/renderMode/RenderObjectType/bounds 缓存
|
|
33
|
+
│ ├── DisplayObjectContainer.ts # 容器,含 isRenderGroup/sortableChildren/zIndex 排序
|
|
34
|
+
│ ├── Bitmap.ts # 位图显示(含 scale9Grid 九宫格)
|
|
35
|
+
│ ├── Sprite.ts # 精灵(容器 + Graphics)
|
|
36
|
+
│ ├── Shape.ts # 矢量图形
|
|
37
|
+
│ ├── Mesh.ts # 网格(vertices/indices/uvs)
|
|
38
|
+
│ ├── Stage.ts # 舞台根节点
|
|
39
|
+
│ ├── Graphics.ts # 矢量绘图命令
|
|
40
|
+
│ ├── GraphicsPath.ts # 命令类型定义(PathCommandType, GraphicsCommand)
|
|
41
|
+
│ ├── enums/ # BlendMode, BitmapFillMode, CapsStyle, GradientType, JointStyle, OrientationMode, StageScaleMode
|
|
42
|
+
│ └── texture/ # BitmapData, Texture, SpriteSheet, RenderTexture
|
|
43
|
+
├── player/ # 渲染管线 + 游戏循环
|
|
44
|
+
│ ├── Player.ts # 播放器(Stage + Renderer 绑定,含 perf 性能指标)
|
|
45
|
+
│ ├── SystemTicker.ts # 帧循环(RAF + ENTER_FRAME,含 callLater 延迟调用队列)
|
|
46
|
+
│ ├── RenderPipe.ts # RenderPipe 接口定义
|
|
47
|
+
│ ├── TouchHandler.ts # 触摸/鼠标输入
|
|
48
|
+
│ ├── ScreenAdapter.ts # 屏幕适配(7种缩放模式)
|
|
49
|
+
│ ├── createPlayer.ts # 统一创建入口(含 Capabilities 初始化、RenderTexture 渲染器注入)
|
|
50
|
+
│ ├── KurotOptions.ts # 配置接口
|
|
51
|
+
│ ├── canvas/ # Canvas 2D 渲染后端(降级方案)
|
|
52
|
+
│ │ ├── CanvasRenderer.ts # 直接遍历渲染器(支持全部 DisplayObject 类型 + 滤镜降级)
|
|
53
|
+
│ │ ├── DisplayList.ts # cacheAsBitmap 离屏缓存
|
|
54
|
+
│ │ └── CanvasBuffer.ts # Canvas 2D 缓冲区(含 hitTestBuffer 像素级命中测试)
|
|
55
|
+
│ └── webgl/ # WebGL 渲染后端(主渲染器)
|
|
56
|
+
│ ├── WebGLRenderer.ts # 两阶段渲染器(build + execute,含 RenderGroup/DisplayListCache 支持)
|
|
57
|
+
│ ├── WebGLRenderContext.ts # WebGL 状态管理 + draw 调度(WebGL1/2 自动选择,配合 WebGLFilterSystem 管理效果)
|
|
58
|
+
│ ├── WebGLRenderBuffer.ts # WebGL 缓冲区(含对象池,支持 offscreen/stencil/scissor)
|
|
59
|
+
│ ├── WebGLRenderTarget.ts # FBO 管理
|
|
60
|
+
│ ├── WebGLVertexArrayObject.ts # VAO 顶点管理(单纹理 20B + 多纹理 24B 双布局)
|
|
61
|
+
│ ├── WebGLDrawCmdManager.ts # 绘制命令队列(12 种命令类型,含 MULTI_TEXTURE)
|
|
62
|
+
│ ├── WebGLProgram.ts # 着色器程序缓存
|
|
63
|
+
│ ├── WebGLUtils.ts # WebGL 工具(compileShader, createProgram, premultiplyTint, deleteWebGLTexture)
|
|
64
|
+
│ ├── InstructionSet.ts # 指令集(renderableIndex O(1) 查找,dirtyRenderables 增量更新)
|
|
65
|
+
│ ├── MultiTextureBatcher.ts # 多纹理批处理(8张/批,slotMap 纹理槽位分配)
|
|
66
|
+
│ ├── pipes/ # RenderPipe 实现
|
|
67
|
+
│ │ ├── BitmapPipe.ts # 位图渲染指令
|
|
68
|
+
│ │ ├── GraphicsPipe.ts # 矢量图形指令(Canvas光栅化 → 纹理上传)
|
|
69
|
+
│ │ ├── MeshPipe.ts # 网格渲染指令
|
|
70
|
+
│ │ ├── FilterPipe.ts # 滤镜 push/pop 指令(含对象池)
|
|
71
|
+
│ │ ├── MaskPipe.ts # 遮罩 push/pop 指令(含对象池)
|
|
72
|
+
│ │ ├── TextPipe.ts # 文本 WebGL 渲染(offscreen canvas → 纹理上传)
|
|
73
|
+
│ │ └── ParticlePipe.ts # 粒子系统渲染
|
|
74
|
+
│ └── shaders/ # GLSL 着色器
|
|
75
|
+
│ ├── ShaderLib.ts # GLSL ES 1.00 着色器库(WebGL1)
|
|
76
|
+
│ └── ShaderLib2.ts # GLSL ES 3.00 着色器库(WebGL2)
|
|
77
|
+
├── events/ # 事件系统(11 个事件类 + EventPhase + IEventDispatcher 接口)
|
|
78
|
+
├── geom/ # 几何工具(Matrix, Point, Rectangle)
|
|
79
|
+
├── filters/ # 滤镜(Blur, Glow, DropShadow, ColorMatrix, CustomFilter, MultiPassFilter, BloomFilter)
|
|
80
|
+
├── text/ # 文本渲染
|
|
81
|
+
│ ├── TextField.ts # 核心文本字段(含 textFlow 富文本、输入模式、密码模式)
|
|
82
|
+
│ ├── BitmapText.ts # 位图文本
|
|
83
|
+
│ ├── BitmapFont.ts # 位图字体
|
|
84
|
+
│ ├── StageText.ts # INPUT 模式 DOM 输入框
|
|
85
|
+
│ ├── HtmlTextParser.ts # HTML 文本解析
|
|
86
|
+
│ ├── InputController.ts # 输入控制器(选择、光标、键盘处理)
|
|
87
|
+
│ ├── TextMeasurer.ts # 文本测量(measureText, getFontString)
|
|
88
|
+
│ ├── WordWrap.ts # 自动换行(tokenize, splitGraphemes)
|
|
89
|
+
│ ├── enums/ # HorizontalAlign, VerticalAlign, TextFieldType, TextFieldInputType
|
|
90
|
+
│ └── types/ # ITextElement, IWTextElement, ILineElement, IHitTextElement 等类型定义
|
|
91
|
+
├── resource/ # 资源管理
|
|
92
|
+
│ ├── Resource.ts # 资源核心(单例模式,async/await API,加载/缓存/生命周期)
|
|
93
|
+
│ ├── ResourceConfig.ts # 资源配置
|
|
94
|
+
│ ├── ResourceEvent.ts # 资源事件
|
|
95
|
+
│ ├── ResourceItem.ts # 资源项
|
|
96
|
+
│ ├── ResourceLoader.ts # 资源加载器
|
|
97
|
+
│ └── analyzers/ # 资源分析器(AnalyzerBase, ImageAnalyzer, JsonAnalyzer, SheetAnalyzer, SoundAnalyzer, TextAnalyzer)
|
|
98
|
+
├── system/ # 系统能力
|
|
99
|
+
│ └── Capabilities.ts # 运行时 WebGL 扩展/平台能力检测(UA + Client Hints)
|
|
100
|
+
├── net/ # 网络加载(HttpRequest, ImageLoader, HttpMethod, HttpResponseType)
|
|
101
|
+
├── media/ # 媒体(Sound, SoundChannel, Video)
|
|
102
|
+
├── utils/ # 工具类(ByteArray, Timer, Logger, FontManager, DebugLog, Base64Util, NumberUtils, toColorString)
|
|
103
|
+
│ # 注:不提供 HashObject,对象身份比较用 === 或 WeakMap 键控查找
|
|
104
|
+
├── localStorage/ # 本地存储
|
|
105
|
+
└── external/ # 外部接口(ExternalInterface)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 三、渲染管线架构
|
|
111
|
+
|
|
112
|
+
### 3.1 两阶段渲染(借鉴 Pixi.js 8)
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
Phase A — Build(仅 structureDirty 时):
|
|
116
|
+
遍历 DisplayObject 树 → 生成 Instruction(含 transform 快照)→ InstructionSet
|
|
117
|
+
|
|
118
|
+
Phase A' — Update(仅 renderDirty 时):
|
|
119
|
+
遍历 dirtyRenderables → 刷新 transform 快照(O(1) lookup via renderableIndex Map)
|
|
120
|
+
|
|
121
|
+
Phase B — Execute(每帧):
|
|
122
|
+
按指令顺序分发到 Pipe → 无场景图遍历
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 3.2 RenderPipe 体系
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
RenderPipe<T extends DisplayObject>
|
|
129
|
+
├── addToInstructionSet(renderable, set) — 结构变化时调用
|
|
130
|
+
├── updateRenderable(renderable) — 数据变化时调用
|
|
131
|
+
└── destroyRenderable(renderable) — 对象销毁时调用(可选,见下方 GPU 纹理回收说明)
|
|
132
|
+
|
|
133
|
+
实现:
|
|
134
|
+
├── BitmapPipe → BitmapInstruction → drawImage()(含 scale9Grid 九宫格)
|
|
135
|
+
├── GraphicsPipe → GraphicsInstruction → Canvas光栅化 → 纹理上传 → drawTexture()
|
|
136
|
+
├── MeshPipe → MeshInstruction → drawMesh()
|
|
137
|
+
├── TextPipe → TextInstruction → offscreen canvas → 纹理上传 → drawImage()
|
|
138
|
+
├── ParticlePipe → ParticleInstruction → 粒子批处理渲染
|
|
139
|
+
├── FilterPipe → FilterPush/Pop → 离屏FBO → 着色器滤镜
|
|
140
|
+
└── MaskPipe → MaskPush/Pop → stencil/scissor/离屏合成
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**GPU 纹理回收**:`destroyRenderable()` 目前不会被 `WebGLRenderer` 自动调用(`DisplayObject` 没有"永久销毁"信号——`$onRemoveFromStage` 会在临时移出舞台又重新加入时反复触发,接上调用链会让虚拟列表等场景反复重新栅格化/上传纹理)。因此 `GraphicsPipe`、`TextPipe` 各自缓存纹理的清理不依赖调用方显式调用,而是用 `FinalizationRegistry` 在对应的 `Graphics` / `TextField` 被 JS 引擎 GC 时自动 `gl.deleteTexture()`。`destroyRenderable()` 仍保留为可选的立即释放路径(调用时会 `unregister` 对应的 GC 回调,避免重复删除),供未来需要显式销毁时机的场景使用。`BitmapPipe` / `MeshPipe` 的 `destroyRenderable` 是 no-op,因为它们绘制用的纹理归属于 `BitmapData` 的生命周期,不由 Pipe 自身管理。
|
|
144
|
+
|
|
145
|
+
### 3.3 RenderGroup 分层
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
backgroundLayer.isRenderGroup = true;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- 每个 RenderGroup 拥有独立的 InstructionSet
|
|
152
|
+
- 父 set 只包含一条 `renderGroup` 指令,指向子树 set
|
|
153
|
+
- 子树结构变化只触发自身 set 重建,不影响父 set
|
|
154
|
+
- 静态子树的 JS 遍历开销降为零
|
|
155
|
+
- 实现通过 `WeakMap<DisplayObjectContainer, InstructionSet>` 管理 group sets
|
|
156
|
+
- 使用 `WeakRef` 跟踪 group 生命周期,便于 GC
|
|
157
|
+
|
|
158
|
+
### 3.4 多纹理批处理
|
|
159
|
+
|
|
160
|
+
- `MultiTextureBatcher` 管理最多 8 个纹理槽位(WebGL1 最小保证纹理单元数)
|
|
161
|
+
- 顶点格式扩展:增加 `aTextureId` float 属性(stride 从 20B 增至 24B)
|
|
162
|
+
- Fragment shader 使用 if/else 链采样(WebGL1 兼容)
|
|
163
|
+
- mesh、filter、blend 变化自动 flush 回退到单纹理路径
|
|
164
|
+
- `WebGLVertexArrayObject` 维护双缓冲区(单纹理 + 多纹理),按需切换 GPU buffer 大小
|
|
165
|
+
|
|
166
|
+
### 3.5 脏标记系统
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
DisplayObject 脏标记:
|
|
170
|
+
├── cacheDirty — cacheAsBitmap 缓存失效,向上传播
|
|
171
|
+
├── renderDirty — 视觉数据变化(位置/纹理/alpha/tint),向上传播
|
|
172
|
+
└── renderMode — 渲染模式(NONE/FILTER/CLIP/SCROLLRECT)
|
|
173
|
+
|
|
174
|
+
通知机制(单 Player 静态钩子,由 Player 构造函数赋值):
|
|
175
|
+
├── $onStructureChange?: () => void — 结构变化回调
|
|
176
|
+
├── $onRenderableDirty?: (obj: DisplayObject) => void — 渲染脏回调
|
|
177
|
+
├── DisplayObjectContainer.$onContainerStructureChange?: (owner) => void — 容器结构变化回调
|
|
178
|
+
├── $markDirty()
|
|
179
|
+
│ ├── 更新 worldAlpha / worldTint 缓存(O(1) 读取)
|
|
180
|
+
│ ├── 调用 $onRenderableDirty?.(this) → WebGLRenderer.markRenderableDirty()
|
|
181
|
+
│ └── 向上传播 cacheDirty + renderDirty
|
|
182
|
+
├── $updateRenderMode()
|
|
183
|
+
│ └── 调用 $onStructureChange?.() → WebGLRenderer.markStructureDirty()
|
|
184
|
+
└── DisplayObjectContainer.markDirtyInternal()
|
|
185
|
+
└── 调用 $onContainerStructureChange?.(this) → markStructureDirty(owner)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
引擎设计上始终是单 Player:`Player` 的构造函数直接把上述三个静态字段赋值
|
|
189
|
+
为具体的闭包函数(见 `player/Player.ts`),并在 `destroy()` 里清空它们。
|
|
190
|
+
|
|
191
|
+
### 3.6 RenderObjectType 快速路由
|
|
192
|
+
|
|
193
|
+
为避免热路径中的 `instanceof` 检查,DisplayObject 使用 `renderObjectType` 枚举:
|
|
194
|
+
|
|
195
|
+
| 枚举值 | 值 | 对应类型 | 路由 Pipe |
|
|
196
|
+
| ---------- | --- | --------- | ------------ |
|
|
197
|
+
| `NONE` | 0 | 无渲染 | — |
|
|
198
|
+
| `BITMAP` | 1 | Bitmap | BitmapPipe |
|
|
199
|
+
| `MESH` | 2 | Mesh | MeshPipe |
|
|
200
|
+
| `SHAPE` | 3 | Shape | GraphicsPipe |
|
|
201
|
+
| `SPRITE` | 4 | Sprite | GraphicsPipe |
|
|
202
|
+
| `TEXT` | 5 | TextField | TextPipe |
|
|
203
|
+
| `PARTICLE` | 6 | Particle | ParticlePipe |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 四、WebGL 渲染后端
|
|
208
|
+
|
|
209
|
+
### 4.1 WebGL 版本选择
|
|
210
|
+
|
|
211
|
+
`WebGLRenderContext` 在初始化时自动检测并选择最佳后端:
|
|
212
|
+
|
|
213
|
+
- **WebGL 2 优先**:使用 `canvas.getContext('webgl2')`,加载 `ShaderLib2`(GLSL ES 3.00)
|
|
214
|
+
- **WebGL 1 降级**:回退到 `canvas.getContext('webgl')`,加载 `ShaderLib`(GLSL ES 1.00)。
|
|
215
|
+
不使用 `experimental-webgl` 前缀——该别名只有 IE11/早期 Safari/旧版
|
|
216
|
+
Android Chrome 才需要,现代浏览器的标准 `'webgl'` 已经足够。
|
|
217
|
+
- **无独立探测 canvas**:`Player` 直接在应用方传入的 canvas 上依次尝试
|
|
218
|
+
`webgl2` → `webgl`,两者都失败才降级到 Canvas 2D,不额外创建临时 canvas
|
|
219
|
+
探测能力。`checkWebGLSupport()` 函数仍导出,供需要独立探测的调用方使用,
|
|
220
|
+
但 `Player` 自身不调用它。
|
|
221
|
+
- 着色器库通过实例属性动态绑定:`ctx.shaders`、`ctx.blurTierFn`、`ctx.makeBlurH`、`ctx.makeBlurV`
|
|
222
|
+
|
|
223
|
+
### 4.2 渲染流程
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
Player.render()
|
|
227
|
+
→ WebGLRenderer.render(stage, buffer, matrix)
|
|
228
|
+
→ Phase A: _buildInstructions() / _updateDirtyRenderables()
|
|
229
|
+
→ Phase B: _executeInstructions()
|
|
230
|
+
→ Pipe.execute() → WebGLRenderContext.drawImage/drawMesh/drawTexture()
|
|
231
|
+
→ WebGLRenderContext.flush() → _flush()
|
|
232
|
+
→ 上传顶点(bufferSubData)→ 遍历 DrawCmdManager → 分发 draw batch
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 4.3 着色器体系
|
|
236
|
+
|
|
237
|
+
| 着色器 | 用途 | 来源 |
|
|
238
|
+
| ---------------------------------------- | ---------------------------------- | --------- |
|
|
239
|
+
| default_vert + texture_frag | 标准纹理绘制 | ShaderLib |
|
|
240
|
+
| multi_vert + multi_frag | 多纹理批处理(8单元) | ShaderLib |
|
|
241
|
+
| default_vert + colorTransform_frag | ColorMatrixFilter | ShaderLib |
|
|
242
|
+
| default_vert + glow_frag | Glow/DropShadow | ShaderLib |
|
|
243
|
+
| fullscreen_vert + makeBlurH/ VFrag | 水平/垂直模糊(ping-pong 双 pass) | ShaderLib |
|
|
244
|
+
| default_vert + primitive_frag | 纯色矩形(stencil mask) | ShaderLib |
|
|
245
|
+
| fullscreen_vert | 全屏 quad blit(滤镜 pass) | ShaderLib |
|
|
246
|
+
|
|
247
|
+
WebGL2(ShaderLib2)提供等价的 GLSL ES 3.00 版本着色器,使用 `in`/`out` 语法替代 `attribute`/`varying`,
|
|
248
|
+
`texture()` 替代 `texture2D()`,blur 着色器按 4/8/16/32 档生成固定上限循环,仍使用三角权重核。
|
|
249
|
+
|
|
250
|
+
### 4.4 滤镜渲染
|
|
251
|
+
|
|
252
|
+
滤镜通过 FilterPipe 的 push/pop 指令对实现:
|
|
253
|
+
|
|
254
|
+
- **push 阶段**:分配离屏 FBO(含 filter padding 扩展),将后续 draw 重定向到离屏 buffer
|
|
255
|
+
- **pop 阶段**:调用 `compositeFilterResult()` 将离屏结果合成回父 buffer
|
|
256
|
+
|
|
257
|
+
合成流程(`compositeFilterResult`):
|
|
258
|
+
|
|
259
|
+
1. `flush()` 确保离屏输入完整。
|
|
260
|
+
2. `WebGLFilterSystem` 按数组顺序执行;中间 pass 关闭 blend/stencil/scissor,使用独立输出。
|
|
261
|
+
3. 显式恢复父 FBO、viewport 与裁剪状态。
|
|
262
|
+
4. 使用 `drawFramebufferTexture()` 合成最终结果,不重复应用已经写入子树的 alpha/tint。
|
|
263
|
+
5. 立即 flush 后归还中间纹理;失败时清理 pass 资源与待执行命令,下一帧可以恢复。
|
|
264
|
+
|
|
265
|
+
- **ColorMatrixFilter**:无 mask 的叶子、单滤镜、继承分辨率时保留 inline 快速路径;组与组合走离屏。
|
|
266
|
+
- **BlurFilter**:横纵分离,quality 控制 pass 对数;大半径先降采样,使 shader 半径不超过 32 物理像素。
|
|
267
|
+
- **Glow / DropShadow**:保留原有 `glow_frag`;knockout 绑定与“是否保留原图”的 shader 语义对应。
|
|
268
|
+
- **CustomFilter**:显式 GLSL 100/300 源码、按链接类型校验 numeric uniforms、独立采样的辅助 BitmapData 上传。
|
|
269
|
+
- **MultiPassFilter / BloomFilter**:有序无环 pass 图,可读取原图或较早输出,支持各 pass 缩放。
|
|
270
|
+
|
|
271
|
+
`WebGLFilterTargetPool` 缓存中间 GPU 目标,空闲上限 16 个 / 64 MiB;子树捕获缓冲与辅助图片不计入这个限制。
|
|
272
|
+
`WebGLFilterTextures` 管理辅助图像上传和恢复。着色器编译缓存按 GL context 与实际源码隔离。
|
|
273
|
+
|
|
274
|
+
Filter padding 按链累加,并包含子级效果的渲染范围,不修改布局 bounds。
|
|
275
|
+
离屏 pass 统一使用 framebuffer UV(左下为 0,0),每一步保持图像方向。
|
|
276
|
+
DropShadow 传入正常角度弧度;辅助 DOM 图片的自动 UV 矩阵负责纵向转换。
|
|
277
|
+
|
|
278
|
+
具体 API、示例、Canvas 与缓存限制见 [GPU filters](filters.md)。
|
|
279
|
+
|
|
280
|
+
### 4.5 遮罩渲染
|
|
281
|
+
|
|
282
|
+
- scrollRect/maskRect 无旋转且没有外层 scissor:scissor 裁剪(GPU 硬件加速)
|
|
283
|
+
- scrollRect/maskRect 有旋转或嵌套在外层 scissor 内:stencil 裁剪;WebGL 主画布与能力探测共用 2D 上下文配置(不申请 depth,申请 stencil)
|
|
284
|
+
- DisplayObject mask:离屏 buffer + destination-in 合成
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 五、Canvas 2D 渲染后端
|
|
289
|
+
|
|
290
|
+
Canvas 2D 渲染器保持直接遍历模式,作为 WebGL 不可用时的降级方案。
|
|
291
|
+
|
|
292
|
+
- Graphics 离屏 Canvas 缓存(`canvasCacheDirty` 脏标记)
|
|
293
|
+
- CSS filter 快速路径(Blur → `blur()`,DropShadow → `drop-shadow()`)
|
|
294
|
+
- ColorMatrixFilter CPU 像素操作降级
|
|
295
|
+
- cacheAsBitmap 支持(通过 DisplayList)
|
|
296
|
+
- 像素级命中测试(3x3 离屏 buffer,hitTestBuffer 函数)
|
|
297
|
+
- 支持所有 DisplayObject 类型:Bitmap, Shape, Sprite, Mesh, TextField(含样式、对齐、自动换行)
|
|
298
|
+
- TextField 渲染:测量 → 分行 → Canvas 2D fillText/strokeText 绘制
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 六、快速启动 API
|
|
303
|
+
|
|
304
|
+
### `createPlayer(options)`
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { createPlayer, Event } from '@kurot/core';
|
|
308
|
+
|
|
309
|
+
const app = createPlayer({
|
|
310
|
+
canvas: document.getElementById('game-canvas') as HTMLCanvasElement,
|
|
311
|
+
frameRate: 60,
|
|
312
|
+
scaleMode: 'showAll',
|
|
313
|
+
contentWidth: 640,
|
|
314
|
+
contentHeight: 1136,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
app.start(root);
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`createPlayer` 自动完成:
|
|
321
|
+
|
|
322
|
+
1. 调用 `Capabilities._init()` 检测运行环境(OS、isMobile、language)
|
|
323
|
+
2. 首次调用时注入 `RenderTexture.renderer`(Canvas 2D 离屏渲染器)
|
|
324
|
+
3. 创建 Stage、Player(WebGL 优先,Canvas 2D 降级)、TouchHandler、ScreenAdapter
|
|
325
|
+
4. 调用 `setupLifecycle(stage)` 绑定 ENTER_FRAME / RENDER 广播
|
|
326
|
+
|
|
327
|
+
### KurotOptions
|
|
328
|
+
|
|
329
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
330
|
+
| --------------- | ------------------- | --------------- | ------------ |
|
|
331
|
+
| `canvas` | `HTMLCanvasElement` | (必填) | 渲染画布 |
|
|
332
|
+
| `frameRate` | `number` | `60` | 目标帧率 |
|
|
333
|
+
| `scaleMode` | `StageScaleMode` | `'showAll'` | 屏幕适配模式 |
|
|
334
|
+
| `contentWidth` | `number` | `canvas.width` | 逻辑内容宽度 |
|
|
335
|
+
| `contentHeight` | `number` | `canvas.height` | 逻辑内容高度 |
|
|
336
|
+
| `resolution` | `number` | `min(DPR, 2)` | 后备缓冲像素密度 |
|
|
337
|
+
| `orientation` | `OrientationMode` | `'auto'` | 屏幕方向 |
|
|
338
|
+
| `maxTouches` | `number` | `99` | 最大触点数 |
|
|
339
|
+
| `background` | `string` | — | CSS 背景色 |
|
|
340
|
+
|
|
341
|
+
### KurotApp 返回值
|
|
342
|
+
|
|
343
|
+
| 属性 | 类型 | 说明 |
|
|
344
|
+
| --------------- | --------------- | ------------- |
|
|
345
|
+
| `player` | `Player` | 渲染播放器 |
|
|
346
|
+
| `stage` | `Stage` | 舞台根节点 |
|
|
347
|
+
| `touchHandler` | `TouchHandler` | 触摸/鼠标处理 |
|
|
348
|
+
| `screenAdapter` | `ScreenAdapter` | 屏幕适配器 |
|
|
349
|
+
| `start(root?)` | `() => void` | 启动游戏循环 |
|
|
350
|
+
| `stop()` | `() => void` | 停止游戏循环 |
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## 七、测试覆盖
|
|
355
|
+
|
|
356
|
+
`test/` 下按模块组织测试文件,运行 `pnpm --dir packages/core test` 查看当前
|
|
357
|
+
文件数与用例数。
|
|
358
|
+
|
|
359
|
+
| 模块 | 主要覆盖内容 |
|
|
360
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
361
|
+
| events/ | Event, EventDispatcher, EventPropagation, EventMap, TouchEvent, HTTPStatusEvent, ProgressEvent |
|
|
362
|
+
| geom/ | Matrix, Point, Rectangle |
|
|
363
|
+
| utils/ | ByteArray, Base64Util, Logger, DebugLog, NumberUtils, toColorString |
|
|
364
|
+
| display/ | DisplayObject, DisplayObjectContainer, DisplayObjectIntegration, Bitmap, BitmapData, Sprite, Shape, Mesh, Stage, StageText, Graphics, Texture, SpriteSheet, BlendMode, DisplayList |
|
|
365
|
+
| filters/ | Filter (全部滤镜), CustomFilter, filters(滤镜集成), EffectTransform(离屏效果变换正确性) |
|
|
366
|
+
| media/ | Sound, SoundChannel, Video |
|
|
367
|
+
| player/ | InstructionSet, InstructionPool, RenderGroup, TextPipe, MaskPipe, WebGLRendererDirty, WebGLRendererLeaf, WebGLVertexArrayObject, WebGLRenderBuffer, WebGLBlurFramebufferPool, CreatePlayer, TouchHandler |
|
|
368
|
+
| net/ | HttpRequest, ImageLoader |
|
|
369
|
+
| resource/ | Resource(并发 loadGroup 队列化), ResourceLoader(并发/重试/无 analyzer 场景) |
|
|
370
|
+
| examples/benchmark/tests/ | 性能基准运行时与跨引擎对比测试 |
|
|
371
|
+
| text/ | 通过 display 集成测试覆盖 |
|