Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 1 | <!DOCTYPE html> |
| 2 | <html lang="en"> |
| 3 | <!-- |
Avi Drissman | dfd88085 | 2022-09-15 20:11:09 | [diff] [blame] | 4 | Copyright 2022 The Chromium Authors |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 5 | Use of this source code is governed by a BSD-style license that can be |
| 6 | found in the LICENSE file. |
| 7 | --> |
| 8 | |
| 9 | <head> |
| 10 | <style> |
| 11 | #scrubberframe::-webkit-slider-thumb { |
| 12 | appearance: none; |
| 13 | width: 20px; |
| 14 | height: 20px; |
| 15 | background-color: grey; |
| 16 | } |
| 17 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 18 | #scrubberframe { |
| 19 | margin-top: 10px; |
| 20 | flex-grow: 1; |
| 21 | appearance: none; |
| 22 | background-color: #f0f0f0; |
| 23 | width: 100%; |
| 24 | } |
| 25 | |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 26 | .scrubber::-webkit-slider-thumb { |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 27 | appearance: none; |
| 28 | width: 20px; |
| 29 | height: 20px; |
| 30 | background-color: grey; |
| 31 | } |
| 32 | |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 33 | .minMaxScrubber { |
| 34 | width: 100%; |
| 35 | display: flex; |
| 36 | } |
| 37 | |
| 38 | .minMaxScrubber > .scrubber { |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 39 | appearance: none; |
| 40 | background-color: #f0f0f0; |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 41 | margin: 0px; |
| 42 | flex-grow: 1; |
| 43 | flex-basis: 20px; /* width of slider-thumb */ |
| 44 | min-width: 0px; |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | #url { |
| 48 | font-family: monospace; |
| 49 | font-size: smaller; |
| 50 | } |
| 51 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 52 | #controls>#buttons { |
| 53 | display: flex; |
| 54 | } |
| 55 | |
| 56 | #log { |
| 57 | min-height: 100px; |
| 58 | max-height: 100%; |
| 59 | font-family: monospace; |
| 60 | overflow: auto; |
| 61 | } |
| 62 | |
| 63 | #connectionPanel, |
| 64 | #saveload { |
| 65 | display: inline-block; |
| 66 | } |
| 67 | |
| 68 | #connectionPanel, |
| 69 | #saveload, |
| 70 | #topPanel { |
| 71 | padding-bottom: 10px; |
| 72 | } |
| 73 | |
| 74 | #topPanel { |
| 75 | display: flex; |
| 76 | } |
| 77 | |
| 78 | #settings { |
| 79 | display: flex; |
| 80 | flex-direction: column; |
| 81 | font-size: small; |
| 82 | } |
| 83 | |
| 84 | .panelSection { |
| 85 | margin-right: 20px; |
| 86 | } |
| 87 | |
| 88 | .panelSection:last-child { |
| 89 | margin-right: 0px; |
| 90 | flex-grow: 1; |
| 91 | } |
| 92 | |
| 93 | #connection-status { |
| 94 | color: limegreen; |
| 95 | font-size: large; |
| 96 | padding-right: 5px; |
| 97 | } |
| 98 | |
| 99 | #connection-status.disconnected { |
| 100 | color: orange; |
| 101 | } |
| 102 | </style> |
| 103 | <link href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css" rel="stylesheet"> |
| 104 | <script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script> |
| 105 | <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined"> |
Jay Yang | 6a75626 | 2022-08-10 19:32:13 | [diff] [blame] | 106 | <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" /> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 107 | |
| 108 | <link rel='stylesheet' href='style.css'> |
| 109 | <script src='filter.js'></script> |
| 110 | <script src='filter-ui.js'></script> |
| 111 | <script src='frame.js'></script> |
| 112 | <script src='connection.js'></script> |
Jay Yang | 6a75626 | 2022-08-10 19:32:13 | [diff] [blame] | 113 | <script src='thread.js'></script> |
| 114 | <script src='thread-ui.js'></script> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 115 | </head> |
| 116 | |
| 117 | <div id='connectionPanel'> |
| 118 | <div class='sectionTitle'> |
| 119 | <font class='disconnected' id='connection-status'>●</font>Connection |
| 120 | </div> |
| 121 | <div class='section'> |
| 122 | <div title="Expert usage only. Autoconnect should provide the dev tools websocket through /json/version discovery."> |
| 123 | <input id='url' name='url' size=60 type="text" |
| 124 | placeholder='WebSocket URL or leave empty for autoconnect...'></input> |
| 125 | </div> |
| 126 | <button class="mdc-button mdc-button--outline" id='connect'> |
| 127 | <div class="mdc-button__ripple"></div> |
| 128 | <span class="mdc-button__label">Connect</span> |
| 129 | </button> |
| 130 | <button class="mdc-button mdc-button--outline" id='disconnect' disabled=true> |
| 131 | <div class="mdc-button__ripple"></div> |
| 132 | <span class="mdc-button__label">Disconnect</span> |
| 133 | </button> |
Justin Chang | 9e976fb | 2022-06-22 22:50:03 | [diff] [blame] | 134 | <input type="checkbox" id="autoconnect" name="autoconnect" checked="true"> |
| 135 | <label for="autoconnect" style="font-size:small; font:Roboto" > Autoconnect</label><br> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 136 | </div> |
| 137 | </div> |
| 138 | |
| 139 | <div id='saveload'> |
| 140 | <div class='sectionTitle'>Save/Load ...</div> |
| 141 | <div class='section'> |
| 142 | <button id='demo' class="mdc-button mdc-button--outline"> |
| 143 | <div class="mdc-button__ripple"></div> |
| 144 | <span class="mdc-button__label">Load demo data</span> |
| 145 | </button> |
| 146 | <button id='savedata' class="mdc-button mdc-button--outline" |
| 147 | title="Serializes current session stream to json file."> |
| 148 | <div class="mdc-button__ripple"></div> |
| 149 | <span class="mdc-button__label">Save to disk</span> |
| 150 | </button> |
| 151 | <button id='loaddata' class="mdc-button mdc-button--outline" |
| 152 | title="Deserializes json file of previous session and imports these frames into the App."> |
| 153 | <div class="mdc-button__ripple"></div> |
| 154 | <span class="mdc-button__label">Load from disk</span> |
| 155 | </button> |
| 156 | </div> |
| 157 | </div> |
| 158 | |
| 159 | <div id='logsPanel'> |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 160 | <div class='panelSection collapsible'> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 161 | <div class='sectionTitle'> |
| 162 | Logs |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 163 | [<span class="plus">+</span><span class="minus">-</span>] |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 164 | <!--input size=40 placeholder='Comma separated filter ...'>(TODO)</input--> |
| 165 | </div> |
| 166 | <div class='section'> |
| 167 | <div id='log'></div> |
| 168 | </div> |
| 169 | </div> |
| 170 | </div> |
| 171 | |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 172 | <div class='panelSection collapsible'> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 173 | <div class='sectionTitle' |
| 174 | title="Filter debug data stream. Filter operations occur in a left to right order with first match being applied."> |
| 175 | <i class="material-icons-outlined">filter_list</i> Filters |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 176 | [<span class="plus">+</span><span class="minus">-</span>] |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 177 | </div> |
| 178 | <div class='section'> |
| 179 | <div id='filters' class="mdc-chip-set mdc-chip-set--filter" role="grid" |
| 180 | title="Filter debug data stream. Filter operations occur in a left to right order with first match being applied."> |
| 181 | </div> |
| 182 | <button class="mdc-button mdc-button--outline" id='createfilter'> |
| 183 | <div class="mdc-button__ripple"></div> |
| 184 | <span class="mdc-button__label"><i class="material-icons-outlined">add_box</i> Add new filter</span> |
| 185 | </button> |
| 186 | </div> |
| 187 | </div> |
| 188 | |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 189 | <div class='panelSection collapsible'> |
Jay Yang | 9c4c577 | 2022-08-06 16:00:34 | [diff] [blame] | 190 | <div class='sectionTitle'> |
Jay Yang | 6a75626 | 2022-08-10 19:32:13 | [diff] [blame] | 191 | <i class="material-symbols-outlined"> |
| 192 | airwave |
Jay Yang | 9c4c577 | 2022-08-06 16:00:34 | [diff] [blame] | 193 | </i> |
| 194 | Threads |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 195 | [<span class="plus">+</span><span class="minus">-</span>] |
Jay Yang | 9c4c577 | 2022-08-06 16:00:34 | [diff] [blame] | 196 | </div> |
| 197 | |
| 198 | <div id='threads' class='section'></div> |
| 199 | </div> |
| 200 | |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 201 | <div class='panelSection collapsible'> |
| 202 | <div class='sectionTitle'> |
| 203 | Viewer Controls |
| 204 | [<span class="plus">+</span><span class="minus">-</span>] |
| 205 | </div> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 206 | <div class='section' id='controls'> |
| 207 | <div id='buttons'> |
| 208 | <button class="mdc-button mdc-button--outline" id='prev'> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 209 | <span class="mdc-button__label"><i class="material-icons-outlined">skip_previous</i> Previous frame</span> |
| 210 | </button> |
| 211 | <button class="mdc-button mdc-button--outline" id='play'> |
| 212 | <div class="mdc-button__ripple"></div> |
| 213 | <span class="mdc-button__label"><i class="material-icons-outlined">play_circle_outline</i> Play</span> |
| 214 | </button> |
| 215 | <button class="mdc-button mdc-button--outline" id='pause'> |
| 216 | <div class="mdc-button__ripple"></div> |
| 217 | <span class="mdc-button__label"><i class="material-icons-outlined">pause_circle_outline</i> Pause</span> |
| 218 | </button> |
| 219 | <button class="mdc-button mdc-button--outline" id='next'> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 220 | <span class="mdc-button__label">Next frame <i class="material-icons-outlined">skip_next</i></span> |
| 221 | </button> |
Peter McNeeley | 3cad8ee9 | 2023-04-28 01:20:50 | [diff] [blame] | 222 | <button class="mdc-button mdc-button--raised" id='live'> |
Justin Chang | 5cd0f4b | 2022-06-25 20:23:38 | [diff] [blame] | 223 | <div class="mdc-button__ripple"></div> |
| 224 | <span class="mdc-button__label">Live <i class="material-icons-outlined">fast_forward</i></span> |
| 225 | </button> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 226 | |
| 227 | </div> |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 228 | <div> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 229 | <div class='sectionTitle'>Frame Selection</div> |
| 230 | <input type='range' min='0' max='0' value='0' id='scrubberframe'></input> |
| 231 | </div> |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 232 | <div> |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 233 | <div class='sectionTitle'>Draw Selection<span id="drawRange"></span></div> |
| 234 | <div class="minMaxScrubber"> |
| 235 | <input type="range" id='minDrawScrubber' class="scrubber" |
| 236 | min="0" max="0" value="0" step="1"></input> |
| 237 | <input type="range" id='maxDrawScrubber' class="scrubber" |
| 238 | min="0" max="1" value="1" step="1"></input> |
| 239 | </div> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 240 | </div> |
| 241 | </div> |
| 242 | </div> |
| 243 | |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 244 | <div class="panelSection collapsible"> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 245 | <div class='sectionTitle'> |
| 246 | Viewer |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 247 | [<span class="plus">+</span><span class="minus">-</span>] |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 248 | </div> |
| 249 | <div style='float: right' class='sectionTitle'> |
| 250 | Scale |
| 251 | <select id="viewerscale"> |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 252 | <option id="100pct">100%</option> |
| 253 | <option id="50pct">50%</option> |
| 254 | <option id="200pct">200%</option> |
“Jay | f42d703a | 2024-12-10 19:49:15 | [diff] [blame] | 255 | <option id="freeCam">Free Camera</option> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 256 | </select> |
| 257 | </div> |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 258 | |
| 259 | <div style='float: right' class='sectionTitle'> |
| 260 | Orientation |
| 261 | <select id="viewerorientation"> |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 262 | <option id="0deg">0 deg clockwise</option> |
| 263 | <option id="90deg">90 deg clockwise</option> |
| 264 | <option id="180deg">180 deg clockwise</option> |
| 265 | <option id="270deg">270 deg clockwise</option> |
| 266 | <option id="hFlip">Horizontal Flip</option> |
| 267 | <option id="vFlip">Vertical Flip</option> |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 268 | </select> |
| 269 | </div> |
| 270 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 271 | <div class='section'> |
River Gilhuly | 02627b15 | 2022-11-23 22:36:02 | [diff] [blame] | 272 | <!--We need to fix viewer size to avoid scroll position change when |
Peter McNeeley | bb96b26 | 2022-08-31 18:51:22 | [diff] [blame] | 273 | multiple displays are present. crbug.com/1358526--> |
Michael Tang | 8b96bb00 | 2023-05-08 20:19:28 | [diff] [blame] | 274 | <div style="height:4000px; border: 1px dotted gray; background-color: #f0f0f0"> |
Peter McNeeley | bb96b26 | 2022-08-31 18:51:22 | [diff] [blame] | 275 | <canvas id='canvas' style="top :0px"></canvas> |
| 276 | </div> |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 277 | </div> |
| 278 | |
| 279 | <div class='modalContainer'></div> |
| 280 | </div> |
| 281 | |
| 282 | <div id='importtracing'> |
| 283 | <div class='sectionTitle'>Tracing (Prototype)...</div> |
| 284 | <div class='section'> |
| 285 | <button id='importtracebutton' class="mdc-button mdc-button--outline" |
| 286 | title="Import tracing data (json) into visual debugger app."> |
| 287 | <div class="mdc-button__ripple"></div> |
| 288 | <span class="mdc-button__label">Import Trace</span> |
| 289 | </button> |
| 290 | </div> |
| 291 | </div> |
| 292 | |
| 293 | <script> |
| 294 | function processIncomingFrame(json) { |
| 295 | if (!json) return; |
| 296 | |
| 297 | new DrawFrame(json); |
| 298 | Player.instance.onNewFrame(); |
| 299 | } |
| 300 | |
| 301 | async function testAnimate() { |
| 302 | const f = await fetch('demo.json'); |
| 303 | const text = await f.text(); |
| 304 | const json = JSON.parse(text); |
| 305 | for (const frame of json) { |
| 306 | processIncomingFrame(frame); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | async function saveDemoDataToDisk() { |
Jay Yang | eae9ccf | 2022-06-30 02:43:20 | [diff] [blame] | 311 | const text = JSON.stringify(DrawFrame.frameBuffer.instances.map(d => d.toJSON())); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 312 | const blob = new Blob([text], { type: 'text/plain' }); |
| 313 | const link = document.createElement('a'); |
| 314 | link.download = 'cvd-stream.json'; |
| 315 | link.href = window.URL.createObjectURL(blob); |
| 316 | link.click(); |
| 317 | } |
| 318 | |
| 319 | window.onload = function () { |
| 320 | |
| 321 | Connection.initialize(); |
| 322 | |
| 323 | const addFilterButton = document.querySelector('#createfilter'); |
| 324 | addFilterButton.addEventListener('click', () => { |
| 325 | showCreateFilterPopup(addFilterButton); |
| 326 | }); |
| 327 | |
| 328 | const container = document.querySelector('.modalContainer'); |
| 329 | container.addEventListener('click', (event) => { |
| 330 | if (event.target == container) |
| 331 | hideModal(); |
| 332 | }); |
| 333 | |
| 334 | const demo = document.querySelector('#demo'); |
| 335 | demo.addEventListener('click', testAnimate); |
| 336 | |
| 337 | const savedata = document.querySelector('#savedata'); |
| 338 | savedata.addEventListener('click', saveDemoDataToDisk); |
| 339 | |
| 340 | const loaddata = document.querySelector('#loaddata'); |
| 341 | loaddata.addEventListener('click', () => { |
| 342 | const f = document.createElement('input'); |
| 343 | f.type = 'file'; |
| 344 | f.addEventListener('change', () => { |
| 345 | const file = new FileReader(f.files[0]); |
| 346 | file.addEventListener('load', () => { |
| 347 | const json = JSON.parse(file.result); |
| 348 | for (const frame of json) { |
| 349 | processIncomingFrame(frame); |
| 350 | } |
| 351 | }); |
| 352 | file.readAsText(f.files[0]); |
| 353 | }); |
| 354 | f.click(); |
| 355 | }); |
| 356 | |
| 357 | const importtracedata = document.querySelector('#importtracebutton'); |
| 358 | importtracedata.addEventListener('click', () => { |
| 359 | const f = document.createElement('input'); |
| 360 | f.type = 'file'; |
| 361 | f.addEventListener('change', () => { |
Michael Tang | 606678b | 2024-02-05 20:31:17 | [diff] [blame] | 362 | async function handleBlob(blob) { |
| 363 | if (blob.type === 'application/x-gzip') { |
| 364 | // If the blob is gzipped, decompress it and recurse. |
| 365 | const ds = new DecompressionStream("gzip"); |
| 366 | const decompressedBlob = blob.stream().pipeThrough(ds); |
| 367 | handleBlob(await new Response(decompressedBlob).blob()); |
| 368 | } else { |
| 369 | try { |
| 370 | const json = JSON.parse(await blob.text()); |
| 371 | handleImportTraceJson(json); |
| 372 | } catch (e) { |
| 373 | if (e instanceof SyntaxError) { |
| 374 | // Not all JSON blobs have the right mimetype, so we just try |
| 375 | // and fail to check. |
| 376 | alert("Not valid JSON: " + blob.message); |
| 377 | } else { |
| 378 | throw e; |
Michael Tang | a18ce18 | 2024-01-03 22:43:41 | [diff] [blame] | 379 | } |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 380 | } |
| 381 | } |
Michael Tang | 606678b | 2024-02-05 20:31:17 | [diff] [blame] | 382 | } |
| 383 | |
| 384 | handleBlob(f.files[0]); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 385 | }); |
| 386 | f.click(); |
Michael Tang | 606678b | 2024-02-05 20:31:17 | [diff] [blame] | 387 | |
| 388 | function handleImportTraceJson(json) { |
| 389 | const traceEvents = json.traceEvents; |
| 390 | |
| 391 | curr_frame = "0"; |
| 392 | curr_draws = []; |
| 393 | sources_this_frame = [] |
| 394 | global_sources_mapping = [] |
| 395 | // Get the source index for an annotation, updating `global_sources_mapping` as needed. |
| 396 | let getSourceIndex = (anno) => { |
| 397 | let found_source_index = global_sources_mapping.indexOf(anno); |
| 398 | if (found_source_index === -1) { |
| 399 | global_sources_mapping.push(anno); |
| 400 | found_source_index = global_sources_mapping.length - 1; |
| 401 | sources_this_frame.push({ |
| 402 | "anno": anno, |
| 403 | "file": "none", |
| 404 | "func": "none", |
| 405 | "index": found_source_index, |
| 406 | "line": -1, |
| 407 | }); |
| 408 | } |
| 409 | return found_source_index; |
| 410 | }; |
| 411 | |
| 412 | threads_this_frame = new Set(); |
| 413 | global_threads = {}; |
| 414 | global_processes = {}; |
| 415 | // Return a faux thread ID that also includes the process ID. |
| 416 | // VizDebugger only tracks a thread ID, but we know the process ID as well in this case. |
| 417 | let trackThreadAndProcessId = (event) => { |
| 418 | // We're assuming the thread ID is not going to exceed u16. |
| 419 | let thread_id = (event.pid * 65536) + event.tid; |
| 420 | threads_this_frame.add({ |
| 421 | "thread_id": thread_id, |
| 422 | "thread_name": `${global_processes[event.pid]}/${global_threads[event.tid]}`, |
| 423 | }) |
| 424 | return thread_id; |
| 425 | } |
| 426 | let resolveThreadNamesAndResetThreadIdsThisFrame = () => { |
| 427 | let threads = []; |
| 428 | for (const thread of threads_this_frame) { |
| 429 | threads.push(thread); |
| 430 | } |
| 431 | threads_this_frame.clear(); |
| 432 | return threads; |
| 433 | }; |
| 434 | |
| 435 | |
| 436 | for (const event of traceEvents) { |
| 437 | if (event.name === "thread_name") { |
| 438 | global_threads[event.tid] = event.args.name; |
| 439 | } else if (event.name === "process_name") { |
| 440 | global_processes[event.pid] = event.args.name; |
| 441 | } else if (event.cat.includes("viz.visual_debugger")) { |
| 442 | if (event.name == "visual_debugger_sync") { |
| 443 | single_frame = { "drawcalls": [], "frame": "none", "logs": [], "new_sources": [], "time": "0", "version": 1, "windowx": 2400, "windowy": 1600, "threads": [{ "thread_id": "1", "thread_name": "allthreads" }] }; |
| 444 | single_frame.drawcalls = curr_draws; |
| 445 | curr_frame = event.args.last_presented_trace_id; |
| 446 | single_frame.frame = curr_frame; |
| 447 | single_frame.new_sources = sources_this_frame; |
| 448 | single_frame.windowx = parseInt(event.args.display_size.split("x")[0]); |
| 449 | single_frame.windowy = parseInt(event.args.display_size.split("x")[1]); |
| 450 | processIncomingFrame(single_frame); |
| 451 | curr_draws = []; |
| 452 | sources_this_frame = []; |
| 453 | threads_this_frame.clear(); |
| 454 | } |
| 455 | else { |
| 456 | const single_call = { "drawindex": curr_draws.length, "option": { "alpha": 5, "color": "#ff0000" }, "pos": [-1, -1], "size": [-1, -1], "source_index": -1, "thread_id": 1, "buff_id": -1 }; |
| 457 | |
| 458 | single_call.pos[0] = parseFloat(event.args.args.pos_x); |
| 459 | single_call.pos[1] = parseFloat(event.args.args.pos_y); |
| 460 | single_call.size[0] = parseFloat(event.args.args.size_x); |
| 461 | single_call.size[1] = parseFloat(event.args.args.size_y); |
| 462 | single_call.text = event.args.args.text; |
| 463 | single_call.source_index = getSourceIndex(event.name); |
| 464 | curr_draws.push(single_call); |
| 465 | } |
| 466 | } else if (event.cat.includes("viz.quads")) { |
| 467 | if (event.name === "cc::LayerTreeHostImpl") { |
| 468 | let draw_calls = []; |
| 469 | let logs = []; |
| 470 | |
| 471 | let render_pass_count = event.args.snapshot.frame.render_passes.length; |
| 472 | for (let i = 0; i < render_pass_count; i++) { |
| 473 | let render_pass = event.args.snapshot.frame.render_passes[i]; |
| 474 | |
| 475 | logs.push({ |
| 476 | "source_index": getSourceIndex("frame.render_pass.meta"), |
| 477 | "drawindex": draw_calls.length + logs.length, |
| 478 | "option": { "alpha": 0, "color": "#0000ff" }, |
| 479 | "thread_id": trackThreadAndProcessId(event), |
| 480 | "value": `Render pass id=${render_pass.id}, output_rect=${render_pass.output_rect}, damage_rect=${render_pass.damage_rect}, quad_list.size=${render_pass.quad_list.length}, copy_requests=${render_pass.copy_requests}`, |
| 481 | }); |
| 482 | |
| 483 | if (i < render_pass_count - 1) { |
| 484 | // Skip non-root render pass quads to reduce visual noise. |
| 485 | continue; |
| 486 | } |
| 487 | |
| 488 | draw_calls.push({ |
| 489 | "source_index": getSourceIndex("frame.render_pass.output_rect"), |
| 490 | "drawindex": draw_calls.length + logs.length, |
| 491 | "option": { "alpha": 5, "color": "#000000" }, |
| 492 | "pos": [render_pass.output_rect[0], render_pass.output_rect[1]], |
| 493 | "size": [render_pass.output_rect[2], render_pass.output_rect[3]], |
| 494 | "thread_id": trackThreadAndProcessId(event), |
| 495 | "buff_id": -1, |
| 496 | }); |
| 497 | |
| 498 | draw_calls.push({ |
| 499 | "source_index": getSourceIndex("frame.render_pass.damage"), |
| 500 | "drawindex": draw_calls.length + logs.length, |
| 501 | "option": { "alpha": 5, "color": "#000000" }, |
| 502 | "pos": [render_pass.damage_rect[0], render_pass.damage_rect[1]], |
| 503 | "size": [render_pass.damage_rect[2], render_pass.damage_rect[3]], |
| 504 | "thread_id": trackThreadAndProcessId(event), |
| 505 | "buff_id": -1, |
| 506 | }); |
| 507 | |
| 508 | for (let quad of render_pass.quad_list) { |
| 509 | draw_calls.push({ |
| 510 | "source_index": getSourceIndex("frame.render_pass.quad"), |
| 511 | "drawindex": draw_calls.length + logs.length, |
| 512 | "option": { "alpha": 5, "color": "#000000" }, |
| 513 | "pos": [ |
| 514 | quad.rect_as_target_space_quad[0], |
| 515 | quad.rect_as_target_space_quad[1], |
| 516 | ], |
| 517 | "size": [ |
| 518 | quad.rect_as_target_space_quad[2] - quad.rect_as_target_space_quad[0], |
| 519 | quad.rect_as_target_space_quad[5] - quad.rect_as_target_space_quad[1], |
| 520 | ], |
| 521 | "thread_id": trackThreadAndProcessId(event), |
| 522 | "buff_id": -1, |
| 523 | }); |
| 524 | |
| 525 | draw_calls.push({ |
| 526 | "source_index": getSourceIndex("frame.render_pass.material"), |
| 527 | "drawindex": draw_calls.length + logs.length, |
| 528 | "option": { "alpha": 0, "color": "#00ff00" }, |
| 529 | "pos": [ |
| 530 | quad.rect_as_target_space_quad[0], |
| 531 | quad.rect_as_target_space_quad[1], |
| 532 | ], |
| 533 | "size": [0, 0], |
| 534 | "text": `${quad.material}`, |
| 535 | "thread_id": trackThreadAndProcessId(event), |
| 536 | "buff_id": -1, |
| 537 | }); |
| 538 | } |
| 539 | } |
| 540 | |
| 541 | // This event contains a snapshot of the layer tree, so we can process a full frame immediately. |
| 542 | processIncomingFrame({ |
| 543 | "drawcalls": draw_calls, |
| 544 | "frame": event.tts, |
| 545 | "logs": logs, |
| 546 | "new_sources": sources_this_frame, |
| 547 | "time": event.ts, |
| 548 | "version": 1, |
| 549 | "windowx": event.args.snapshot.device_viewport_size.width, |
| 550 | "windowy": event.args.snapshot.device_viewport_size.height, |
| 551 | "threads": resolveThreadNamesAndResetThreadIdsThisFrame(), |
| 552 | }); |
| 553 | sources_this_frame = []; |
| 554 | } |
| 555 | } |
| 556 | } |
| 557 | } |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 558 | }); |
| 559 | |
Kevin Haslett | 6f0f72b | 2023-12-18 18:04:07 | [diff] [blame] | 560 | document.querySelectorAll('.panelSection.collapsible').forEach( |
| 561 | (section) => { |
| 562 | let title = section.querySelector('.sectionTitle'); |
| 563 | title.addEventListener('click', () => { |
| 564 | section.classList.toggle('collapsed'); |
| 565 | }); |
| 566 | }); |
| 567 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 568 | setUpPlayer(); |
Jay Yang | 32d4985 | 2022-06-21 19:18:28 | [diff] [blame] | 569 | restoreFilters(); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 570 | } |
| 571 | |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 572 | /** |
| 573 | * Gets value from localStorage if it exists, and sets the <option> with that |
| 574 | * id to be selected in options_el. |
| 575 | * @param {string} key: The localStorage key to get if present. |
| 576 | * @param {Element} options_el: The <select> Element to be updated. |
| 577 | * @returns {boolean} Whether a value from localStorage was used to change the |
| 578 | * the selected option. |
| 579 | */ |
| 580 | function getStoredOptionsValue(key, options_el) { |
| 581 | let storedId = localStorage.getItem(key); |
| 582 | if (storedId != null) { |
| 583 | let option = options_el.namedItem(storedId); |
| 584 | if (option == null) { |
| 585 | console.error(`No option with id ${storedId} found on ${options_el.id}`); |
| 586 | localStorage.removeItem(key); |
| 587 | } else { |
| 588 | options_el.selectedIndex = option.index; |
| 589 | return true; |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | return false; |
| 594 | } |
| 595 | |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 596 | function updateDrawScrubberSizes(minIndex, maxIndex, nDraws) { |
| 597 | const scrubberMin = document.querySelector('#minDrawScrubber'); |
| 598 | const scrubberMax = document.querySelector('#maxDrawScrubber'); |
| 599 | const drawRange = document.querySelector('#drawRange'); |
| 600 | |
| 601 | // The point where the sliders meet will be halway between the two values. |
| 602 | // This means the slider you select when clicking will always be the one |
| 603 | // closest to your mouse. |
| 604 | let mid = Math.floor((minIndex + maxIndex) / 2); |
| 605 | scrubberMin.max = mid; |
| 606 | scrubberMax.min = mid; |
| 607 | // Update the size of each slider to its fraction of the total range. |
| 608 | scrubberMin.style = `flex-grow: ${mid}`; |
| 609 | scrubberMax.style = `flex-grow: ${nDraws - mid}`; |
| 610 | } |
| 611 | |
| 612 | function setDrawScrubbers(minIndex, maxIndex, nDraws) { |
| 613 | const scrubberMin = document.querySelector('#minDrawScrubber'); |
| 614 | const scrubberMax = document.querySelector('#maxDrawScrubber'); |
| 615 | const drawRange = document.querySelector('#drawRange'); |
| 616 | |
Kevin Haslett | 20172e9c | 2023-12-15 22:01:09 | [diff] [blame] | 617 | scrubberMax.max = nDraws; |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 618 | scrubberMin.value = minIndex; |
| 619 | scrubberMax.value = maxIndex; |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 620 | drawRange.textContent = ` [${minIndex}, ${maxIndex})`; |
| 621 | |
| 622 | updateDrawScrubberSizes(minIndex, maxIndex, nDraws); |
| 623 | } |
| 624 | |
Kevin Haslett | 20172e9c | 2023-12-15 22:01:09 | [diff] [blame] | 625 | function updateFrameScrubber(oldest, newest, current) { |
| 626 | const scrubberFrame = document.querySelector('#scrubberframe'); |
| 627 | scrubberFrame.min = oldest; |
| 628 | scrubberFrame.max = newest; |
| 629 | scrubberFrame.value = current; |
| 630 | } |
| 631 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 632 | function setUpPlayer() { |
| 633 | // First, set up the viewer. |
| 634 | const canvas = document.querySelector('#canvas'); |
| 635 | const logContainer = document.querySelector('#log'); |
| 636 | const viewer = new Viewer(canvas, logContainer); |
| 637 | // Now create the player for the viewer. |
| 638 | const player = new Player(viewer, (frame) => { |
Kevin Haslett | 20172e9c | 2023-12-15 22:01:09 | [diff] [blame] | 639 | updateFrameScrubber( |
| 640 | DrawFrame.frameBuffer.oldestIndex(), |
| 641 | DrawFrame.frameBuffer.newestIndex(), |
| 642 | player.currentFrameIndex); |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 643 | setDrawScrubbers( |
| 644 | frame.minIndex(), frame.maxIndex(), frame.submissionCount()); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 645 | }); |
| 646 | |
| 647 | document.querySelector('#pause').addEventListener('click', () => { |
| 648 | player.pause(); |
Justin Chang | 5cd0f4b | 2022-06-25 20:23:38 | [diff] [blame] | 649 | pause.setAttribute('style', 'background:#000000;color:white'); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 650 | }); |
| 651 | |
| 652 | document.querySelector('#play').addEventListener('click', () => { |
| 653 | player.play(); |
Justin Chang | 5cd0f4b | 2022-06-25 20:23:38 | [diff] [blame] | 654 | pause.removeAttribute('style'); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 655 | }); |
| 656 | |
| 657 | document.querySelector('#prev').addEventListener('click', () => { |
| 658 | player.rewind(); |
| 659 | }); |
| 660 | |
| 661 | document.querySelector('#next').addEventListener('click', () => { |
| 662 | player.forward(); |
| 663 | }); |
| 664 | |
Justin Chang | 5cd0f4b | 2022-06-25 20:23:38 | [diff] [blame] | 665 | document.querySelector('#live').addEventListener('click', () => { |
Peter McNeeley | 3cad8ee9 | 2023-04-28 01:20:50 | [diff] [blame] | 666 | player.live(); |
Justin Chang | 5cd0f4b | 2022-06-25 20:23:38 | [diff] [blame] | 667 | has_disconnected = false; |
Justin Chang | 5cd0f4b | 2022-06-25 20:23:38 | [diff] [blame] | 668 | pause.removeAttribute('style'); |
| 669 | }); |
| 670 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 671 | const scrubberFrame = document.querySelector('#scrubberframe'); |
| 672 | scrubberFrame.addEventListener('input', () => { |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 673 | player.freezeFrame(parseInt(scrubberFrame.value)); |
Justin Chang | 5cd0f4b | 2022-06-25 20:23:38 | [diff] [blame] | 674 | pause.setAttribute('style', 'background:#000000;color:white'); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 675 | }); |
| 676 | |
Kevin Haslett | 78d72c9 | 2023-12-14 15:46:11 | [diff] [blame] | 677 | const scrubberDrawMin = document.querySelector('#minDrawScrubber'); |
| 678 | const scrubberDrawMax = document.querySelector('#maxDrawScrubber'); |
| 679 | function drawScrubberChanged() { |
| 680 | let min = parseInt(scrubberDrawMin.value); |
| 681 | let max = parseInt(scrubberDrawMax.value); |
| 682 | player.freezeFrame(parseInt(scrubberFrame.value), min, max); |
| 683 | updateDrawScrubberSizes(min, max, parseInt(scrubberDrawMax.max)); |
| 684 | } |
| 685 | |
| 686 | scrubberDrawMin.addEventListener('input', () => { |
| 687 | drawScrubberChanged(); |
| 688 | }); |
| 689 | scrubberDrawMax.addEventListener('input', () => { |
| 690 | drawScrubberChanged() |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 691 | }); |
Justin Chang | c0f45a6 | 2022-06-24 15:07:10 | [diff] [blame] | 692 | |
| 693 | let currentMouseX = 0; |
| 694 | let currentMouseY = 0; |
| 695 | let zoom = 100; |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 696 | const viewerScale = document.querySelector("#viewerscale"); |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 697 | |
| 698 | function scaleChanged() { |
“Jay | f42d703a | 2024-12-10 19:49:15 | [diff] [blame] | 699 | const selected = viewerScale.selectedOptions[0]; |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 700 | if (selected.id != "freeCam") { |
“Jay | f42d703a | 2024-12-10 19:49:15 | [diff] [blame] | 701 | viewer.resetTranslation(); |
Justin Chang | c0f45a6 | 2022-06-24 15:07:10 | [diff] [blame] | 702 | player.setViewerScale(viewerScale.value); |
| 703 | zoom = parseInt(viewerScale.value); |
| 704 | } |
| 705 | else { |
| 706 | player.setViewerScale(zoom); |
| 707 | canvas.addEventListener('mouseenter', () => { |
| 708 | canvas.addEventListener('mousemove', function (event) { |
| 709 | currentMouseX = Math.round(event.offsetX); |
| 710 | currentMouseY = Math.round(event.offsetY); |
| 711 | }); |
| 712 | }); |
| 713 | canvas.addEventListener('wheel', function(e) { |
| 714 | e.preventDefault(); |
| 715 | var delta = e.deltaY; |
“Jay | f42d703a | 2024-12-10 19:49:15 | [diff] [blame] | 716 | const selected = viewerScale.selectedOptions[0]; |
River Gilhuly | 02627b15 | 2022-11-23 22:36:02 | [diff] [blame] | 717 | |
“Jay | f42d703a | 2024-12-10 19:49:15 | [diff] [blame] | 718 | if (selected.id == "freeCam") { |
| 719 | viewer.zoomToMouse(currentMouseX, currentMouseY, delta); |
| 720 | } |
Justin Chang | c0f45a6 | 2022-06-24 15:07:10 | [diff] [blame] | 721 | |
| 722 | }, {passive:false}); |
| 723 | } |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 724 | } |
| 725 | const scaleKey = "scale"; |
| 726 | if (getStoredOptionsValue(scaleKey, viewerScale)) { |
| 727 | scaleChanged(); |
| 728 | } |
| 729 | viewerScale.addEventListener('input', () => { |
| 730 | scaleChanged(); |
| 731 | localStorage.setItem(scaleKey, viewerScale.selectedOptions[0].id); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 732 | }); |
Justin Chang | c0f45a6 | 2022-06-24 15:07:10 | [diff] [blame] | 733 | |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 734 | const viewerOrientation = document.querySelector("#viewerorientation"); |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 735 | |
| 736 | function orientationChanged() { |
| 737 | let selected = viewerOrientation.selectedOptions[0]; |
| 738 | // Change dropdown style when setting non-zero orientation. |
| 739 | if (selected.id != '0deg') { |
| 740 | viewerOrientation.classList.add("nonzero-orientation"); |
| 741 | } else { |
| 742 | viewerOrientation.classList.remove("nonzero-orientation"); |
| 743 | } |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 744 | player.setViewerOrientation(viewerOrientation.value); |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 745 | } |
| 746 | const orientationKey = 'orientation'; |
| 747 | if (getStoredOptionsValue(orientationKey, viewerOrientation)) { |
| 748 | orientationChanged(); |
| 749 | } |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 750 | viewerOrientation.addEventListener('change', () => { |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 751 | orientationChanged(); |
| 752 | localStorage.setItem(orientationKey, viewerOrientation.selectedOptions[0].id); |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 753 | }); |
Kevin Haslett | 45222c9 | 2023-12-12 15:57:02 | [diff] [blame] | 754 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 755 | } |
| 756 | |
| 757 | |
River Gilhuly | 02627b15 | 2022-11-23 22:36:02 | [diff] [blame] | 758 | function showModal(element, focusSelector) { |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 759 | const container = document.querySelector('.modalContainer'); |
| 760 | container.appendChild(element); |
| 761 | container.style.display = 'block'; |
River Gilhuly | 02627b15 | 2022-11-23 22:36:02 | [diff] [blame] | 762 | element.querySelector(focusSelector).focus(); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 763 | } |
| 764 | |
| 765 | function hideModal() { |
| 766 | const container = document.querySelector('.modalContainer'); |
| 767 | container.style.display = 'none'; |
| 768 | container.textContent = ''; |
| 769 | } |
| 770 | |
| 771 | </script> |
| 772 | |
River Gilhuly | 02627b15 | 2022-11-23 22:36:02 | [diff] [blame] | 773 | </html> |