blob: 349a386e3a91cf47aa97ecaacd1f87ac346a6eb8 [file] [log] [blame]
Avi Drissmandfd880852022-09-15 20:11:091// Copyright 2022 The Chromium Authors
Peter McNeeleyad3b2a52022-04-02 00:23:192// 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//
7class 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 McNeeley3c244002023-07-26 20:03:1925// This is currently only used for drawing rects and positional text.
26
Peter McNeeleyad3b2a52022-04-02 00:23:1927class DrawCall {
28 constructor(json) {
Jay Yang9c4c5772022-08-06 16:00:3429 // 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 McNeeleyad3b2a52022-04-02 00:23:1932 this.sourceIndex_ = parseInt(json.source_index);
33 this.drawIndex_ = parseInt(json.drawindex);
Kevin Haslettcfcb006f2023-12-12 21:42:2834 this.threadId_ =
35 parseInt(json.thread_id) || DrawFrame.demo_thread.thread_id;
Peter McNeeley3c244002023-07-26 20:03:1936 this.text = json.text;
Peter McNeeleyad3b2a52022-04-02 00:23:1937 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 Haslettcfcb006f2023-12-12 21:42:2850 this.buffer_id = json.buff_id || -1;
Justin Changb07dc202022-08-10 22:36:0651 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 McNeeleyad3b2a52022-04-02 00:23:1971 }
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 Gilhuly401040762022-08-17 16:02:3087 draw(context, buffer_map, threadConfig) {
Peter McNeeleyad3b2a52022-04-02 00:23:1988 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 Yang6a756262022-08-10 19:32:1399 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 McNeeleyad3b2a52022-04-02 00:23:19111
Jay Yang6a756262022-08-10 19:32:13112 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 Yangd3391f02022-06-17 19:04:00117
Peter McNeeleyad3b2a52022-04-02 00:23:19118 if (color && alpha) {
Jay Yangd3391f02022-06-17 19:04:00119 context.fillStyle = color + alpha;
River Gilhuly401040762022-08-17 16:02:30120 context.fillRect(this.pos_.x,
121 this.pos_.y,
122 this.size_.width,
123 this.size_.height);
Peter McNeeleyad3b2a52022-04-02 00:23:19124 }
125
Jay Yangd3391f02022-06-17 19:04:00126 context.strokeStyle = color;
River Gilhuly401040762022-08-17 16:02:30127 context.strokeRect(this.pos_.x,
128 this.pos_.y,
129 this.size_.width,
130 this.size_.height);
Justin Changb07dc202022-08-10 22:36:06131 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 Gilhuly401040762022-08-17 16:02:30140 this.pos_.x,
141 this.pos_.y,
142 this.size_.width,
143 this.size_.height);
Justin Chang17a28df42022-07-26 14:30:37144 }
Jay Yangd3391f02022-06-17 19:04:00145 }
Peter McNeeleyad3b2a52022-04-02 00:23:19146};
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.
152class Filter {
153 static instances = [];
154
Peter McNeeleyb76b14a92022-10-27 17:48:43155 constructor(enabled, selector, action, index) {
Peter McNeeleyad3b2a52022-04-02 00:23:19156 this.selector_ = {
157 filename: selector.filename,
158 func: selector.func,
159 anno: selector.anno,
160 };
161
Peter McNeeleyad3b2a52022-04-02 00:23:19162 // 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 McNeeleyb76b14a92022-10-27 17:48:43170 this.enabled_ = enabled;
Peter McNeeleyad3b2a52022-04-02 00:23:19171
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};