fix: 离屏画布没有删除导致的内存泄漏

This commit is contained in:
unanmed 2024-11-20 21:11:01 +08:00
parent 003369bed1
commit f1b20450aa
15 changed files with 179 additions and 87 deletions

View File

@ -1,4 +1,5 @@
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import { logger } from '../common/logger';
interface OffscreenCanvasEvent { interface OffscreenCanvasEvent {
/** 当被动触发resize时例如core.domStyle.scale变化、窗口大小变化时触发使用size函数并不会触发 */ /** 当被动触发resize时例如core.domStyle.scale变化、窗口大小变化时触发使用size函数并不会触发 */
@ -26,6 +27,12 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
/** 更新标识符,如果发生变化则说明画布被动清空 */ /** 更新标识符,如果发生变化则说明画布被动清空 */
symbol: number = 0; symbol: number = 0;
private _freezed: boolean = false;
/** 当前画布是否被冻结 */
get freezed() {
return this._freezed;
}
/** /**
* *
* @param alpha * @param alpha
@ -33,6 +40,7 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
*/ */
constructor(alpha: boolean = true, canvas?: HTMLCanvasElement) { constructor(alpha: boolean = true, canvas?: HTMLCanvasElement) {
super(); super();
// console.trace();
this.canvas = canvas ?? document.createElement('canvas'); this.canvas = canvas ?? document.createElement('canvas');
this.ctx = this.canvas.getContext('2d', { alpha })!; this.ctx = this.canvas.getContext('2d', { alpha })!;
@ -46,6 +54,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* *
*/ */
size(width: number, height: number) { size(width: number, height: number) {
if (this._freezed) {
logger.warn(33);
return;
}
let ratio = this.highResolution ? devicePixelRatio : 1; let ratio = this.highResolution ? devicePixelRatio : 1;
const scale = core.domStyle.scale; const scale = core.domStyle.scale;
if (this.autoScale) { if (this.autoScale) {
@ -69,6 +81,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* core.domStyle.scale * core.domStyle.scale
*/ */
withGameScale(auto: boolean) { withGameScale(auto: boolean) {
if (this._freezed) {
logger.warn(33);
return;
}
this.autoScale = auto; this.autoScale = auto;
this.size(this.width, this.height); this.size(this.width, this.height);
} }
@ -77,6 +93,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* *
*/ */
setHD(hd: boolean) { setHD(hd: boolean) {
if (this._freezed) {
logger.warn(33);
return;
}
this.highResolution = hd; this.highResolution = hd;
this.size(this.width, this.height); this.size(this.width, this.height);
} }
@ -85,6 +105,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* 齿 * 齿
*/ */
setAntiAliasing(anti: boolean) { setAntiAliasing(anti: boolean) {
if (this._freezed) {
logger.warn(33);
return;
}
this.antiAliasing = anti; this.antiAliasing = anti;
this.ctx.imageSmoothingEnabled = anti; this.ctx.imageSmoothingEnabled = anti;
} }
@ -93,6 +117,10 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* *
*/ */
clear() { clear() {
if (this._freezed) {
logger.warn(33);
return;
}
this.ctx.save(); this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
@ -103,13 +131,21 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
* *
*/ */
delete() { delete() {
this.canvas.remove();
this.ctx.reset();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
MotaOffscreenCanvas2D.list.delete(this);
}
freeze() {
this._freezed = true;
MotaOffscreenCanvas2D.list.delete(this); MotaOffscreenCanvas2D.list.delete(this);
} }
/** /**
* Canvas2D对象 * Canvas2D对象
* @param canvas MotaOffscreenCanvas2D对象 * @param canvas MotaOffscreenCanvas2D对象
* @returns * @returns
*/ */
static clone(canvas: MotaOffscreenCanvas2D): MotaOffscreenCanvas2D { static clone(canvas: MotaOffscreenCanvas2D): MotaOffscreenCanvas2D {
const newCanvas = new MotaOffscreenCanvas2D(); const newCanvas = new MotaOffscreenCanvas2D();
@ -123,12 +159,13 @@ export class MotaOffscreenCanvas2D extends EventEmitter<OffscreenCanvasEvent> {
canvas.width, canvas.width,
canvas.height canvas.height
); );
newCanvas.freeze();
return newCanvas; return newCanvas;
} }
static refreshAll() { static refreshAll(force: boolean = false) {
this.list.forEach(v => { this.list.forEach(v => {
if (v.autoScale) { if (force || v.autoScale) {
v.size(v.width, v.height); v.size(v.width, v.height);
v.symbol++; v.symbol++;
v.emit('resize'); v.emit('resize');

View File

@ -480,6 +480,7 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
master.size(32, 32); master.size(32, 32);
master.ctx.drawImage(img, 0, 0, 32, 32, 0, 0, 32, 32); master.ctx.drawImage(img, 0, 0, 32, 32, 0, 0, 32, 32);
masterMap[auto] = master.canvas.toDataURL('image/png'); masterMap[auto] = master.canvas.toDataURL('image/png');
master.delete();
// 自动图块的绘制信息 // 自动图块的绘制信息
for (let i = 0; i <= 0b11111111; i++) { for (let i = 0; i <= 0b11111111; i++) {
@ -504,6 +505,7 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
canvas.setAntiAliasing(false); canvas.setAntiAliasing(false);
canvas.withGameScale(false); canvas.withGameScale(false);
canvas.size(32 * frame, 32); canvas.size(32 * frame, 32);
canvas.freeze();
const ctx = canvas.ctx; const ctx = canvas.ctx;
for (let i = 0; i < frame; i++) { for (let i = 0; i < frame; i++) {
const dx = 32 * i; const dx = 32 * i;
@ -525,16 +527,17 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
} }
} }
const judge = new MotaOffscreenCanvas2D();
judge.setHD(false);
judge.setAntiAliasing(false);
judge.withGameScale(false);
judge.size(32, 32);
// 进行父子关系判断 // 进行父子关系判断
for (const [key, img] of Object.entries(core.material.images.autotile)) { for (const [key, img] of Object.entries(core.material.images.autotile)) {
const auto = map[key as AllIdsOf<'autotile'>]; const auto = map[key as AllIdsOf<'autotile'>];
// 只针对3*4的图块进行截取第一行中间的然后判断 // 只针对3*4的图块进行截取第一行中间的然后判断
const judge = new MotaOffscreenCanvas2D(); judge.clear();
judge.setHD(false);
judge.setAntiAliasing(false);
judge.withGameScale(false);
judge.size(32, 32);
judge.ctx.drawImage(img, 32, 0, 32, 32, 0, 0, 32, 32); judge.ctx.drawImage(img, 32, 0, 32, 32, 0, 0, 32, 32);
const data = judge.canvas.toDataURL('image/png'); const data = judge.canvas.toDataURL('image/png');
@ -547,6 +550,7 @@ function splitAutotiles(map: IdToNumber): AutotileCaches {
} }
} }
} }
judge.delete();
return cache as AutotileCaches; return cache as AutotileCaches;
} }

View File

@ -4,28 +4,28 @@ import { logger } from '../common/logger';
import { Transform } from './transform'; import { Transform } from './transform';
import EventEmitter from 'eventemitter3'; import EventEmitter from 'eventemitter3';
export interface CameraTranslate { export interface ICameraTranslate {
readonly type: 'translate'; readonly type: 'translate';
readonly from: Camera; readonly from: Camera;
x: number; x: number;
y: number; y: number;
} }
export interface CameraRotate { export interface ICameraRotate {
readonly type: 'rotate'; readonly type: 'rotate';
readonly from: Camera; readonly from: Camera;
/** 旋转角,单位弧度 */ /** 旋转角,单位弧度 */
angle: number; angle: number;
} }
export interface CameraScale { export interface ICameraScale {
readonly type: 'scale'; readonly type: 'scale';
readonly from: Camera; readonly from: Camera;
x: number; x: number;
y: number; y: number;
} }
type CameraOperation = CameraTranslate | CameraScale | CameraRotate; type CameraOperation = ICameraTranslate | ICameraScale | ICameraRotate;
interface CameraEvent { interface CameraEvent {
destroy: []; destroy: [];
@ -145,8 +145,8 @@ export class Camera extends EventEmitter<CameraEvent> {
* *
* @returns * @returns
*/ */
addTranslate(): CameraTranslate { addTranslate(): ICameraTranslate {
const item: CameraTranslate = { const item: ICameraTranslate = {
type: 'translate', type: 'translate',
x: 0, x: 0,
y: 0, y: 0,
@ -160,8 +160,8 @@ export class Camera extends EventEmitter<CameraEvent> {
* *
* @returns * @returns
*/ */
addRotate(): CameraRotate { addRotate(): ICameraRotate {
const item: CameraRotate = { const item: ICameraRotate = {
type: 'rotate', type: 'rotate',
angle: 0, angle: 0,
from: this from: this
@ -174,8 +174,8 @@ export class Camera extends EventEmitter<CameraEvent> {
* *
* @returns * @returns
*/ */
addScale(): CameraScale { addScale(): ICameraScale {
const item: CameraScale = { const item: ICameraScale = {
type: 'scale', type: 'scale',
x: 1, x: 1,
y: 1, y: 1,
@ -213,7 +213,7 @@ export class Camera extends EventEmitter<CameraEvent> {
* @param time * @param time
*/ */
applyTranslateAnimation( applyTranslateAnimation(
operation: CameraTranslate, operation: ICameraTranslate,
animate: Animation, animate: Animation,
time: number time: number
) { ) {
@ -237,7 +237,7 @@ export class Camera extends EventEmitter<CameraEvent> {
* @param time * @param time
*/ */
applyRotateAnimation( applyRotateAnimation(
operation: CameraRotate, operation: ICameraRotate,
animate: Animation, animate: Animation,
time: number time: number
) { ) {
@ -260,7 +260,7 @@ export class Camera extends EventEmitter<CameraEvent> {
* @param time * @param time
*/ */
applyScaleAnimation( applyScaleAnimation(
operation: CameraScale, operation: ICameraScale,
animate: Animation, animate: Animation,
time: number time: number
) { ) {
@ -284,7 +284,7 @@ export class Camera extends EventEmitter<CameraEvent> {
* @param time * @param time
*/ */
applyTranslateTransition( applyTranslateTransition(
operation: CameraTranslate, operation: ICameraTranslate,
animate: Transition, animate: Transition,
time: number time: number
) { ) {
@ -308,7 +308,7 @@ export class Camera extends EventEmitter<CameraEvent> {
* @param time * @param time
*/ */
applyRotateTransition( applyRotateTransition(
operation: CameraRotate, operation: ICameraRotate,
animate: Transition, animate: Transition,
time: number time: number
) { ) {
@ -331,7 +331,7 @@ export class Camera extends EventEmitter<CameraEvent> {
* @param time * @param time
*/ */
applyScaleTransition( applyScaleTransition(
operation: CameraScale, operation: ICameraScale,
animate: Transition, animate: Transition,
time: number time: number
) { ) {
@ -514,7 +514,7 @@ export class CameraAnimation extends EventEmitter<CameraAnimationEvent> {
* @param timing * @param timing
*/ */
translate( translate(
operation: CameraTranslate, operation: ICameraTranslate,
x: number, x: number,
y: number, y: number,
time: number, time: number,
@ -542,7 +542,7 @@ export class CameraAnimation extends EventEmitter<CameraAnimationEvent> {
* @param timing * @param timing
*/ */
rotate( rotate(
operation: CameraRotate, operation: ICameraRotate,
angle: number, angle: number,
time: number, time: number,
start: number, start: number,
@ -568,7 +568,7 @@ export class CameraAnimation extends EventEmitter<CameraAnimationEvent> {
* @param timing * @param timing
*/ */
scale( scale(
operation: CameraScale, operation: ICameraScale,
scale: number, scale: number,
time: number, time: number,
start: number, start: number,

View File

@ -385,6 +385,7 @@ export abstract class GL2 extends RenderItem {
destroy(): void { destroy(): void {
this.programs.forEach(v => v.destroy()); this.programs.forEach(v => v.destroy());
this.canvas.remove();
super.destroy(); super.destroy();
} }

View File

@ -462,6 +462,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
this.remove(); this.remove();
this.emit('destroy'); this.emit('destroy');
this.removeAllListeners(); this.removeAllListeners();
this.cache.delete();
RenderItem.itemMap.delete(this._id); RenderItem.itemMap.delete(this._id);
} }
} }

View File

@ -1,5 +1,6 @@
import { EventEmitter } from '@/core/common/eventEmitter'; import { EventEmitter } from '@/core/common/eventEmitter';
import { logger } from '@/core/common/logger'; import { logger } from '@/core/common/logger';
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
interface BlockCacherEvent { interface BlockCacherEvent {
split: () => void; split: () => void;
@ -16,13 +17,22 @@ interface BlockData {
restHeight: number; restHeight: number;
} }
export interface IBlockCacheable {
/**
*
*/
destroy(): void;
}
/** /**
* *
* 13x13划分缓存13x13的缓存分块 * 13x13划分缓存13x13的缓存分块
* 便`xx -> yy` * 便`xx -> yy`
* xx说明传入的数据是元素还是分块的数据yy表示其返回值或转换为的值 * xx说明传入的数据是元素还是分块的数据yy表示其返回值或转换为的值
*/ */
export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> { export class BlockCacher<
T extends IBlockCacheable
> extends EventEmitter<BlockCacherEvent> {
/** 区域宽度 */ /** 区域宽度 */
width: number; width: number;
/** 区域高度 */ /** 区域高度 */
@ -118,7 +128,12 @@ export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> {
const depth = this.cacheDepth; const depth = this.cacheDepth;
for (let i = 0; i < depth; i++) { for (let i = 0; i < depth; i++) {
if (deep & (1 << i)) { if (deep & (1 << i)) {
this.cache.delete(index * this.cacheDepth + i); const nowIndex = index * this.cacheDepth + i;
const item = this.cache.get(nowIndex);
if (item) {
item.destroy();
this.cache.delete(nowIndex);
}
} }
} }
} }
@ -126,14 +141,19 @@ export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> {
/** /**
* {@link clearCache} ->void * {@link clearCache} ->void
*/ */
clearCacheByIndex(index: number) { clearCacheByIndex(index: number, func: (item: T) => void) {
this.cache.delete(index); const item = this.cache.get(index);
if (item) {
item.destroy();
this.cache.delete(index);
}
} }
/** /**
* *
*/ */
clearAllCache() { clearAllCache() {
this.cache.forEach(v => v.destroy());
this.cache.clear(); this.cache.clear();
} }
@ -272,4 +292,24 @@ export class BlockCacher<T> extends EventEmitter<BlockCacherEvent> {
(y + 1) * this.blockSize (y + 1) * this.blockSize
]; ];
} }
/**
*
*/
destroy() {
this.clearAllCache();
}
}
export interface ICanvasCacheItem extends IBlockCacheable {
readonly canvas: MotaOffscreenCanvas2D;
symbol: number;
}
export class CanvasCacheItem implements ICanvasCacheItem {
constructor(public canvas: MotaOffscreenCanvas2D, public symbol: number) {}
destroy(): void {
this.canvas.delete();
}
} }

View File

@ -7,7 +7,12 @@ import {
LayerGroup LayerGroup
} from './layer'; } from './layer';
import { ESpriteEvent, Sprite } from '../sprite'; import { ESpriteEvent, Sprite } from '../sprite';
import { BlockCacher } from './block'; import {
BlockCacher,
CanvasCacheItem,
IBlockCacheable,
ICanvasCacheItem
} from './block';
import type { import type {
DamageEnemy, DamageEnemy,
EnemyCollection, EnemyCollection,
@ -16,7 +21,7 @@ import type {
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { getDamageColor } from '@/plugin/utils'; import { getDamageColor } from '@/plugin/utils';
import { transformCanvas } from '../item'; import { RenderItem, transformCanvas } from '../item';
import EventEmitter from 'eventemitter3'; import EventEmitter from 'eventemitter3';
import { Transform } from '../transform'; import { Transform } from '../transform';
@ -120,11 +125,6 @@ export interface DamageRenderable {
strokeWidth?: number; strokeWidth?: number;
} }
interface DamageCache {
canvas: MotaOffscreenCanvas2D;
symbol: number;
}
interface EDamageEvent extends ESpriteEvent { interface EDamageEvent extends ESpriteEvent {
setMapSize: [width: number, height: number]; setMapSize: [width: number, height: number];
beforeDamageRender: [need: Set<number>, transform: Transform]; beforeDamageRender: [need: Set<number>, transform: Transform];
@ -132,11 +132,11 @@ interface EDamageEvent extends ESpriteEvent {
dirtyUpdate: [block: number]; dirtyUpdate: [block: number];
} }
export class Damage extends Sprite<EDamageEvent> { export class Damage extends RenderItem<EDamageEvent> {
mapWidth: number = 0; mapWidth: number = 0;
mapHeight: number = 0; mapHeight: number = 0;
block: BlockCacher<DamageCache>; block: BlockCacher<ICanvasCacheItem>;
/** 键表示分块索引,值表示在这个分块上的渲染信息(当然实际渲染位置可以不在这个分块上) */ /** 键表示分块索引,值表示在这个分块上的渲染信息(当然实际渲染位置可以不在这个分块上) */
renderable: Map<number, Set<DamageRenderable>> = new Map(); renderable: Map<number, Set<DamageRenderable>> = new Map();
@ -147,8 +147,6 @@ export class Damage extends Sprite<EDamageEvent> {
/** 单元格大小 */ /** 单元格大小 */
cellSize: number = 32; cellSize: number = 32;
/** 伤害渲染层 */
damageMap: MotaOffscreenCanvas2D = new MotaOffscreenCanvas2D();
/** 默认伤害字体 */ /** 默认伤害字体 */
font: string = '300 9px Verdana'; font: string = '300 9px Verdana';
/** 默认描边样式,当伤害文字不存在描边属性时会使用此属性 */ /** 默认描边样式,当伤害文字不存在描边属性时会使用此属性 */
@ -165,18 +163,15 @@ export class Damage extends Sprite<EDamageEvent> {
this.block = new BlockCacher(0, 0, core._WIDTH_, 1); this.block = new BlockCacher(0, 0, core._WIDTH_, 1);
this.type = 'absolute'; this.type = 'absolute';
this.size(core._PX_, core._PY_); this.size(core._PX_, core._PY_);
this.damageMap.withGameScale(true); this.setHD(true);
this.damageMap.setHD(true); this.setAntiAliasing(true);
this.damageMap.setAntiAliasing(true); }
this.damageMap.size(core._PX_, core._PY_);
this.setRenderFn((canvas, transform) => { protected render(
const { ctx } = canvas; canvas: MotaOffscreenCanvas2D,
const { width, height } = canvas; transform: Transform
ctx.imageSmoothingEnabled = false; ): void {
this.renderDamage(transform); this.renderDamage(canvas, transform);
ctx.drawImage(this.damageMap.canvas, 0, 0, width, height);
});
} }
private onExtract = () => { private onExtract = () => {
@ -468,12 +463,10 @@ export class Damage extends Sprite<EDamageEvent> {
* *
* @param transform * @param transform
*/ */
renderDamage(transform: Transform) { renderDamage(canvas: MotaOffscreenCanvas2D, transform: Transform) {
// console.time('damage'); // console.time('damage');
const { ctx } = this.damageMap; const { ctx } = canvas;
ctx.save(); transformCanvas(canvas, transform);
this.damageMap.clear();
transformCanvas(this.damageMap, transform);
// console.trace(); // console.trace();
const render = this.calNeedRender(transform); const render = this.calNeedRender(transform);
@ -529,10 +522,7 @@ export class Damage extends Sprite<EDamageEvent> {
}); });
ctx.drawImage(temp.canvas, px, py, size, size); ctx.drawImage(temp.canvas, px, py, size, size);
block.cache.set(v, { block.cache.set(v, new CanvasCacheItem(temp, temp.symbol));
canvas: temp,
symbol: temp.symbol
});
}); });
ctx.restore(); ctx.restore();
// console.timeEnd('damage'); // console.timeEnd('damage');
@ -540,6 +530,7 @@ export class Damage extends Sprite<EDamageEvent> {
destroy(): void { destroy(): void {
super.destroy(); super.destroy();
this.block.destroy();
this.enemy?.off('extract', this.onExtract); this.enemy?.off('extract', this.onExtract);
} }
} }

View File

@ -5,7 +5,12 @@ import { TimingFn } from 'mutate-animate';
import { IAnimateFrame, renderEmits, RenderItem } from '../item'; import { IAnimateFrame, renderEmits, RenderItem } from '../item';
import { logger } from '@/core/common/logger'; import { logger } from '@/core/common/logger';
import { RenderableData, texture } from '../cache'; import { RenderableData, texture } from '../cache';
import { BlockCacher } from './block'; import {
BlockCacher,
CanvasCacheItem,
IBlockCacheable,
ICanvasCacheItem
} from './block';
import { Transform } from '../transform'; import { Transform } from '../transform';
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor'; import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
import { RenderAdapter } from '../adapter'; import { RenderAdapter } from '../adapter';
@ -388,7 +393,7 @@ export interface ILayerRenderExtends {
* @param layer Layer实例 * @param layer Layer实例
* @param images * @param images
*/ */
onBackgroundGenerated?(layer: Layer, images: HTMLCanvasElement[]): void; onBackgroundGenerated?(layer: Layer, images: MotaOffscreenCanvas2D[]): void;
/** /**
* {@link Layer.putRenderData} * {@link Layer.putRenderData}
@ -504,11 +509,6 @@ export interface ILayerRenderExtends {
onDestroy?(layer: Layer): void; onDestroy?(layer: Layer): void;
} }
interface LayerCacheItem {
symbol: number;
canvas: MotaOffscreenCanvas2D;
}
export interface LayerMovingRenderable extends RenderableData { export interface LayerMovingRenderable extends RenderableData {
zIndex: number; zIndex: number;
x: number; x: number;
@ -550,12 +550,17 @@ export class Layer extends Container {
/** 背景图块 */ /** 背景图块 */
background: AllNumbers = 0; background: AllNumbers = 0;
/** 背景图块画布 */ /** 背景图块画布 */
backImage: HTMLCanvasElement[] = []; backImage: MotaOffscreenCanvas2D[] = [];
/** 背景贴图 */ /** 背景贴图 */
floorImage: FloorAnimate[] = []; floorImage: FloorAnimate[] = [];
/** 分块信息 */ /** 分块信息 */
block: BlockCacher<LayerCacheItem> = new BlockCacher(0, 0, core._WIDTH_, 4); block: BlockCacher<ICanvasCacheItem> = new BlockCacher(
0,
0,
core._WIDTH_,
4
);
/** 大怪物渲染信息 */ /** 大怪物渲染信息 */
bigImages: Map<number, LayerMovingRenderable> = new Map(); bigImages: Map<number, LayerMovingRenderable> = new Map();
@ -717,13 +722,17 @@ export class Layer extends Container {
const num = this.background; const num = this.background;
const data = texture.getRenderable(num); const data = texture.getRenderable(num);
this.backImage.forEach(v => v.delete());
this.backImage = []; this.backImage = [];
if (!data) return; if (!data) return;
const frame = data.frame; const frame = data.frame;
const temp = new MotaOffscreenCanvas2D();
temp.setHD(false);
temp.setAntiAliasing(false);
temp.withGameScale(false);
for (let i = 0; i < frame; i++) { for (let i = 0; i < frame; i++) {
const canvas = new MotaOffscreenCanvas2D(); const canvas = new MotaOffscreenCanvas2D();
const temp = new MotaOffscreenCanvas2D();
const ctx = canvas.ctx; const ctx = canvas.ctx;
const tempCtx = temp.ctx; const tempCtx = temp.ctx;
const [sx, sy, w, h] = data.render[i]; const [sx, sy, w, h] = data.render[i];
@ -731,9 +740,6 @@ export class Layer extends Container {
canvas.setAntiAliasing(false); canvas.setAntiAliasing(false);
canvas.withGameScale(false); canvas.withGameScale(false);
canvas.size(core._PX_, core._PY_); canvas.size(core._PX_, core._PY_);
temp.setHD(false);
temp.setAntiAliasing(false);
temp.withGameScale(false);
temp.size(w, h); temp.size(w, h);
const img = data.autotile ? data.image[0b11111111] : data.image; const img = data.autotile ? data.image[0b11111111] : data.image;
@ -743,8 +749,9 @@ export class Layer extends Container {
ctx.fillStyle = pattern; ctx.fillStyle = pattern;
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
this.backImage.push(canvas.canvas); this.backImage.push(canvas);
} }
temp.delete();
for (const ex of this.extend.values()) { for (const ex of this.extend.values()) {
ex.onBackgroundGenerated?.(this, this.backImage); ex.onBackgroundGenerated?.(this, this.backImage);
@ -1078,7 +1085,7 @@ export class Layer extends Container {
const sx = x * blockSize; const sx = x * blockSize;
const sy = y * blockSize; const sy = y * blockSize;
ctx.drawImage( ctx.drawImage(
img, img.canvas,
sx * cell, sx * cell,
sy * cell, sy * cell,
blockSize * cell, blockSize * cell,
@ -1180,10 +1187,7 @@ export class Layer extends Container {
blockSize * cell, blockSize * cell,
blockSize * cell blockSize * cell
); );
this.block.cache.set(index, { this.block.cache.set(index, new CanvasCacheItem(temp, temp.symbol));
canvas: temp,
symbol: temp.symbol
});
}); });
} }
@ -1379,6 +1383,12 @@ export class Layer extends Container {
ex.onDestroy?.(this); ex.onDestroy?.(this);
} }
super.destroy(); super.destroy();
this.staticMap.delete();
this.movingMap.delete();
this.backMap.delete();
this.backImage.forEach(v => v.delete());
this.block.destroy();
this.main.destroy();
layerAdapter.remove(this); layerAdapter.remove(this);
} }

View File

@ -78,7 +78,9 @@ export class MotaRenderer extends Container {
} }
destroy() { destroy() {
super.destroy();
MotaRenderer.list.delete(this.id); MotaRenderer.list.delete(this.id);
this.target.delete();
} }
static get(id: string) { static get(id: string) {

View File

@ -17,7 +17,7 @@ export function enableViewport() {
/** /**
* *
*/ */
export function AddTiming(timing1: TimingFn, timing2: TimingFn): TimingFn { export function addTiming(timing1: TimingFn, timing2: TimingFn): TimingFn {
return (p: number) => timing1(p) + timing2(p); return (p: number) => timing1(p) + timing2(p);
} }

View File

@ -57,6 +57,7 @@
"30": "Cannot use indices named $1 since no definition for it. Please define it in advance.", "30": "Cannot use indices named $1 since no definition for it. Please define it in advance.",
"31": "Cannot use indices since the indices instance is not belong to the program.", "31": "Cannot use indices since the indices instance is not belong to the program.",
"32": "Sub-image exceeds texture dimensions, auto adjusting size.", "32": "Sub-image exceeds texture dimensions, auto adjusting size.",
"33": "Cannot modify MotaOffscreenCanvas2D that is freezed.",
"1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.", "1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency.",
"1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance." "1101": "Cannot add new effect to point effect instance, for there's no more reserve space for it. Please increase the max count of the instance."
} }

View File

@ -2,9 +2,7 @@ import { Shader, ShaderProgram } from '@/core/render/shader';
import { IWeather, WeatherController } from './weather'; import { IWeather, WeatherController } from './weather';
import { MotaRenderer } from '@/core/render/render'; import { MotaRenderer } from '@/core/render/render';
import { Container } from '@/core/render/container'; import { Container } from '@/core/render/container';
import { GL2Program, IShaderUniform, UniformType } from '@/core/render/gl2'; import { IShaderUniform, UniformType } from '@/core/render/gl2';
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { Transform } from '@/core/render/transform';
const rainVs = /* glsl */ ` const rainVs = /* glsl */ `
in vec2 a_rainVertex; in vec2 a_rainVertex;

View File

@ -163,8 +163,10 @@ export class ArrowProjectile extends Projectile<TowerBoss> {
this.easing = void 0; this.easing = void 0;
this.dangerEasing = void 0; this.dangerEasing = void 0;
this.horizontal?.clear(); this.horizontal?.clear();
this.horizontal?.delete();
this.horizontal = null; this.horizontal = null;
this.vertical?.clear(); this.vertical?.clear();
this.vertical?.delete();
this.vertical = null; this.vertical = null;
} }
@ -465,6 +467,7 @@ export class ThunderProjectile extends Projectile<TowerBoss> {
static end() { static end() {
this.cache?.clear(); this.cache?.clear();
this.cache?.delete();
this.cache = null; this.cache = null;
} }
@ -648,8 +651,10 @@ export class ThunderBallProjectile extends Projectile<TowerBoss> {
static end() { static end() {
this.dangerEasing = void 0; this.dangerEasing = void 0;
this.horizontal?.clear(); this.horizontal?.clear();
this.horizontal?.delete();
this.horizontal = null; this.horizontal = null;
this.vertical?.clear(); this.vertical?.clear();
this.vertical?.delete();
this.vertical = null; this.vertical = null;
} }

View File

@ -337,6 +337,8 @@ export class Chase extends EventEmitter<ChaseEvent> {
Chase.shader.remove(); Chase.shader.remove();
this.emit('end', success); this.emit('end', success);
this.removeAllListeners(); this.removeAllListeners();
this.pathMap.forEach(v => v.delete());
this.pathMap.clear();
} }
} }

View File

@ -1,7 +1,7 @@
import { Animation, hyper, linear, power, sleep } from 'mutate-animate'; import { Animation, hyper, linear, power, sleep } from 'mutate-animate';
import { Chase, ChaseData, IChaseController } from './chase'; import { Chase, ChaseData, IChaseController } from './chase';
import { completeAchievement } from '../ui/achievement'; import { completeAchievement } from '../ui/achievement';
import { Camera, CameraAnimation, CameraScale } from '@/core/render/camera'; import { Camera, CameraAnimation, ICameraScale } from '@/core/render/camera';
import { LayerGroup } from '@/core/render/preset/layer'; import { LayerGroup } from '@/core/render/preset/layer';
import { MotaRenderer } from '@/core/render/render'; import { MotaRenderer } from '@/core/render/render';
import { Sprite } from '@/core/render/sprite'; import { Sprite } from '@/core/render/sprite';
@ -272,7 +272,7 @@ function playAudio(from: number, chase: Chase) {
function processScale( function processScale(
chase: Chase, chase: Chase,
ani: Animation, ani: Animation,
scale: CameraScale, scale: ICameraScale,
camera: Camera camera: Camera
) { ) {
chase.onceLoc(35, 3, 'MT15', () => { chase.onceLoc(35, 3, 'MT15', () => {