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.2k
お絵かきツールのパフォーマンスチューニング
pixiv Sketch WebGLお絵かき機能のざっくりとしたチューニングの話
nontan-rh
April 14, 2017
Tweet
Share
More Decks by nontan-rh
See All by nontan-rh
VRoidキャラメイクシステム 完全解説
nontan
1
2.1k
Other Decks in Programming
See All in Programming
ドメインイベント増えすぎ問題
h0r15h0
1
270
あれやってみてー駆動から成長を加速させる / areyattemite-driven
nashiusagi
1
200
Fibonacci Function Gallery - Part 1
philipschwarz
PRO
0
210
Zoneless Testing
rainerhahnekamp
0
120
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
3
120
KubeCon + CloudNativeCon NA 2024 Overviewat Kubernetes Meetup Tokyo #68 / amsy810_k8sjp68
masayaaoyama
0
250
PHPで学ぶプログラミングの教訓 / Lessons in Programming Learned through PHP
nrslib
2
170
From Translations to Multi Dimension Entities
alexanderschranz
2
130
rails statsで大解剖 🔍 “B/43流” のRailsの育て方を歴史とともに振り返ります
shoheimitani
2
930
htmxって知っていますか?次世代のHTML
hiro_ghap1
0
330
急成長期の品質とスピードを両立するフロントエンド技術基盤
soarteclab
0
930
talk-with-local-llm-with-web-streams-api
kbaba1001
0
180
Featured
See All Featured
Building Your Own Lightsaber
phodgson
103
6.1k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
32
2.7k
Gamification - CAS2011
davidbonilla
80
5.1k
Keith and Marios Guide to Fast Websites
keithpitt
410
22k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.3k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
0
97
Practical Orchestrator
shlominoach
186
10k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
28
900
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
Reflections from 52 weeks, 52 projects
jeffersonlam
347
20k
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をストロークの終わりまで遅延させる
以上です 御清聴ありがとうございました