1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tools/render/trace_renderer.h"
16
17 #include "tools/render/layout_constants.h"
18
19 namespace quic_trace {
20 namespace render {
21
22 namespace {
23
24 // Passthrough vector shader.
25 const char* kVertexShader = R"(
26 in vec4 coord;
27 void main(void) {
28 gl_Position = vec4(coord);
29 }
30 )";
31
32 // The geometry shader that accepts a packet represented as a 4-tuple of format
33 // (time, offset, size, kind) and represents it as a square, assigning
34 // appropriate color in process.
35 const char* kGeometryShader = R"(
36 uniform float distance_scale;
37 uniform vec2 margin;
38 uniform int highlighted_packet;
39
40 const float kSentPacketDurationMs = 1000;
41 const vec4 kUnit = vec4(1,1,1,1);
42
43 out vec4 v_color;
44
45 layout(points) in;
46 layout(triangle_strip, max_vertices = 4) out;
47
48 vec2 norm(vec2 x) {
49 return x * 2 - vec2(1, 1);
50 }
51
52 vec4 mapPoint(float x, float y) {
53 vec2 point = vec2(x, y);
54 point = norm((point - program_state.offset) / program_state.viewport);
55 return vec4(point * margin, 0, 1);
56 }
57
58 void main(void) {
59 float time = gl_in[0].gl_Position[0];
60 float offset = gl_in[0].gl_Position[1];
61 float size = gl_in[0].gl_Position[2];
62 float kind = gl_in[0].gl_Position[3];
63
64 // Sent is blue.
65 v_color = vec4(0.0, 0.0, 0.6, 1.0);
66 // Ack is green.
67 if (kind <= -1) {
68 v_color = vec4(0.0, 0.6, 0.0, 1.0);
69 }
70 // Lost is red.
71 if (kind <= -2) {
72 v_color = vec4(0.8, 0.0, 0.0, 1.0);
73 }
74 // App-limited is orange.
75 if (kind <= -3) {
76 v_color = vec4(1.0, 0.425, 0.0, 1.0);
77 }
78 // Highlight the specified packet by making it brighter.
79 if (highlighted_packet == gl_PrimitiveIDIn) {
80 v_color = kUnit - (kUnit - v_color) * 0.3;
81 }
82
83 float duration = distance_scale * kSentPacketDurationMs;
84 size *= distance_scale;
85
86 gl_Position = mapPoint(time, offset);
87 EmitVertex();
88
89 gl_Position = mapPoint(time + duration, offset);
90 EmitVertex();
91
92 gl_Position = mapPoint(time, offset + size);
93 EmitVertex();
94
95 gl_Position = mapPoint(time + duration, offset + size);
96 EmitVertex();
97
98 EndPrimitive();
99 }
100 )";
101
102 // A passthrough fragment shader.
103 const char* kFragmentShader = R"(
104 in vec4 v_color;
105 out vec4 color;
106 void main(void) {
107 color = v_color;
108 }
109 )";
110
PacketTypeToFloat(PacketType type)111 float PacketTypeToFloat(PacketType type) {
112 switch (type) {
113 case PacketType::SENT:
114 return 0;
115 case PacketType::ACKED:
116 return -1;
117 case PacketType::LOST:
118 return -2;
119 case PacketType::APP_LIMITED:
120 return -3;
121 }
122 }
123 } // namespace
124
TraceRenderer(const ProgramState * state)125 TraceRenderer::TraceRenderer(const ProgramState* state)
126 : state_(state), shader_(kVertexShader, kGeometryShader, kFragmentShader) {}
127
PacketCountHint(size_t count)128 void TraceRenderer::PacketCountHint(size_t count) {
129 DCHECK(!buffer_ready_);
130 packet_buffer_.reserve(count);
131 }
132
AddPacket(uint64_t time,uint64_t offset,uint64_t size,PacketType type)133 void TraceRenderer::AddPacket(uint64_t time,
134 uint64_t offset,
135 uint64_t size,
136 PacketType type) {
137 DCHECK(!buffer_ready_);
138 packet_buffer_.push_back(
139 Packet{static_cast<float>(time), static_cast<float>(offset),
140 static_cast<float>(size), PacketTypeToFloat(type)});
141 max_x_ = std::max(max_x_, time + kSentPacketDurationMs);
142 max_y_ = std::max(max_y_, static_cast<float>(offset + size));
143 }
144
FinishPackets()145 void TraceRenderer::FinishPackets() {
146 glBindBuffer(GL_ARRAY_BUFFER, *buffer_);
147 glBufferData(GL_ARRAY_BUFFER,
148 sizeof(*packet_buffer_.data()) * packet_buffer_.size(),
149 packet_buffer_.data(), GL_STATIC_DRAW);
150
151 packet_count_ = packet_buffer_.size();
152 packet_buffer_.clear();
153 buffer_ready_ = true;
154 }
155
Render()156 void TraceRenderer::Render() {
157 DCHECK(buffer_ready_);
158 glUseProgram(*shader_);
159 glBindVertexArray(*array_);
160 glBindBuffer(GL_ARRAY_BUFFER, *buffer_);
161
162 // Distance scaling.
163 // When packets are sufficiently far, they might get smaller than one pixel;
164 // that makes rendering look noisy and useless. Scale them back to ensure
165 // they take up at least a pixel on X axis. Note that we're actually scaling
166 // to 1.1 pixels in order to avoid jitter caused by edge cases when zooming.
167 float distance_scale = 1.f;
168 float packet_size_in_pixels =
169 kSentPacketDurationMs / state_->viewport().x * state_->window().x;
170 if (packet_size_in_pixels < 1.1f) {
171 distance_scale = 1.1f / packet_size_in_pixels;
172 }
173
174 const vec2 margin = TraceMargin(state_->dpi_scale());
175 const vec2 margin_scale = vec2(1.f, 1.f) - 2 * margin / state_->window();
176 state_->Bind(shader_);
177 shader_.SetUniform("distance_scale", distance_scale);
178 shader_.SetUniform("margin", margin_scale.x, margin_scale.y);
179 shader_.SetUniform("highlighted_packet", highlighted_packet_);
180
181 GlVertexArrayAttrib coord(shader_, "coord");
182 glVertexAttribPointer(*coord, 4, GL_FLOAT, GL_FALSE, 0, 0);
183
184 const vec2 render_size = state_->window() - 2 * margin;
185 glEnable(GL_SCISSOR_TEST);
186 glScissor(margin.x, margin.y, render_size.x, render_size.y);
187 glDrawArrays(GL_POINTS, 0, packet_count_);
188 glDisable(GL_SCISSOR_TEST);
189 }
190
191 } // namespace render
192 } // namespace quic_trace
193