Avi Drissman | dfd88085 | 2022-09-15 20:11:09 | [diff] [blame] | 1 | // Copyright 2022 The Chromium Authors |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // Represents a source for a draw call, or a log, etc. |
| 6 | // |
| 7 | class Source { |
| 8 | static instances = []; |
| 9 | |
| 10 | constructor(json) { |
| 11 | this.file_ = json.file; |
| 12 | this.func_ = json.func; |
| 13 | this.line_ = json.line; |
| 14 | this.anno_ = json.anno; |
| 15 | const index = parseInt(json.index); |
| 16 | Source.instances[index] = this; |
| 17 | } |
| 18 | |
| 19 | get file() { return this.file_; } |
| 20 | get func() { return this.func_; } |
| 21 | get anno() { return this.anno_; } |
| 22 | }; |
| 23 | |
| 24 | // Represents a draw call. |
Peter McNeeley | 3c24400 | 2023-07-26 20:03:19 | [diff] [blame] | 25 | // This is currently only used for drawing rects and positional text. |
| 26 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 27 | class DrawCall { |
| 28 | constructor(json) { |
Jay Yang | 9c4c577 | 2022-08-06 16:00:34 | [diff] [blame] | 29 | // e.g. {"drawindex":"44", option":{"alpha":"1.000000","color":"#ffffff"} |
| 30 | // ,"pos":"0.000000,763.000000","size":"255x5","source_index":"0", |
| 31 | // "thread_id":"123456"} |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 32 | this.sourceIndex_ = parseInt(json.source_index); |
| 33 | this.drawIndex_ = parseInt(json.drawindex); |
Kevin Haslett | cfcb006f | 2023-12-12 21:42:28 | [diff] [blame] | 34 | this.threadId_ = |
| 35 | parseInt(json.thread_id) || DrawFrame.demo_thread.thread_id; |
Peter McNeeley | 3c24400 | 2023-07-26 20:03:19 | [diff] [blame] | 36 | this.text = json.text; |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 37 | this.size_ = { |
| 38 | width: json.size[0], |
| 39 | height: json.size[1], |
| 40 | }; |
| 41 | |
| 42 | this.pos_ = { |
| 43 | x: json.pos[0], |
| 44 | y: json.pos[1], |
| 45 | }; |
| 46 | if (json.option) { |
| 47 | this.color_ = json.option.color; |
| 48 | this.alpha_ = DrawCall.alphaIntToHex(json.option.alpha) |
| 49 | } |
Kevin Haslett | cfcb006f | 2023-12-12 21:42:28 | [diff] [blame] | 50 | this.buffer_id = json.buff_id || -1; |
Justin Chang | b07dc20 | 2022-08-10 22:36:06 | [diff] [blame] | 51 | if (json.uv_size && json.uv_pos) { |
| 52 | this.uv_size = { |
| 53 | width: json.uv_size[0], |
| 54 | height: json.uv_size[1], |
| 55 | }; |
| 56 | this.uv_pos = { |
| 57 | x: json.uv_pos[0], |
| 58 | y: json.uv_pos[1], |
| 59 | }; |
| 60 | } |
| 61 | else { |
| 62 | this.uv_size = { |
| 63 | width: 1.0, |
| 64 | height: 1.0, |
| 65 | }; |
| 66 | this.uv_pos = { |
| 67 | x: 0.0, |
| 68 | y: 0.0, |
| 69 | }; |
| 70 | } |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | // Used in conversion of Json. |
| 74 | static alphaIntToHex(value) { |
| 75 | value = Math.trunc(value); |
| 76 | value = Math.max(0, Math.min(value, 255)); |
| 77 | return value.toString(16).padStart(2, '0'); |
| 78 | } |
| 79 | |
| 80 | // Used internally to convert from UI filter to hex. |
| 81 | static alphaFloatToHex(value) { |
| 82 | value = Math.trunc(value * 255); |
| 83 | value = Math.max(0, Math.min(value, 255)); |
| 84 | return value.toString(16).padStart(2, '0'); |
| 85 | } |
| 86 | |
River Gilhuly | 40104076 | 2022-08-17 16:02:30 | [diff] [blame] | 87 | draw(context, buffer_map, threadConfig) { |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 88 | let filter = undefined; |
| 89 | const filters = Filter.enabledInstances(); |
| 90 | // TODO: multiple filters can match the same draw call. For now, let's just |
| 91 | // pick the earliest filter that matches, and let it decide what to do. |
| 92 | for (const f of filters) { |
| 93 | if (f.matches(Source.instances[this.sourceIndex_])) { |
| 94 | filter = f; |
| 95 | break; |
| 96 | } |
| 97 | } |
| 98 | |
Jay Yang | 6a75626 | 2022-08-10 19:32:13 | [diff] [blame] | 99 | var color; |
| 100 | var alpha; |
| 101 | // If thread drawing is overriding filters. |
| 102 | if (threadConfig.overrideFilters) { |
| 103 | color = threadConfig.threadColor; |
| 104 | alpha = threadConfig.threadAlpha; |
| 105 | } |
| 106 | // Otherwise, follow filter draw options. |
| 107 | else { |
| 108 | // No filters match this draw. So skip. |
| 109 | if (!filter) return; |
| 110 | if (!filter.shouldDraw) return; |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 111 | |
Jay Yang | 6a75626 | 2022-08-10 19:32:13 | [diff] [blame] | 112 | color = (filter && filter.drawColor) ? filter.drawColor : this.color_ |
| 113 | alpha = (filter && filter.fillAlpha) ? |
| 114 | DrawCall.alphaFloatToHex(parseFloat(filter.fillAlpha) / 100) : |
| 115 | this.alpha_; |
| 116 | } |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 117 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 118 | if (color && alpha) { |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 119 | context.fillStyle = color + alpha; |
River Gilhuly | 40104076 | 2022-08-17 16:02:30 | [diff] [blame] | 120 | context.fillRect(this.pos_.x, |
| 121 | this.pos_.y, |
| 122 | this.size_.width, |
| 123 | this.size_.height); |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 124 | } |
| 125 | |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 126 | context.strokeStyle = color; |
River Gilhuly | 40104076 | 2022-08-17 16:02:30 | [diff] [blame] | 127 | context.strokeRect(this.pos_.x, |
| 128 | this.pos_.y, |
| 129 | this.size_.width, |
| 130 | this.size_.height); |
Justin Chang | b07dc20 | 2022-08-10 22:36:06 | [diff] [blame] | 131 | var buff_id = this.buffer_id.toString(); |
| 132 | if(buffer_map[buff_id]) { |
| 133 | var buff_width = buffer_map[buff_id].width; |
| 134 | var buff_height = buffer_map[buff_id].height; |
| 135 | context.drawImage(buffer_map[buff_id], |
| 136 | this.uv_pos.x * buff_width, |
| 137 | this.uv_pos.y * buff_height, |
| 138 | this.uv_size.width * buff_width, |
| 139 | this.uv_size.height * buff_height, |
River Gilhuly | 40104076 | 2022-08-17 16:02:30 | [diff] [blame] | 140 | this.pos_.x, |
| 141 | this.pos_.y, |
| 142 | this.size_.width, |
| 143 | this.size_.height); |
Justin Chang | 17a28df4 | 2022-07-26 14:30:37 | [diff] [blame] | 144 | } |
Jay Yang | d3391f0 | 2022-06-17 19:04:00 | [diff] [blame] | 145 | } |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 146 | }; |
| 147 | |
| 148 | |
| 149 | // Represents a filter for draw calls. A filter specifies a selector (e.g. |
| 150 | // filename, and/or function name), and the action to take (e.g. skip draw, or |
| 151 | // color to use for draw, etc.) if the filter matches. |
| 152 | class Filter { |
| 153 | static instances = []; |
| 154 | |
Peter McNeeley | b76b14a9 | 2022-10-27 17:48:43 | [diff] [blame] | 155 | constructor(enabled, selector, action, index) { |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 156 | this.selector_ = { |
| 157 | filename: selector.filename, |
| 158 | func: selector.func, |
| 159 | anno: selector.anno, |
| 160 | }; |
| 161 | |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 162 | // XXX: If there are multiple selectors that apply to the same draw, then |
| 163 | // I guess the newest filter will take effect. |
| 164 | this.action_ = { |
| 165 | skipDraw: action.skipDraw, |
| 166 | color: action.color, |
| 167 | alpha: action.alpha, |
| 168 | }; |
| 169 | |
Peter McNeeley | b76b14a9 | 2022-10-27 17:48:43 | [diff] [blame] | 170 | this.enabled_ = enabled; |
Peter McNeeley | ad3b2a5 | 2022-04-02 00:23:19 | [diff] [blame] | 171 | |
| 172 | if (index === undefined) { |
| 173 | Filter.instances.push(this); |
| 174 | this.index_ = Filter.instances.length - 1; |
| 175 | } |
| 176 | else { |
| 177 | Filter.instances[index] = this; |
| 178 | this.index_ = index; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | get enabled() { return this.enabled_; } |
| 183 | set enabled(e) { this.enabled_ = e; } |
| 184 | |
| 185 | get file() { return this.selector_.filename || ""; } |
| 186 | get func() { return this.selector_.func || ""; } |
| 187 | get anno() { return this.selector_.anno || "" }; |
| 188 | |
| 189 | get shouldDraw() { return !this.action_.skipDraw; } |
| 190 | // undefined if using caller color |
| 191 | get drawColor() { return this.action_.color; } |
| 192 | // undefined if using caller alpha |
| 193 | get fillAlpha() { return this.action_.alpha; } |
| 194 | |
| 195 | get index() { return this.index_; } |
| 196 | |
| 197 | get streamFilter() { |
| 198 | return { |
| 199 | selector: { |
| 200 | file: this.selector_.filename, |
| 201 | func: this.selector_.func, |
| 202 | anno: this.selector_.anno |
| 203 | }, |
| 204 | active: !this.action_.skipDraw, |
| 205 | enabled: this.enabled_ |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | matches(source) { |
| 210 | if (!(source instanceof Source)) return false; |
| 211 | if (!this.enabled) return false; |
| 212 | |
| 213 | if (this.selector_.filename) { |
| 214 | const m = source.file.search(this.selector_.filename); |
| 215 | if (m == -1) return false; |
| 216 | } |
| 217 | |
| 218 | if (this.selector_.func) { |
| 219 | const m = source.func.search(this.selector_.func); |
| 220 | if (m == -1) return false; |
| 221 | } |
| 222 | |
| 223 | if (this.selector_.anno) { |
| 224 | const m = source.anno.search(this.selector_.anno); |
| 225 | if (m == -1) return false; |
| 226 | } |
| 227 | |
| 228 | return true; |
| 229 | } |
| 230 | |
| 231 | createUIString() { |
| 232 | let str = ''; |
| 233 | if (this.selector_.filename) { |
| 234 | const parts = this.selector_.filename.split('/'); |
| 235 | str += ` <i class="material-icons-outlined md-18"> |
| 236 | text_snippet</i>${parts[parts.length - 1]}`; |
| 237 | } |
| 238 | if (this.selector_.func) { |
| 239 | str += ` <i class="material-icons-outlined md-18"> |
| 240 | code</i>${this.selector_.func}`; |
| 241 | } |
| 242 | if (this.selector_.anno) { |
| 243 | str += ` <i class="material-icons-outlined md-18"> |
| 244 | message</i>${this.selector_.anno}`; |
| 245 | } |
| 246 | return str; |
| 247 | } |
| 248 | |
| 249 | static enabledInstances() { |
| 250 | return Filter.instances.filter(f => f.enabled); |
| 251 | } |
| 252 | |
| 253 | static getFilter(index) { |
| 254 | return Filter.instances[index]; |
| 255 | } |
| 256 | |
| 257 | static swapFilters(indexA, indexB) { |
| 258 | var filterA = Filter.instances[indexA]; |
| 259 | var filterB = Filter.instances[indexB]; |
| 260 | filterA.index_ = indexB; |
| 261 | filterB.index_ = indexA; |
| 262 | Filter.instances[indexB] = filterA; |
| 263 | Filter.instances[indexA] = filterB; |
| 264 | } |
| 265 | |
| 266 | static deleteFilter(index) { |
| 267 | Filter.instances.splice(index, 1); |
| 268 | for (var i = index; i < Filter.instances.length; i++) { |
| 269 | Filter.instances[i].index_ -= 1; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | static sendStreamFilters() { |
| 274 | const message = {}; |
| 275 | message['method'] = 'VisualDebugger.filterStream'; |
| 276 | message['params'] = { |
| 277 | filter: { filters: Filter.instances.map((f) => f.streamFilter) } |
| 278 | }; |
| 279 | Connection.sendMessage(message); |
| 280 | } |
| 281 | }; |