์๋ ํ์ธ์, ์ ๋ ํ์ฌ ์ ํ๋ก์ ํธ๋ฅผ ์ํด PixiJS๋ฅผ ์กฐ์ฌ ์ค์ด๋ฉฐ ์ง๊ธ๊น์ง ์ ๋ง ๋ง์์ ๋ญ๋๋ค! ์์ฉ ํ๋ก๊ทธ๋จ์ ์ฑ๋ฅ๊ณผ ๊ด๋ จ๋ ๋ช ๊ฐ์ง ์๊ตฌ ์ฌํญ์ด ์์ต๋๋ค. ์ ๋ง ๊น๋ค๋ก์ด ๋ถ๋ถ์ ์ง์์ ์ผ๋ก ์ ๋ฐ์ดํธ๋๋ ๋ง์ ํ ์คํธ ๋ ์ด๋ธ์ด ํ์๋๋ค๋ ๊ฒ์ ๋๋ค. ์ฑ๋ฅ์ ๋ ํฅ์์ํฌ ๋ฐฉ๋ฒ์ ์ฐพ์ ์ ์์์ผ๋ฉฐ ๋ง์ง๋ง ํฌ๋ง์์ด ์ปค๋ฎค๋ํฐ์์ ๋์์ ์ฐพ๋ ๊ฒ์ ๋๋ค.
1500 ๊ฐ์ ํ
์คํธ ๋ ์ด๋ธ
์ด๋น 60 ๊ฐ์ ์์น ์
๋ฐ์ดํธ
60FPS
๋๋ ์ด๋ฏธ BitMapText๋ฅผ ์ฌ์ฉํ์ฌ MacBook Pro์์ ~ 28 FPS์ ์ต์ ์์ ๋ฅผ ๋ง๋ค์์ต๋๋ค.
https://www.pixiplayground.com/#/edit/rLbAN_xrw7yUU_cg_c8Xv
์ด๊ฒ์ ๊ฐ์ ํ ์์ด๋์ด๊ฐ ์์ต๋๊น? ๋ง์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค!
_ ๋ชจ๋ _ ๋ฒ ํ๋ฉด์ 1500 ๊ฐ๊ฐ ํ์ํฉ๋๊น? ์คํ ์คํฌ๋ฆฐ ๋ ์ด๋ธ์ ์ ๊ฑฐํ๋ฉด ์ฑ๋ฅ์ ๋ง์ ๋์์ด๋ฉ๋๋ค. Pixi๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ปฌ๋ง์ ์ํํ์ง ์์ง๋ง ์ฌ๊ธฐ์ ๋์์ด ๋ ์์๋ ๋ช ๊ฐ์ง ์ปค๋ฎค๋ํฐ ํ๋ฌ๊ทธ์ธ (์ : @SukantPal์ @pixi-essentials/cull
)์ด ์์ต๋๋ค.
1500 ๊ฐ์ ๋ ์ด๋ธ์ด ๋ชจ๋ ๊ณ ์ ํฉ๋๊น? ์ค๋ณต ํญ๋ชฉ์ด ๋ง์ผ๋ฉด RenderTexture๋ฅผ ์ฌ์ฉํ์ฌ Sprite๋ก ๋ณํํ๊ณ ๋ ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ก ์ฐธ์กฐ๋ฅผ ๊ณต์ ํ ์ ์์ต๋๋ค.
@ChristophWalter ์ฌ๋ฌ๋ถ์ด ๋ณด์ฌ์ค ์์ ๋ @bigtimebuddy๊ฐ ์ธ๊ธํ๋ฏ์ด ์ปฌ๋ง์ผ๋ก๋ถํฐ ์ ์ฌ์ ์ผ๋ก ์ด์ต์ ์ป์ ์ ์์ต๋๋ค. 112 ๊ฐ์ ๋ฌธ์๊ฐ์๋ 1500 ๊ฐ์ ๋ ์ด๋ธ์ ๋ ๋๋ง ํ _ ๋ง์ด _์ ๋๋ค. ์๋ฅผ ๋ค์ด ๊ด๋ จ ์ต์ ํ๋ฅผ ์ ์ํ๋ ๋ฐ ์ฌ์ฉํ ์์๋ ์ด์ ๊ฐ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ํ ์คํธ๊ฐ ๋๋ถ๋ถ ๊ฒน์นฉ๋๋ค. ๋๋ ๋น์ ์ ํ๋ก์ ํธ๊ฐ ๋๋ฌด ์กฐ๋ฐํ์ฌ ์ฌ์ฉ์๊ฐ ์ฝ์ ์์๋ ๋ค์ฃฝ๋ฐ์ฃฝ ๋ ํ ์คํธ๋ฅผ ํ์ ํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ์ง ์์ต๋๋ค.
MESH.BATCHABLE_SIZE๋ฅผ 200๊ณผ ๊ฐ์ด ๋ ๋์ ๊ฐ์ผ๋ก ์ค์ ํ๋ฉด ๋์์ด ๋ ์ ์์ต๋๋ค. iMac์์ 6x CPU ์๋ ์ ํ ํ์๋ ์ฌ์ ํ 45FPS๋ฅผ ํตํด ์ ์์ด ๊ณต๊ธ๋๊ธฐ ๋๋ฌธ์ ์ฒ ์ ํ ํ ์คํธํ์ง ์์์ต๋๋ค.
์์ ๋ฅผ ๋๋ฌด ๋ง์ด ํ๋กํ์ผ ๋งํ์ง ์์์ต๋๋ค. ๊ทธ๋ฌ๋ ์์ ์ฉ ํ๋ก์ ํธ์์ ์์ ์ค์ด๊ณ ์ด๊ฒ์ด GPU ์ธก ๋ณ๋ชฉ์ผ๋ก ํ๋ช ๋๋ฉด ๊ฐ ๋ฌธ์ (๋ชจ๋ As, ๋ชจ๋ B, ๋ชจ๋ C ๋ฑ)๋ฅผ ํจ๊ป ๋ ๋๋งํ๋ ๋น ์์ฐจ์ ๋ฌธ์ ๋ฅผ ๊ณ ๋ คํ ์ ์์ต๋๋ค. ํ ์ค์ฒ ์ง์ญ์ฑ์ ๊ฐ์ ํ๊ธฐ ์ํด ํ์ผ ์์ง ๊ฐ๋ฐ ๋ฐ / ๋๋ ํ๋ฉด์์ _changed_๊ฐ์๋ ๋ถ๋ถ์ ๋ ๋๋งํ๋ diff-rect ์ต์ ํ๋ฅผ ์ํํฉ๋๋ค (์ฆ, 1500 ๊ฐ์ ๋ ์ด๋ธ ์ค 60 ๊ฐ์ ์ ๋ฐ์ดํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํจ). ์ด๊ฒ์ ๋จ์ง ์์ด๋์ด์ด๋ฉฐ ๊ทธ๋๋ก ๋ฐ์ ๋ค์ฌ ์ ธ์ผํฉ๋๋ค.
๋์ ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!
์ค์ ๋ก ์ฐ๋ฆฌ๋ ์ปฌ๋ง์ ์ฌ์ฉํ ๊ฒ์ด๋ฉฐ ๋์์ ๋ง์ ๋ ์ด๋ธ์ ํ์ ํ ๊ฐ๋ฅ์ฑ์ ๊ฑฐ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ฒค์น ๋งํฌ ์ด์ ๋ก ์ฐ๋ฆฌ๋ ์น์ด ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๊ฒฝ์ํด์ผํฉ๋๋ค. ์ฐ๋ฆฌ๋ ์ด๋ฏธ ์ด๋ฌํ ์๊ตฌ ์ฌํญ์ ์๋ฌธ์ ์ ๊ธฐํ์ง๋ง webgl์ด์ด๋ฅผ ์ฒ๋ฆฌ ํ ์ โโ์๋ค๋ ๊ฒ์ ๋ณด์ฌ์ค ์ ์๋ค๋ฉด ๋ง์ ๋์์ด ๋ ๊ฒ์ ๋๋ค.
๋ ์ด๋ธ์ ๊ณ ์ ํฉ๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๋ค์ ๊ทธ๋ ๊ฒ ์์ฃผ ๋ณ๊ฒฝ๋์ง ์์ ์ ์์ต๋๋ค. ์ด ์์ ๋ ํ ์คํธ ๋ด์ฉ์ ์ ํ ๋ณ๊ฒฝํ์ง ์์ต๋๋ค. ๋ฐ๋ผ์ ์ต์ ํ ํ ๊ฐ๋ฅ์ฑ์ด ์์ต๋๋ค. ์์น ( ๋์ดํฐ )๋ฅผ ์ ๋ฐ์ดํธํ์ง ์์๋ FPS๊ฐ ๋์ผํ๊ฒ ์ ์ง๋๋ค๋ ๊ฒ์ ์์์ต๋๋ค. ํ ์คํธ๊ฐ ๋ณ๊ฒฝ ๋ ๋๋ง ๋ค์ ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๊น?
MESH.BATCHABLE_SIZE๋ฅผ ์ค์ ํด๋ ๋ด ํธ์ด ๋ฐ๋์ง ์์์ต๋๋ค. ๋ค๋ฅธ ์์ด๋์ด๋ฅผ ์ดํด ๋ณด๊ฑฐ๋ iMac ๊ตฌ์ ์ ๊ถ์ฅํฉ๋๋ค. ๐ ์ ๋ ๋ ๋๋ง์๋ณ๋ก ๊ด์ฌ์ด ์์ผ๋ฏ๋ก์ด ์์ด๋์ด๊ฐ ์ ๋ง ๋์์ด๋ฉ๋๋ค!
์, ํน์ ์๋ฅผ ์ค์ ๋ก ์ต์ ํํ๋ ๊ฒฝ์ฐ ์ต๊ณ ์ ๋ฐฉ๋ฒ์ @bigtimebuddy๊ฐ ๋งํ๋๋ก ํ
์ด๋ ๋ ๊ฐ์ง ์ฃผ์ ์ด์ ์ ์ ๊ณตํฉ๋๋ค.
๋ฐ๋ผ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ก์ธ์ค๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ด๊ฒ์ ๊ฐ๋จํ ๊ณผ์ ์ด์ด์ผํฉ๋๋ค. ์ด๋ป๊ฒ๋๋์ง ์๋ ค์ฃผ์ธ์!
์ด๋ด, ๋๋ ๋น์ ์ ์ ์์ ๊ตฌํํ๋ ค๊ณ ๋ ธ๋ ฅํ์ต๋๋ค. BitmapText๋ฅผ ํ ์ค์ฒ๋ก ๋ณํํ๋ ๊ฒ์ด ์๋ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์์ต๋๋ค. ํ์ง๋ง ํ์ฌ ๋ฉ์์ ํ ์ค์ฒ๋ฅผ ํ์ํ๋ ค๊ณ ๋ ธ๋ ฅ ์ค์ ๋๋ค. PIXI.Mesh ๋ฐ PIXI.SimpleMesh๋ฅผ ์ฌ์ฉํด ๋ณด์์ต๋๋ค. ๋๋ง์ ์ ฐ์ด๋๋ฅผ ๋ง๋ค์ด์ผํฉ๋๊น, ์๋๋ฉด PIXI.MeshMaterial์ ์ฌ์ฉํ ์ ์์ต๋๊น?
const bitmapFontText = new PIXI.BitmapText(
'Lorem ipsum dolor\nsit amet consetetur\nsadipscing elitr sed',
{ font: '55px Desyrel', align: 'left' }
);
const texture = PIXI.RenderTexture.create({ width: 800, height: 600 });
app.renderer.render(bitmapFontText, texture);
const vertices = [
-0.5, -0.5,
0.5, -0.5,
0.5, 0.5,
-0.5, 0.5
];
const uvs = [
0, 0,
1, 0,
1, 1,
0, 1,
];
const indices = [0, 1, 2, 0, 2, 3];
const geometry = new PIXI.MeshGeometry(vertices, uvs, indices);
const shader = new PIXI.MeshMaterial(texture);
const mesh = new PIXI.Mesh(geometry, shader);
app.stage.addChild(mesh);
https://www.pixiplayground.com/#/edit/7RHqFti0tdSzw -6iOtylk
-
_Edit_ : ์์ ํ์ต๋๋ค. ๊ผญ์ง์ ์ ํฝ์
์ขํ๋ฅผ ๋ํ๋ด์ผํฉ๋๋ค. _ ๋ค์ ๋จ๊ณ _ : ๋ฉ์์ ํ๋ ์ด์์ ํ
์ค์ฒ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
const vertices = [
0, 0,
500, 0,
500, 500,
0, 500
];
-
_ ํธ์ง 2_ : ๋ชจ๋ ๋ผ๋ฒจ์ ํ๋์ ํ
์ค์ฒ๋ฅผ ์ฌ์ฉํ ๋๋ง ์๋ํฉ๋๋ค. ๊ทธ๋ ์ฃ ? ๋ด๊ฐ ์๋ชป ํํํ์ ์๋ ์์ต๋๋ค. ๋ ์ด๋ธ์ ๊ณ ์ ํ์ง๋ง ๋ชจ๋ ํ๋ ์์์ ๋ด์ฉ์ด ๋ณ๊ฒฝ๋์ง๋ ์์ต๋๋ค.
์์น ( ๋์ดํฐ )๋ฅผ ์ ๋ฐ์ดํธํ์ง ์์๋ FPS๊ฐ ๋์ผํ๊ฒ ์ ์ง๋๋ค๋ ๊ฒ์ ์์์ต๋๋ค. ํ ์คํธ๊ฐ ๋ณ๊ฒฝ ๋ ๋๋ง ๋ค์ ๋ ๋๋งํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๊น?
PIXI.Mesh๊ฐ ๋์์ด ๋ ๊น์? PixiJS์ ๋ ๋๋ง ํ๋ก์ธ์ค์ ๋ํด ์ฝ์ ์์๋ ๋ฆฌ์์ค๋ฅผ ์๊ณ ์์ต๋๊น?
@ChristophWalter (์์ฉ ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ๋ ๋์ ) ์ง์ ๋ ๋๋ฌ๋ฅผ ๋ง๋ค๊ณ ์ฅ๋ฉด์ด ๋ณ๊ฒฝ ๋ ๋ Renderer.render
_only_๋ฅผ ํธ์ถํ๋ ํฐ์ปค ๋ฃจํ๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
์์ฉ ํ๋ก๊ทธ๋จ ์ต์
์ autoStart: false
๋ฅผ ์ ๋ฌํ ๋ค์ ๋ณ๊ฒฝ ์ฌํญ์ด์์ ๋ app.render()
๋ฅผ ํธ์ถ ํ ์๋ ์์ต๋๋ค. ๊ฐ์ ์ฐจ์ด.
๊ฐ์ฌํฉ๋๋ค. ์ด๋ป๊ฒ ๋ ํ ์คํธ์์ ํ ์ค์ฒ๋ฅผ ์์ฑํ๊ณ ์คํ๋ผ์ดํธ์์ ์ฌ์ฉํ๋ ์์ ์ ์ํํ์ต๋๋ค. ํ์ง๋ง ์ง๊ธ์ ์ ๊ทธ๋ฐ์ง ์ดํด๊ฐ ์๋๋ฉฐ ๋์ดํฐ์์ ์ฌํ ํ ์ ์์ต๋๋ค. ๐
๊ทธ๋ฌ๋ ์ด์ ์์ฉ ํ๋ก๊ทธ๋จ์ 60FPS๋ก ๋ชจ๋ ํ๋ ์์ 1500 ๊ฐ์ ๋ ์ด๋ธ ์์น๋ฅผ ๋ชจ๋ ์ ๋ฐ์ดํธํฉ๋๋ค. ๋ํ ๋งค์ด๋ง๋ค ํ ์คํธ ๋ด์ฉ์ด ์ ๋ฐ์ดํธ๋ฉ๋๋ค. ์ด๋ก ์ธํด ๋งค์ด ์งง์ ๋๊ฒฐ์ด ๋ฐ์ํฉ๋๋ค. ๋ฐ๋ผ์ ๋ค์ ๋จ๊ณ๋ ์ด๋ฌํ ํ ์คํธ ์ ๋ฐ์ดํธ๋ฅผ ๋น๋๊ธฐ ์ ์ผ๋ก ๋ ๋๋งํ๋ ๊ฒ์ ๋๋ค (์ : ์น ์์ ์). ํ์ฌ ImageBitmap ๋ฐ OffscreenCanvas์ ๋ํด ์ฝ๊ณ ์์ต๋๋ค. ์ด๊ฒ์ ๋ค๋ฅธ ์ฌ๋๋ค์๊ฒ ํฅ๋ฏธ๋ก์ธ ์ ์์ผ๋ฏ๋ก ์งํ ์ํฉ์ ๊ณต์ ํ๊ฒ ์ต๋๋ค.
๋ช ์ฃผ ์ ์ ์กฐ์ฌ๋ฅผ ์ค๋จ ํ ์งง์ ์
๋ฐ์ดํธ
์ค์ ๋ก ๋งค์ฐ ๊ฐ๋จํ๊ฒ ์์
ํ ์น ์์
์๋ก ํ
์คํธ๋ฅผ ๋ ๋๋งํ์ต๋๋ค.
// render-text-worker.js
onmessage = function(event) {
const textLines = event.data.text.split(/\n/);
const index = event.data.index;
const offscreen = new OffscreenCanvas(150,45);
const ctx = offscreen.getContext("2d");
ctx.font = "15px monospace";
textLines.forEach((text, index) => {
ctx.fillText(text, 0, 15 + index * 15)
})
const imageBitmap = offscreen.transferToImageBitmap();
postMessage({imageBitmap, index});
};
// index.js
const worker = new Worker("render-text-worker.js");
const callbacks ={}
function getLabelTexture(index, text, callback) {
callbacks[index] = callback
worker.postMessage({ index, text });
}
worker.onmessage = function({ data }) {
const callback = callbacks[data.index]
callback(PIXI.Texture.from(data.imageBitmap));
}
๋ถํํ๋ ๋ด ์ ์ค ์ผ์ด์ค์๋ ๊ฐ์ ์ด ์์ต๋๋ค. ํ ์คํธ๋ฅผ ๋ ๋๋งํ๋ ๋์ ํ๋ ์์ ์ฌ์ ํ โโ๋จ์ด์ง๋๋ค. ์์ ์๋ก๋ถํฐ ์ด๋ฏธ์ง ๋นํธ ๋งต์ ์ ์กํ๋ ๋ฐ ํ์ํ ์์ ๋๋ฌธ์ผ ์ ์์ต๋๋ค.
ํน์ ์ฌ์ฉ ์ฌ๋ก์ ๊ฒฝ์ฐ ์ฑ๋ฅ ์๊ตฌ ์ฌํญ์ ์ถฉ์กฑํ๊ธฐ ์ํด ๊ฐ ๋ ์ด๋ธ์ ํ ์คํธ ๊ธธ์ด๋ฅผ ์ค์ด๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๊ฐ์ฌํฉ๋๋ค. ์ด๋ป๊ฒ ๋ ํ ์คํธ์์ ํ ์ค์ฒ๋ฅผ ์์ฑํ๊ณ ์คํ๋ผ์ดํธ์์ ์ฌ์ฉํ๋ ์์ ์ ์ํํ์ต๋๋ค. ํ์ง๋ง ์ง๊ธ์ ์ ๊ทธ๋ฐ์ง ์ดํด๊ฐ ์๋๋ฉฐ ๋์ดํฐ์์ ์ฌํ ํ ์ ์์ต๋๋ค. ๐
๊ทธ๋ฌ๋ ์ด์ ์์ฉ ํ๋ก๊ทธ๋จ์ 60FPS๋ก ๋ชจ๋ ํ๋ ์์ 1500 ๊ฐ์ ๋ ์ด๋ธ ์์น๋ฅผ ๋ชจ๋ ์ ๋ฐ์ดํธํฉ๋๋ค. ๋ํ ๋งค์ด๋ง๋ค ํ ์คํธ ๋ด์ฉ์ด ์ ๋ฐ์ดํธ๋ฉ๋๋ค. ์ด๋ก ์ธํด ๋งค์ด ์งง์ ๋๊ฒฐ์ด ๋ฐ์ํฉ๋๋ค. ๋ฐ๋ผ์ ๋ค์ ๋จ๊ณ๋ ์ด๋ฌํ ํ ์คํธ ์ ๋ฐ์ดํธ๋ฅผ ๋น๋๊ธฐ ์ ์ผ๋ก ๋ ๋๋งํ๋ ๊ฒ์ ๋๋ค (์ : ์น ์์ ์). ํ์ฌ ImageBitmap ๋ฐ OffscreenCanvas์ ๋ํด ์ฝ๊ณ ์์ต๋๋ค. ์ด๊ฒ์ ๋ค๋ฅธ ์ฌ๋๋ค์๊ฒ ํฅ๋ฏธ๋ก์ธ ์ ์์ผ๋ฏ๋ก ์งํ ์ํฉ์ ๊ณต์ ํ๊ฒ ์ต๋๋ค.