ui: add burn in debug mode (#36625)

* ui: add burn in debug mode

* scary

* lil less

* lil cleanup

* revert that

* cleanup
master
Adeeb Shihadeh 15 hours ago committed by GitHub
parent 81be78cd4d
commit d0c6f845da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      system/ui/README.md
  2. 56
      system/ui/lib/application.py
  3. 16
      system/ui/lib/shader_polygon.py

@ -6,6 +6,7 @@ Quick start:
* set `SHOW_FPS=1` to show the FPS * set `SHOW_FPS=1` to show the FPS
* set `STRICT_MODE=1` to kill the app if it drops too much below 60fps * set `STRICT_MODE=1` to kill the app if it drops too much below 60fps
* set `SCALE=1.5` to scale the entire UI by 1.5x * set `SCALE=1.5` to scale the entire UI by 1.5x
* set `BURN_IN=1` to get a burn-in heatmap version of the UI
* https://www.raylib.com/cheatsheet/cheatsheet.html * https://www.raylib.com/cheatsheet/cheatsheet.html
* https://electronstudio.github.io/raylib-python-cffi/README.html#quickstart * https://electronstudio.github.io/raylib-python-cffi/README.html#quickstart

@ -6,6 +6,7 @@ import signal
import sys import sys
import pyray as rl import pyray as rl
import threading import threading
import platform
from contextlib import contextmanager from contextlib import contextmanager
from collections.abc import Callable from collections.abc import Callable
from collections import deque from collections import deque
@ -34,6 +35,43 @@ SCALE = float(os.getenv("SCALE", "1.0"))
PROFILE_RENDER = int(os.getenv("PROFILE_RENDER", "0")) PROFILE_RENDER = int(os.getenv("PROFILE_RENDER", "0"))
PROFILE_STATS = int(os.getenv("PROFILE_STATS", "100")) # Number of functions to show in profile output PROFILE_STATS = int(os.getenv("PROFILE_STATS", "100")) # Number of functions to show in profile output
GL_VERSION = """
#version 300 es
precision highp float;
"""
if platform.system() == "Darwin":
GL_VERSION = """
#version 330 core
"""
BURN_IN_MODE = "BURN_IN" in os.environ
BURN_IN_VERTEX_SHADER = GL_VERSION + """
in vec3 vertexPosition;
in vec2 vertexTexCoord;
uniform mat4 mvp;
out vec2 fragTexCoord;
void main() {
fragTexCoord = vertexTexCoord;
gl_Position = mvp * vec4(vertexPosition, 1.0);
}
"""
BURN_IN_FRAGMENT_SHADER = GL_VERSION + """
in vec2 fragTexCoord;
uniform sampler2D texture0;
out vec4 fragColor;
void main() {
vec4 sampled = texture(texture0, fragTexCoord);
float intensity = sampled.b;
// Map blue intensity to green -> yellow -> red to highlight burn-in risk.
vec3 start = vec3(0.0, 1.0, 0.0);
vec3 middle = vec3(1.0, 1.0, 0.0);
vec3 end = vec3(1.0, 0.0, 0.0);
vec3 gradient = mix(start, middle, clamp(intensity * 2.0, 0.0, 1.0));
gradient = mix(gradient, end, clamp((intensity - 0.5) * 2.0, 0.0, 1.0));
fragColor = vec4(gradient, sampled.a);
}
"""
DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_SIZE = 60
DEFAULT_TEXT_COLOR = rl.WHITE DEFAULT_TEXT_COLOR = rl.WHITE
@ -155,6 +193,7 @@ class GuiApplication:
self._scaled_width = int(self._width * self._scale) self._scaled_width = int(self._width * self._scale)
self._scaled_height = int(self._height * self._scale) self._scaled_height = int(self._height * self._scale)
self._render_texture: rl.RenderTexture | None = None self._render_texture: rl.RenderTexture | None = None
self._burn_in_shader: rl.Shader | None = None
self._textures: dict[str, rl.Texture] = {} self._textures: dict[str, rl.Texture] = {}
self._target_fps: int = _DEFAULT_FPS self._target_fps: int = _DEFAULT_FPS
self._last_fps_log_time: float = time.monotonic() self._last_fps_log_time: float = time.monotonic()
@ -212,8 +251,10 @@ class GuiApplication:
rl.set_config_flags(flags) rl.set_config_flags(flags)
rl.init_window(self._scaled_width, self._scaled_height, title) rl.init_window(self._scaled_width, self._scaled_height, title)
needs_render_texture = self._scale != 1.0 or BURN_IN_MODE
if self._scale != 1.0: if self._scale != 1.0:
rl.set_mouse_scale(1 / self._scale, 1 / self._scale) rl.set_mouse_scale(1 / self._scale, 1 / self._scale)
if needs_render_texture:
self._render_texture = rl.load_render_texture(self._width, self._height) self._render_texture = rl.load_render_texture(self._width, self._height)
rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)
rl.set_target_fps(fps) rl.set_target_fps(fps)
@ -222,6 +263,8 @@ class GuiApplication:
self._set_styles() self._set_styles()
self._load_fonts() self._load_fonts()
self._patch_text_functions() self._patch_text_functions()
if BURN_IN_MODE and self._burn_in_shader is None:
self._burn_in_shader = rl.load_shader_from_memory(BURN_IN_VERTEX_SHADER, BURN_IN_FRAGMENT_SHADER)
if not PC: if not PC:
self._mouse.start() self._mouse.start()
@ -337,6 +380,10 @@ class GuiApplication:
rl.unload_render_texture(self._render_texture) rl.unload_render_texture(self._render_texture)
self._render_texture = None self._render_texture = None
if self._burn_in_shader:
rl.unload_shader(self._burn_in_shader)
self._burn_in_shader = None
if not PC: if not PC:
self._mouse.stop() self._mouse.stop()
@ -395,7 +442,14 @@ class GuiApplication:
rl.clear_background(rl.BLACK) rl.clear_background(rl.BLACK)
src_rect = rl.Rectangle(0, 0, float(self._width), -float(self._height)) src_rect = rl.Rectangle(0, 0, float(self._width), -float(self._height))
dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height))
rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) texture = self._render_texture.texture
if texture:
if BURN_IN_MODE and self._burn_in_shader:
rl.begin_shader_mode(self._burn_in_shader)
rl.draw_texture_pro(texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE)
rl.end_shader_mode()
else:
rl.draw_texture_pro(texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE)
if self._show_fps: if self._show_fps:
rl.draw_fps(10, 10) rl.draw_fps(10, 10)

@ -1,9 +1,8 @@
import platform
import pyray as rl import pyray as rl
import numpy as np import numpy as np
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Optional, cast from typing import Any, Optional, cast
from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.application import gui_app, GL_VERSION
MAX_GRADIENT_COLORS = 20 # includes stops as well MAX_GRADIENT_COLORS = 20 # includes stops as well
@ -29,16 +28,7 @@ class Gradient:
self.stops = [i / max(1, color_count - 1) for i in range(color_count)] self.stops = [i / max(1, color_count - 1) for i in range(color_count)]
VERSION = """ FRAGMENT_SHADER = GL_VERSION + """
#version 300 es
precision highp float;
"""
if platform.system() == "Darwin":
VERSION = """
#version 330 core
"""
FRAGMENT_SHADER = VERSION + """
in vec2 fragTexCoord; in vec2 fragTexCoord;
out vec4 finalColor; out vec4 finalColor;
@ -83,7 +73,7 @@ void main() {
""" """
# Default vertex shader # Default vertex shader
VERTEX_SHADER = VERSION + """ VERTEX_SHADER = GL_VERSION + """
in vec3 vertexPosition; in vec3 vertexPosition;
in vec2 vertexTexCoord; in vec2 vertexTexCoord;
out vec2 fragTexCoord; out vec2 fragTexCoord;

Loading…
Cancel
Save