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
DataformでPythonする / dataform-de-python
snhryt
0
170
ライブ配信サービスの インフラのジレンマ -マルチクラウドに至ったワケ-
mirrativ
1
190
Understanding Ruby Grammar Through Conflicts
yui_knk
1
100
Portapad紹介プレゼンテーション
gotoumakakeru
1
130
バイブコーディング × 設計思考
nogu66
0
120
CEDEC 2025 『ゲームにおけるリアルタイム通信への QUIC導入事例の紹介』
segadevtech
3
860
AIのメモリー
watany
13
1.4k
大規模FlutterプロジェクトのCI実行時間を約8割削減した話
teamlab
PRO
0
470
Gemini CLIの"強み"を知る! Gemini CLIとClaude Codeを比較してみた!
kotahisafuru
3
1k
Nuances on Kubernetes - RubyConf Taiwan 2025
envek
0
160
Google I/O recap web編 大分Web祭り2025
kponda
0
2.5k
一人でAIプロダクトを作るための工夫 〜技術選定・開発プロセス編〜 / I want AI to work harder
rkaga
12
2.6k
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.6k
Bash Introduction
62gerente
614
210k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Gamification - CAS2011
davidbonilla
81
5.4k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
60k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
1.1k
Product Roadmaps are Hard
iamctodd
PRO
54
11k
Embracing the Ebb and Flow
colly
86
4.8k
Stop Working from a Prison Cell
hatefulcrawdad
271
21k
The Cost Of JavaScript in 2023
addyosmani
53
8.8k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
Large-scale JavaScript Application Architecture
addyosmani
512
110k
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をストロークの終わりまで遅延させる
以上です 御清聴ありがとうございました