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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
nontan-rh
April 14, 2017
Programming
2.5k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
お絵かきツールのパフォーマンスチューニング
pixiv Sketch WebGLお絵かき機能のざっくりとしたチューニングの話
nontan-rh
April 14, 2017
More Decks by nontan-rh
See All by nontan-rh
VRoidキャラメイクシステム 完全解説
nontan
1
2.3k
Other Decks in Programming
See All in Programming
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
12k
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
270
Developing with AI Agents — Codex, Claude Code & Cowork Practical Guide
x5gtrn
PRO
0
1.2k
技術記事、 専門家としてのプログラマ、 言語化
mizchi
4
2.3k
AIチームを指揮するOSS「TAKT」活用術 / How to Use “TAKT,” an OSS Tool for Orchestrating AI Teams
nrslib
6
880
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
5.3k
Contextとはなにか
chiroruxx
0
290
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
0
220
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
530
Lessons from Spec-Driven Development
simas
PRO
0
170
Vite+ Unified Toolchain for the Web
naokihaba
0
270
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
390
Featured
See All Featured
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
1
180
Producing Creativity
orderedlist
PRO
348
40k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
Claude Code のすすめ
schroneko
67
230k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
160
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.5k
Git: the NoSQL Database
bkeepers
PRO
432
67k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
The untapped power of vector embeddings
frankvandijk
2
1.8k
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をストロークの終わりまで遅延させる
以上です 御清聴ありがとうございました