mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 15:36:18 +00:00
🆙 Memory usage fixes
This commit is contained in:
@@ -1,9 +1,61 @@
|
|||||||
import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetAssetManager, GetAvatarRenderManager, IFigurePart, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroRectangle, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
|
import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetAssetManager, GetAvatarRenderManager, IFigurePart, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroRectangle, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
|
||||||
import { IAvatarEditorCategoryPartItem } from './IAvatarEditorCategoryPartItem';
|
import { IAvatarEditorCategoryPartItem } from './IAvatarEditorCategoryPartItem';
|
||||||
|
|
||||||
|
const MAX_CACHE_BYTES = 200 * 1024 * 1024;
|
||||||
|
|
||||||
|
class LRUImageCache
|
||||||
|
{
|
||||||
|
private _cache: Map<string, string> = new Map();
|
||||||
|
private _currentBytes: number = 0;
|
||||||
|
|
||||||
|
public get(key: string): string | undefined
|
||||||
|
{
|
||||||
|
const value = this._cache.get(key);
|
||||||
|
|
||||||
|
if(value !== undefined)
|
||||||
|
{
|
||||||
|
this._cache.delete(key);
|
||||||
|
this._cache.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(key: string, value: string): void
|
||||||
|
{
|
||||||
|
if(this._cache.has(key))
|
||||||
|
{
|
||||||
|
const old = this._cache.get(key);
|
||||||
|
|
||||||
|
this._currentBytes -= (key.length + old.length) * 2;
|
||||||
|
this._cache.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const entryBytes = (key.length + value.length) * 2;
|
||||||
|
|
||||||
|
while(this._currentBytes + entryBytes > MAX_CACHE_BYTES && this._cache.size > 0)
|
||||||
|
{
|
||||||
|
const firstKey = this._cache.keys().next().value;
|
||||||
|
const firstValue = this._cache.get(firstKey);
|
||||||
|
|
||||||
|
this._currentBytes -= (firstKey.length + firstValue.length) * 2;
|
||||||
|
this._cache.delete(firstKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cache.set(key, value);
|
||||||
|
this._currentBytes += entryBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear(): void
|
||||||
|
{
|
||||||
|
this._cache.clear();
|
||||||
|
this._currentBytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class AvatarEditorThumbnailsHelper
|
export class AvatarEditorThumbnailsHelper
|
||||||
{
|
{
|
||||||
private static THUMBNAIL_CACHE: Map<string, string> = new Map();
|
private static THUMBNAIL_CACHE: LRUImageCache = new LRUImageCache();
|
||||||
private static THUMB_DIRECTIONS: number[] = [ 2, 6, 0, 4, 3, 1 ];
|
private static THUMB_DIRECTIONS: number[] = [ 2, 6, 0, 4, 3, 1 ];
|
||||||
private static ALPHA_FILTER: NitroAlphaFilter = new NitroAlphaFilter({ alpha: 0.2 });
|
private static ALPHA_FILTER: NitroAlphaFilter = new NitroAlphaFilter({ alpha: 0.2 });
|
||||||
private static DRAW_ORDER: string[] = [
|
private static DRAW_ORDER: string[] = [
|
||||||
@@ -37,9 +89,18 @@ export class AvatarEditorThumbnailsHelper
|
|||||||
'ptr',
|
'ptr',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static getThumbnailKey(setType: string, part: IAvatarEditorCategoryPartItem): string
|
private static getThumbnailKey(setType: string, part: IAvatarEditorCategoryPartItem, partColors?: IPartColor[], isDisabled?: boolean): string
|
||||||
{
|
{
|
||||||
return `${ setType }-${ part.partSet.id }`;
|
let key = `${ setType }-${ part.partSet.id }`;
|
||||||
|
|
||||||
|
if(partColors?.length)
|
||||||
|
{
|
||||||
|
key += '-' + partColors.map(c => c?.rgb?.toString(16) ?? '0').join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isDisabled) key += '-d';
|
||||||
|
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static clearCache(): void
|
public static clearCache(): void
|
||||||
@@ -51,7 +112,7 @@ export class AvatarEditorThumbnailsHelper
|
|||||||
{
|
{
|
||||||
if(!setType || !setType.length || !part || !part.partSet || !part.partSet.parts || !part.partSet.parts.length) return null;
|
if(!setType || !setType.length || !part || !part.partSet || !part.partSet.parts || !part.partSet.parts.length) return null;
|
||||||
|
|
||||||
const thumbnailKey = this.getThumbnailKey(setType, part);
|
const thumbnailKey = this.getThumbnailKey(setType, part, useColors ? partColors : null, isDisabled);
|
||||||
const cached = this.THUMBNAIL_CACHE.get(thumbnailKey);
|
const cached = this.THUMBNAIL_CACHE.get(thumbnailKey);
|
||||||
|
|
||||||
if(cached) return cached;
|
if(cached) return cached;
|
||||||
@@ -145,7 +206,7 @@ export class AvatarEditorThumbnailsHelper
|
|||||||
{
|
{
|
||||||
if(!figureString || !figureString.length) return null;
|
if(!figureString || !figureString.length) return null;
|
||||||
|
|
||||||
const thumbnailKey = figureString;
|
const thumbnailKey = figureString + (isDisabled ? '-d' : '');
|
||||||
const cached = this.THUMBNAIL_CACHE.get(thumbnailKey);
|
const cached = this.THUMBNAIL_CACHE.get(thumbnailKey);
|
||||||
|
|
||||||
if(cached) return cached;
|
if(cached) return cached;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { AvatarScaleType, AvatarSetType, GetAvatarRenderManager } from '@nitrots
|
|||||||
import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
|
import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Base, BaseProps } from '../Base';
|
import { Base, BaseProps } from '../Base';
|
||||||
|
|
||||||
|
const AVATAR_CACHE_MAX_SIZE = 200;
|
||||||
const AVATAR_IMAGE_CACHE: Map<string, string> = new Map();
|
const AVATAR_IMAGE_CACHE: Map<string, string> = new Map();
|
||||||
|
|
||||||
export interface LayoutAvatarImageViewProps extends BaseProps<HTMLDivElement>
|
export interface LayoutAvatarImageViewProps extends BaseProps<HTMLDivElement>
|
||||||
@@ -75,7 +76,16 @@ export const LayoutAvatarImageView: FC<LayoutAvatarImageViewProps> = props =>
|
|||||||
|
|
||||||
if(imageUrl && !isDisposed.current)
|
if(imageUrl && !isDisposed.current)
|
||||||
{
|
{
|
||||||
if(!avatarImage.isPlaceholder()) AVATAR_IMAGE_CACHE.set(figureKey, imageUrl);
|
if(!avatarImage.isPlaceholder())
|
||||||
|
{
|
||||||
|
if(AVATAR_IMAGE_CACHE.size >= AVATAR_CACHE_MAX_SIZE)
|
||||||
|
{
|
||||||
|
const firstKey = AVATAR_IMAGE_CACHE.keys().next().value;
|
||||||
|
AVATAR_IMAGE_CACHE.delete(firstKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
AVATAR_IMAGE_CACHE.set(figureKey, imageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
setAvatarUrl(imageUrl);
|
setAvatarUrl(imageUrl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { GetRoomEngine, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
|
import { GetRoomEngine, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
|
||||||
import { CSSProperties, FC, useEffect, useMemo, useState } from 'react';
|
import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Base, BaseProps } from '../Base';
|
import { Base, BaseProps } from '../Base';
|
||||||
|
|
||||||
interface LayoutRoomObjectImageViewProps extends BaseProps<HTMLDivElement>
|
interface LayoutRoomObjectImageViewProps extends BaseProps<HTMLDivElement>
|
||||||
@@ -15,6 +15,14 @@ export const LayoutRoomObjectImageView: FC<LayoutRoomObjectImageViewProps> = pro
|
|||||||
{
|
{
|
||||||
const { roomId = -1, objectId = 1, category = -1, direction = 2, scale = 1, style = {}, ...rest } = props;
|
const { roomId = -1, objectId = 1, category = -1, direction = 2, scale = 1, style = {}, ...rest } = props;
|
||||||
const [ imageElement, setImageElement ] = useState<HTMLImageElement>(null);
|
const [ imageElement, setImageElement ] = useState<HTMLImageElement>(null);
|
||||||
|
const isMounted = useRef(true);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
isMounted.current = true;
|
||||||
|
|
||||||
|
return () => { isMounted.current = false; };
|
||||||
|
}, []);
|
||||||
|
|
||||||
const getStyle = useMemo(() =>
|
const getStyle = useMemo(() =>
|
||||||
{
|
{
|
||||||
@@ -42,15 +50,23 @@ export const LayoutRoomObjectImageView: FC<LayoutRoomObjectImageViewProps> = pro
|
|||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
const imageResult = GetRoomEngine().getRoomObjectImage(roomId, objectId, category, new Vector3d(direction * 45), 64, {
|
const imageResult = GetRoomEngine().getRoomObjectImage(roomId, objectId, category, new Vector3d(direction * 45), 64, {
|
||||||
imageReady: async (id, texture, image) => setImageElement(await TextureUtils.generateImage(texture)),
|
imageReady: async (id, texture, image) =>
|
||||||
|
{
|
||||||
|
const img = await TextureUtils.generateImage(texture);
|
||||||
|
|
||||||
|
if(img && isMounted.current) setImageElement(img);
|
||||||
|
},
|
||||||
imageFailed: null
|
imageFailed: null
|
||||||
});
|
});
|
||||||
|
|
||||||
// needs (roomObjectImage.data.width > 140) || (roomObjectImage.data.height > 200) scale 1
|
|
||||||
|
|
||||||
if(!imageResult) return;
|
if(!imageResult) return;
|
||||||
|
|
||||||
(async () => setImageElement(await TextureUtils.generateImage(imageResult.data)))();
|
(async () =>
|
||||||
|
{
|
||||||
|
const img = await TextureUtils.generateImage(imageResult.data);
|
||||||
|
|
||||||
|
if(img && isMounted.current) setImageElement(img);
|
||||||
|
})();
|
||||||
}, [ roomId, objectId, category, direction, scale ]);
|
}, [ roomId, objectId, category, direction, scale ]);
|
||||||
|
|
||||||
if(!imageElement) return null;
|
if(!imageElement) return null;
|
||||||
|
|||||||
@@ -38,10 +38,11 @@ export const LayoutRoomPreviewerView: FC<{
|
|||||||
clear: true
|
clear: true
|
||||||
});
|
});
|
||||||
|
|
||||||
let canvas = GetRenderer().texture.generateCanvas(texture);
|
const canvas = GetRenderer().texture.generateCanvas(texture);
|
||||||
const base64 = canvas.toDataURL('image/png');
|
const base64 = canvas.toDataURL('image/png');
|
||||||
|
|
||||||
canvas = null;
|
canvas.width = 0;
|
||||||
|
canvas.height = 0;
|
||||||
|
|
||||||
elementRef.current.style.backgroundImage = `url(${ base64 })`;
|
elementRef.current.style.backgroundImage = `url(${ base64 })`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { AvatarEditorFigureCategory, AvatarFigureContainer, AvatarFigurePartType, FigureSetIdsMessageEvent, GetAvatarRenderManager, GetSessionDataManager, GetWardrobeMessageComposer, IAvatarFigureContainer, IFigurePartSet, IPalette, IPartColor, SetType, UserWardrobePageEvent } from '@nitrots/nitro-renderer';
|
import { AvatarEditorFigureCategory, AvatarFigureContainer, AvatarFigurePartType, FigureSetIdsMessageEvent, GetAvatarRenderManager, GetSessionDataManager, GetWardrobeMessageComposer, IAvatarFigureContainer, IFigurePartSet, IPalette, IPartColor, SetType, UserWardrobePageEvent } from '@nitrots/nitro-renderer';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useBetween } from 'use-between';
|
import { useBetween } from 'use-between';
|
||||||
import { AvatarEditorColorSorter, AvatarEditorPartSorter, AvatarEditorThumbnailsHelper, GetClubMemberLevel, GetConfigurationValue, IAvatarEditorCategory, IAvatarEditorCategoryPartItem, Randomizer, SendMessageComposer } from '../../api';
|
import { AvatarEditorColorSorter, AvatarEditorPartSorter, GetClubMemberLevel, GetConfigurationValue, IAvatarEditorCategory, IAvatarEditorCategoryPartItem, Randomizer, SendMessageComposer } from '../../api';
|
||||||
import { useMessageEvent } from '../events';
|
import { useMessageEvent } from '../events';
|
||||||
import { useFigureData } from './useFigureData';
|
import { useFigureData } from './useFigureData';
|
||||||
|
|
||||||
@@ -244,11 +244,6 @@ const useAvatarEditorState = () =>
|
|||||||
setSavedFigures(savedFigures);
|
setSavedFigures(savedFigures);
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
AvatarEditorThumbnailsHelper.clearCache();
|
|
||||||
}, [ selectedColorParts ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!isVisible) return;
|
if(!isVisible) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user