Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
お絵かきツールのパフォーマンスチューニング
Search
nontan-rh
April 14, 2017
Programming
1
2.3k
お絵かきツールのパフォーマンスチューニング
pixiv Sketch WebGLお絵かき機能のざっくりとしたチューニングの話
nontan-rh
April 14, 2017
Tweet
Share
More Decks by nontan-rh
See All by nontan-rh
VRoidキャラメイクシステム 完全解説
nontan
1
2.2k
Other Decks in Programming
See All in Programming
Result型で“失敗”を型にするPHPコードの書き方
kajitack
4
380
エラーって何種類あるの?
kajitack
5
310
Gleamという選択肢
comamoca
6
760
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
46
31k
Select API from Kotlin Coroutine
jmatsu
1
190
Go1.25からのGOMAXPROCS
kuro_kurorrr
1
800
Google Agent Development Kit でLINE Botを作ってみた
ymd65536
2
190
C++20 射影変換
faithandbrave
0
530
git worktree × Claude Code × MCP ~生成AI時代の並列開発フロー~
hisuzuya
1
480
Bytecode Manipulation 으로 생산성 높이기
bigstark
2
380
Haskell でアルゴリズムを抽象化する / 関数型言語で競技プログラミング
naoya
17
4.9k
AIコーディング道場勉強会#2 君(エンジニア)たちはどう生きるか
misakiotb
1
250
Featured
See All Featured
Raft: Consensus for Rubyists
vanstee
140
7k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
Mobile First: as difficult as doing things right
swwweet
223
9.7k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.4k
The Art of Programming - Codeland 2020
erikaheidi
54
13k
Java REST API Framework Comparison - PWX 2021
mraible
31
8.6k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.3k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
930
The Language of Interfaces
destraynor
158
25k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
BBQ
matthewcrist
89
9.7k
Code Reviewing Like a Champion
maltzj
524
40k
Transcript
お絵かきツールの パフォーマンスチューニング ~ 60FPSのために ~
自己紹介 • 名前: 磯崎 希 (のんたん) • 職業: アルバイト •
出身: ネイティブ畑 ◦ C++, Swift, など... ◦ Javascript歴は2ヶ月くらいです • 仕事内容: iOS, Android, Webのお絵かき機能を担当しています ◦ OpenGLまわりをゴリゴリやってます
今回の内容 • pixiv Sketchのお絵かき機能の総合的なパフォーマンス改善の話をします ◦ WebGLだけじゃないです • 改善例を列挙します • 基本的な話が多いです
準備
パフォーマンス改善の手順 1. ちゃんと測る 2. ちゃんとボトルネックを潰す 3. 1.に戻る
パフォーマンス改善の手順(つづき) • 「ちゃんと測る」がとても難しい ◦ クロック周波数が変化する ◦ 裏のプロセスの状況によって変化する ◦ 手でドローの情報を入力するのでムラがある ◦
ブラウザが起動してからどれくらい時間が経ったかでムラがある ◦ 何回も測って平均を取るべき ◦ 改善したかどうか検定するべき
パフォーマンスの指標 • FPSで評価するわけではない ◦ マウスイベントが入ってきたタイミングで処理をしている ◦ お絵かきツールでフレーム落ちは末期的 ◦ フレーム落ちが存在しているかどうかだけ見る •
代わりに1フレーム当たり処理時間の割合を見る ◦ 改善目的の処理が他の主要な処理の何 %の時間を要しているか
パフォーマンスの指標(Chromeの場合 その1)
パフォーマンスの指標(Chromeの場合 その2)
パフォーマンスの指標(Edgeの場合 その1)
パフォーマンスの指標(Edgeの場合 その2)
実例
GCが......? • ブラウザによって差が大きいが......
ストロークデータ生成(before) const BSplineCurve = { refine: (points: [number, number][]): [number,
number][] => { const refined = []; if (points.length - 1 >= 1) { for (let i = 1; i <= points.length - 1; i++) { refined .push(points[i - 1]); refined .push([(points[i - 1][0] + points[i][0]) * 0.5, (points[i - 1][1] + points[i][1]) * 0.5]); } } if (points.length > 0) { refined.push(points[points.length - 1]); } return refined; }, };
ストロークデータ生成(after) const BSplineCurve = { refine: (dst: number[], src: number[],
num): void => { for (let i = 1; i <= num - 1; i++) { const srcBase = (i - 1) * 2; const dstBase = (i - 1) * 4; dst[dstBase + 0] = src[srcBase]; dst[dstBase + 1] = src[srcBase + 1]; dst[dstBase + 2] = (src[srcBase] + src[srcBase + 2]) * 0.5; dst[dstBase + 3] = (src[srcBase + 1] + src[srcBase + 3]) * 0.5; } dst[(num - 1) * 4] = src[(num - 1) * 2]; dst[((num - 1) * 4) + 1] = src[((num - 1) * 2) + 1]; return ((num - 1) * 2) + 1; }, };
ストロークデータ生成(結果)
レイヤー合成が......?
レイヤー合成範囲 • 線を引いている途中、1フレームの間に変化している部分は一部のみ • 具体的には線の先っぽのみ
レイヤー合成範囲 • 変更があり得る場所だけを更新すれば良い • 更新範囲を覆う縦横が水平垂直の矩形を算出して、その部分だけ更新する
レイヤー合成範囲(補足) • B-スプライン曲線は凸包性がある ◦ 曲線は曲線を作るのに用いた制御点の凸包に収まる • 合成範囲は各制御点のx, yそれぞれの最小、最大を求めるだけで計算できる
レイヤー合成範囲(コード) // dirtyRect: 更新すべき矩形 gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array([ dirtyRect.left,
dirtyRect.top, dirtyRect.right, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, dirtyRect.left, dirtyRect.bottom, ]), gl.DYNAMIC_DRAW); gl.enableVertexAttribArray (this.vertexLocation ); gl.vertexAttribPointer (this.vertexLocation , 2, gl.FLOAT, false, 0, 0); gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
gl.bufferData, gl.bufferSubDataが重い...... • 前に述べた矩形(dirtyRect)の転送
Uniform化 // 初期化(一回だけ走る) const rect = new Float32Array([0, 0, 0,
1, 1, 1, 1, 0]); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, rect, gl.STATIC_DRAW); // 描画 gl.uniform4f(dirtyRectLocation , dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom); gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); // シェーダ(vertex shader) attribute vec2 inVertex ; uniform vec4 dirtyRect ; gl_Position = vec4((vec2(1.) - inVertex) * dirtyRect.xy + inVertex * dirtyRect.zw, 0., 1.);
レイヤー合成最適化の結果
gl.readPixels, IOは重い...... • Sketchで扱うデータはindexedDBに自動保存される ◦ 細かいストロークを連続したときに問題に
バックアップを遅延させる • ストロークが終わってから1秒間バックアップを遅延させる ◦ 1秒間の間に次のストロークが始まったらバックアップをキャンセル ◦ 次のストロークに関しても同様に
バックアップを遅延させる(コード) class BackupScheduler { schedule = () => { if
(this.timeoutId) { clearTimeout (this.timeoutId); this.timeoutId = null; } this.timeoutId = setTimeout(() => { if (this.isDrawing) { this.timeoutId = null; return; } // gl.readPixels(...); // indexedDBに保存 }, 1000); }; }
バックアップを遅延させる(結果)
グラフが紫色に...... • 内訳は「レンダリング」 ◦ WebGLでのレンダリングの時間ではなく、ブラウザ側の再描画の時間
グラフが紫色に...... • 原因はcanvasにメニューが覆いかぶさっていたこと ◦ 下のcanvasが再描画されると、その上にかかっている要素に再描画フラグが立つ (?) • メニューとcanvasの重なりをなくして改善
結果(Edge)
結果(Chrome)
描き出しが重い......
ReactのsetStateが重かった • ブラウザによって差が大きい • ストローク中はsetStateを呼ばないようにする ◦ Reactに頼らない ◦ どうしても使いたければ、 setStateをストロークの終わりまで遅延させる
以上です 御清聴ありがとうございました