def _create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self._hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", x=float((Globals.base.win.get_x_size()) // 2) / self._gui_scale - 465 // 2, y=220, parent=self._fullscreen_node) self.set_reload_hint_visible(False) if not NATIVE_CXX_LOADED: # Warning when using the python version python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", x=((Globals.base.win.get_x_size()/self._gui_scale) - 1054) // 2, y=(Globals.base.win.get_y_size()/self._gui_scale) - 118 - 40, parent=self._fullscreen_node) Sequence( python_warning.color_scale_interval(0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), python_warning.color_scale_interval(0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self._keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", x=30, y=Globals.base.win.get_y_size()//self._gui_scale - 510.0, parent=self._fullscreen_node, any_filter=False)
def _create_components(self): """ Internal method to init the widgets components """ # Create the buffer which stores the last FPS values self._storage_buffer = Image.create_buffer("FPSValues", 250, "R16") self._storage_buffer.set_clear_color(Vec4(0)) self._storage_buffer.clear_image() self._store_index = PTAInt.empty_array(1) self._store_index[0] = 0 self._current_ftime = PTAFloat.empty_array(1) self._current_ftime[0] = 16.0 self._chart_ms_max = PTAFloat.empty_array(1) self._chart_ms_max[0] = 40 # Create the texture where the gui component is rendered inside self._display_tex = Image.create_2d("FPSChartRender", 250, 120, "RGBA8") self._display_tex.set_clear_color(Vec4(0)) self._display_tex.clear_image() self._display_img = Sprite(image=self._display_tex, parent=self._node, w=250, h=120, x=10, y=10) # Defer the further loading Globals.base.taskMgr.doMethodLater(0.3, self._late_init, "FPSChartInit")
def create_components(self): """ Creates the gui components """ self.debugger_width = 460 self.debugger_visible = False self.debugger_interval = None self.create_stats() self.create_hints() self.pipeline_logo = Sprite( image="/$$rp/data/gui/pipeline_logo_text.png", x=30, y=30, parent=self.fullscreen_node) if self.advanced_info: self.exposure_node = self.fullscreen_node.attach_new_node("ExposureWidget") self.exposure_widget = ExposureWidget(self.pipeline, self.exposure_node) self.fps_node = self.fullscreen_node.attach_new_node("FPSChart") self.fps_node.set_pos(Vec3(21, 1, -108 - 40)) self.fps_widget = FPSChart(self.pipeline, self.fps_node) self.pixel_widget = PixelInspector(self.pipeline) self.buffer_viewer = BufferViewer(self.pipeline, self.fullscreen_node) self.pipe_viewer = PipeViewer(self.pipeline, self.fullscreen_node) self.rm_selector = RenderModeSelector(self.pipeline, self.fullscreen_node) self.error_msg_handler = ErrorMessageDisplay() self.handle_window_resize()
def _create_topbar(self): """ Creates the topbar """ self._pipeline_logo = Sprite( image="/$$rp/data/gui/pipeline_logo_text.png", x=30, y=30, parent=self._fullscreen_node)
def _create_components(self): """ Internal method to init the widgets components """ self._node.hide() # Create the texture where the gui component is rendered inside self._storage_tex = Image.create_2d("ExposureDisplay", 140, 20, "RGBA8") self._storage_tex.set_clear_color(Vec4(0.2, 0.6, 1.0, 1.0)) self._storage_tex.clear_image() self._bg_frame = DirectFrame( parent=self._node, frameColor=(0.1, 0.1, 0.1, 1.0), frameSize=(200, 0, -10, -85), pos=(0, 0, 0)) self._display_img = Sprite( image=self._storage_tex, parent=self._node, w=140, h=20, x=20, y=50) self._display_txt = Text( text="Current Exposure".upper(), parent=self._node, x=160, y=40, size=13, color=Vec3(0.8), align="right") # Create the shader which generates the visualization texture self._cshader_node = ComputeNode("ExposureWidget") self._cshader_node.add_dispatch(140 // 10, 20 // 4, 1) self._cshader_np = self._node.attach_new_node(self._cshader_node) # Defer the further loading Globals.base.taskMgr.doMethodLater(1.0, self._late_init, "ExposureLateInit")
def _create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self._hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", x=float((Globals.base.win.get_x_size()) // 2) / self._gui_scale - 465 // 2, y=220, parent=self._fullscreen_node) self.set_reload_hint_visible(False) if not NATIVE_CXX_LOADED: # Warning when using the python version python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", x=((Globals.base.win.get_x_size() / self._gui_scale) - 1054) // 2, y=(Globals.base.win.get_y_size() / self._gui_scale) - 118 - 40, parent=self._fullscreen_node) Sequence( python_warning.color_scale_interval(0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), python_warning.color_scale_interval(0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self._keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", x=30, y=Globals.base.win.get_y_size() // self._gui_scale - 510.0, parent=self._fullscreen_node, any_filter=False)
def create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self.hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", parent=self.fullscreen_node) self.set_reload_hint_visible(False) self.python_warning = None if not NATIVE_CXX_LOADED: # Warning when using the python version self.python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", parent=self.fullscreen_node) Sequence( self.python_warning.color_scale_interval(0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), self.python_warning.color_scale_interval(0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self.keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", parent=self.fullscreen_node, any_filter=False)
def create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self.hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", parent=self.fullscreen_node) self.set_reload_hint_visible(False) self.python_warning = None if not NATIVE_CXX_LOADED: # Warning when using the python version self.python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", parent=self.fullscreen_node) Sequence( self.python_warning.color_scale_interval( 0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), self.python_warning.color_scale_interval( 0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self.keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", parent=self.fullscreen_node, any_filter=False)
def create(self): """ Creates the gui components """ screen_w, screen_h = Globals.base.win.get_x_size(), Globals.base.win.get_y_size() self.fullscreen_node = Globals.base.pixel2dp.attach_new_node("LoadingScreen") self.fullscreen_node.set_bin("fixed", 10) self.fullscreen_node.set_depth_test(False) scale_w = screen_w / 1920.0 scale_h = screen_h / 1080.0 scale = max(scale_w, scale_h) self.fullscreen_bg = Sprite( image="/$$rp/data/gui/loading_screen_bg.txo", x=(screen_w-1920.0*scale)//2, y=(screen_h-1080.0*scale)//2, w=int(1920 * scale), h=int(1080 * scale), parent=self.fullscreen_node, near_filter=False) for _ in range(2): Globals.base.graphicsEngine.render_frame()
def create(self): """ Creates the gui components """ screen_w, screen_h = Globals.native_resolution.x, Globals.native_resolution.y self.fullscreen_node = Globals.base.pixel2dp.attach_new_node( "LoadingScreen") self.fullscreen_node.set_bin("fixed", 10) self.fullscreen_node.set_depth_test(False) scale_w = screen_w / 1920.0 scale_h = screen_h / 1080.0 scale = max(scale_w, scale_h) self.fullscreen_bg = Sprite(image=self.image_source, x=(screen_w - 1920.0 * scale) // 2, y=(screen_h - 1080.0 * scale) // 2, w=int(1920 * scale), h=int(1080 * scale), parent=self.fullscreen_node, near_filter=False) for _ in range(2): Globals.base.graphicsEngine.render_frame()
class Debugger(RPObject): """ This class manages the onscreen gui and """ def __init__(self, pipeline): RPObject.__init__(self) self.debug("Creating debugger") self._pipeline = pipeline self._analyzer = SceneGraphAnalyzer() self._load_config() self._fullscreen_node = Globals.base.pixel2d.attach_new_node( "PipelineDebugger") self._create_components() self._init_keybindings() self._init_notify() Globals.base.doMethodLater( 0.5, lambda task: self._collect_scene_data(), "RPDebugger_collectSceneData_initial") Globals.base.doMethodLater(0.1, self._update_stats, "RPDebugger_updateStats") def _load_config(self): """ Loads the gui configuration from config/debugging.yaml """ def _create_components(self): """ Creates the gui components """ # When using small resolutions, scale the GUI so its still useable, # otherwise the sub-windows are bigger than the main window scale_factor = min(1.0, Globals.base.win.get_x_size() / 1920.0) self._fullscreen_node.set_scale(scale_factor) self._gui_scale = scale_factor # Component values self._debugger_width = 460 # Create states self._debugger_visible = False # Create intervals self._debugger_interval = None # Create the actual GUI self._create_topbar() self._create_stats() self._create_hints() self._exposure_node = self._fullscreen_node.attach_new_node("ExposureWidget") self._exposure_node.set_pos( Globals.base.win.get_x_size() / self._gui_scale - 200, 1, -Globals.base.win.get_y_size() / self._gui_scale + 120) self._exposure_widget = ExposureWidget(self._pipeline, self._exposure_node) self._fps_node = self._fullscreen_node.attach_new_node("FPSChart") self._fps_node.set_pos(Vec3(21, 1, -108 - 40)) self._fps_widget = FPSChart(self._pipeline, self._fps_node) self._pixel_widget = PixelInspector(self._pipeline) self._buffer_viewer = BufferViewer(self._pipeline, self._fullscreen_node) self._pipe_viewer = PipeViewer(self._pipeline, self._fullscreen_node) self._rm_selector = RenderModeSelector(self._pipeline, self._fullscreen_node) def _init_notify(self): """ Inits the notify stream which gets all output from panda and parses it """ self._error_msg_handler = ErrorMessageDisplay() def update(self): """ Updates the gui """ self._error_msg_handler.update() self._pixel_widget.update() def get_error_msg_handler(self): """ Returns the error message handler """ return self._error_msg_handler def _create_topbar(self): """ Creates the topbar """ self._pipeline_logo = Sprite( image="/$$rp/data/gui/pipeline_logo_text.png", x=30, y=50, parent=self._fullscreen_node) def _collect_scene_data(self, task=None): """ Analyzes the scene graph to provide useful information """ self._analyzer.clear() for geom_node in Globals.base.render.find_all_matches("**/+GeomNode"): self._analyzer.add_node(geom_node.node()) if task: return task.again def _create_stats(self): """ Creates the stats overlay """ self._overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self._overlay_node.set_pos(Globals.base.get_aspect_ratio() - 0.07, 1, 1.0 - 0.07) self._debug_lines = [] for i in range(5): self._debug_lines.append(TextNode( pos=Vec2(0, -i * 0.046), parent=self._overlay_node, pixel_size=16, align="right", color=Vec3(1))) def _create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self._hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", x=float((Globals.base.win.get_x_size()) // 2) / self._gui_scale - 465 // 2, y=220, parent=self._fullscreen_node) self.set_reload_hint_visible(False) if not NATIVE_CXX_LOADED: # Warning when using the python version python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", x=((Globals.base.win.get_x_size()/self._gui_scale) - 1054) // 2, y=(Globals.base.win.get_y_size()/self._gui_scale) - 118 - 40, parent=self._fullscreen_node) Sequence( python_warning.color_scale_interval(0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), python_warning.color_scale_interval(0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self._keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", x=30, y=Globals.base.win.get_y_size()//self._gui_scale - 510.0, parent=self._fullscreen_node, any_filter=False) def _update_stats(self, task=None): """ Updates the stats overlay """ clock = Globals.clock self._debug_lines[0].text = "{:3.0f} fps | {:3.1f} ms | {:3.1f} ms max".format( clock.get_average_frame_rate(), 1000.0 / max(0.001, clock.get_average_frame_rate()), clock.get_max_frame_duration() * 1000.0) text = "{:4d} render states | {:4d} transforms" text += " | {:4d} commands | {:4d} lights | {:5d} shadow sources " text += "| {:3.1f}% atlas usage" self._debug_lines[1].text = text.format( RenderState.get_num_states(), TransformState.get_num_states(), self._pipeline.light_mgr.cmd_queue.num_processed_commands, self._pipeline.light_mgr.num_lights, self._pipeline.light_mgr.num_shadow_sources, self._pipeline.light_mgr.shadow_atlas_coverage) text = "Pipeline: {:3.0f} MiB VRAM | {:5d} images | {:5d} textures | " text += "{:5d} render targets | {:3d} plugins" tex_memory, tex_count = self._buffer_viewer.stage_information self._debug_lines[2].text = text.format( tex_memory / (1024**2), len(Image.REGISTERED_IMAGES), tex_count, RenderTarget.NUM_ALLOCATED_BUFFERS, len(self._pipeline.plugin_mgr.enabled_plugins)) text = "Scene: {:4.0f} MiB VRAM | {:3d} textures | {:4d} geoms " text += "| {:4d} nodes | {:7,.0f} vertices | {:5.0f} MiB vTX data " scene_tex_size = 0 for tex in TexturePool.find_all_textures(): scene_tex_size += tex.estimate_texture_memory() self._debug_lines[3].text = text.format( scene_tex_size / (1024**2), len(TexturePool.find_all_textures()), self._analyzer.get_num_geoms(), self._analyzer.get_num_nodes(), self._analyzer.get_num_vertices(), self._analyzer.get_vertex_data_size() / (1024**2), ) sun_vector = Vec3(0) if self._pipeline.plugin_mgr.is_plugin_enabled("scattering"): sun_vector = self._pipeline.plugin_mgr.instances["scattering"].sun_vector text = "{} ({:1.3f}) | {:0.2f} {:0.2f} {:0.2f} | {:3d} daytime settings | X {:3.1f} Y {:3.1f} Z {:3.1f}" text += " | Total tasks: {:2d} | scheduled: {:2d}" self._debug_lines[4].text = text.format( self._pipeline.daytime_mgr.formatted_time, self._pipeline.daytime_mgr.time, sun_vector.x, sun_vector.y, sun_vector.z, len(self._pipeline.plugin_mgr.day_settings), Globals.base.camera.get_x(Globals.base.render), Globals.base.camera.get_y(Globals.base.render), Globals.base.camera.get_z(Globals.base.render), self._pipeline.task_scheduler.num_tasks, self._pipeline.task_scheduler.num_scheduled_tasks, ) if task: return task.again def set_reload_hint_visible(self, flag): """ Sets whether the shader reload hint is visible """ if flag: self._hint_reloading.show() else: self._hint_reloading.hide() def _init_keybindings(self): """ Inits the debugger keybindings """ Globals.base.accept("v", self._buffer_viewer.toggle) Globals.base.accept("c", self._pipe_viewer.toggle) Globals.base.accept("z", self._rm_selector.toggle) Globals.base.accept("f5", self._toggle_gui_visible) Globals.base.accept("f6", self._toggle_fps_visible) def _toggle_gui_visible(self): """ Shows / Hides the gui """ if not self._fullscreen_node.is_hidden(): self._fullscreen_node.hide() self._overlay_node.hide() else: self._fullscreen_node.show() self._overlay_node.show() def _toggle_fps_visible(self): """ Shows / Hides the FPS graph """ if not self._fps_node.is_hidden(): self._fps_node.hide() else: self._fps_node.show()
def _render_stages(self): """ Renders the stages to the window """ self._remove_components() entries_per_row = 6 aspect = Globals.native_resolution.y / Globals.native_resolution.x entry_width = 235 entry_height = (entry_width - 20) * aspect + 55 # Store already processed images processed = set() index = -1 # Iterate over all stages for stage_tex in self._stages: if stage_tex in processed: continue processed.add(stage_tex) index += 1 stage_name = stage_tex.get_name() xoffs = index % entries_per_row yoffs = index // entries_per_row node = self._content_node.attach_new_node("Preview") node.set_sz(-1) node.set_pos(10 + xoffs * (entry_width - 14), 1, yoffs * (entry_height - 14 + 10)) r, g, b = 0.2, 0.2, 0.2 if isinstance(stage_tex, Image): r, g, b = 0.2, 0.4, 0.6 stage_name = stage_name.replace("render_pipeline_internal:", "") parts = stage_name.split(":") stage_name = parts[-1] DirectFrame( parent=node, frameSize=(7, entry_width - 17, -7, -entry_height + 17), frameColor=(r, g, b, 1.0), pos=(0, 0, 0)) frame_hover = DirectFrame( parent=node, frameSize=(0, entry_width - 10, 0, -entry_height + 10), frameColor=(0, 0, 0, 0), pos=(0, 0, 0), state=DGG.NORMAL) frame_hover.bind( DGG.ENTER, partial(self._on_texture_hovered, frame_hover)) frame_hover.bind( DGG.EXIT, partial(self._on_texture_blurred, frame_hover)) frame_hover.bind( DGG.B1PRESS, partial(self._on_texture_clicked, stage_tex)) Text(text=stage_name, x=15, y=29, parent=node, size=12, color=Vec3(0.8)) # Scale image so it always fits w, h = stage_tex.get_x_size(), stage_tex.get_y_size() padd_x, padd_y = 24, 57 scale_x = (entry_width - padd_x) / max(1, w) scale_y = (entry_height - padd_y) / max(1, h) scale_factor = min(scale_x, scale_y) if stage_tex.get_texture_type() == Image.TT_buffer_texture: scale_factor = 1 w = entry_width - padd_x h = entry_height - padd_y preview = Sprite( image=stage_tex, w=scale_factor * w, h=scale_factor * h, any_filter=False, parent=node, x=7, y=40, transparent=False) preview.set_shader_input("mipmap", 0) preview.set_shader_input("slice", 0) preview.set_shader_input("brightness", 1) preview.set_shader_input("tonemap", False) preview_shader = DisplayShaderBuilder.build( stage_tex, scale_factor * w, scale_factor * h) preview.set_shader(preview_shader) num_rows = (index + entries_per_row) // entries_per_row self._set_scroll_height(50 + (entry_height - 14 + 10) * num_rows)
class Debugger(RPObject): """ This class manages the onscreen gui and """ def __init__(self, pipeline): RPObject.__init__(self) self.debug("Creating debugger") self._pipeline = pipeline self._analyzer = SceneGraphAnalyzer() self._load_config() self._fullscreen_node = Globals.base.pixel2d.attach_new_node( "PipelineDebugger") self._create_components() self._init_keybindings() self._init_notify() Globals.base.doMethodLater( 0.5, lambda task: self._collect_scene_data(), "RPDebugger_collectSceneData_initial") Globals.base.doMethodLater(0.1, self._update_stats, "RPDebugger_updateStats") def _load_config(self): """ Loads the gui configuration from config/debugging.yaml """ self._config = load_yaml_file("/$$rpconfig/debugging.yaml") def _create_components(self): """ Creates the gui components """ # When using small resolutions, scale the GUI so its still useable, # otherwise the sub-windows are bigger than the main window scale_factor = min(1.0, Globals.base.win.get_x_size() / 1920.0) self._fullscreen_node.set_scale(scale_factor) self._gui_scale = scale_factor # Component values self._debugger_width = 460 # Create states self._debugger_visible = False # Create intervals self._debugger_interval = None # Create the actual GUI self._create_debugger() self._create_topbar() self._create_stats() self._create_hints() self._exposure_node = self._fullscreen_node.attach_new_node("ExposureWidget") self._exposure_node.set_pos( Globals.base.win.get_x_size() / self._gui_scale - 200, 1, -Globals.base.win.get_y_size() / self._gui_scale + 120) self._exposure_widget = ExposureWidget(self._pipeline, self._exposure_node) self._fps_node = Globals.base.pixel2d.attach_new_node("FPSChart") self._fps_node.set_pos(Vec3(21, 1, -108) * scale_factor) self._fps_node.set_scale(scale_factor) self._fps_widget = FPSChart(self._pipeline, self._fps_node) self._pixel_widget = PixelInspector(self._pipeline) self._buffer_viewer = BufferViewer(self._pipeline, self._fullscreen_node) self._pipe_viewer = PipeViewer(self._pipeline, self._fullscreen_node) def _init_notify(self): """ Inits the notify stream which gets all output from panda and parses it """ self._error_msg_handler = ErrorMessageDisplay() def update(self): """ Updates the gui """ self._error_msg_handler.update() self._pixel_widget.update() def get_error_msg_handler(self): """ Returns the error message handler """ return self._error_msg_handler def _create_topbar(self): """ Creates the topbar """ self._pipeline_logo = Sprite( image="/$$rp/data/gui/pipeline_logo_text.png", x=30, y=30, parent=self._fullscreen_node) def _collect_scene_data(self, task=None): """ Analyzes the scene graph to provide useful information """ self._analyzer.clear() for geom_node in Globals.base.render.find_all_matches("**/+GeomNode"): self._analyzer.add_node(geom_node.node()) if task: return task.again def _create_stats(self): """ Creates the stats overlay """ self._overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self._overlay_node.set_pos(Globals.base.get_aspect_ratio() - 0.07, 1, 1.0 - 0.07) self._debug_lines = [] for i in range(5): self._debug_lines.append(TextNode( pos=Vec2(0, -i * 0.046), parent=self._overlay_node, pixel_size=16, align="right", color=Vec3(1))) def _create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self._hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", x=float((Globals.base.win.get_x_size()) // 2) / self._gui_scale - 465 // 2, y=220, parent=self._fullscreen_node) self.set_reload_hint_visible(False) if not NATIVE_CXX_LOADED: # Warning when using the python version python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", x=((Globals.base.win.get_x_size()/self._gui_scale) - 1054) // 2, y=(Globals.base.win.get_y_size()/self._gui_scale) - 118 - 40, parent=self._fullscreen_node) Sequence( python_warning.color_scale_interval(0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), python_warning.color_scale_interval(0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self._keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", x=30, y=Globals.base.win.get_y_size()//self._gui_scale - 510.0, parent=self._fullscreen_node, any_filter=False) def _update_stats(self, task=None): """ Updates the stats overlay """ clock = Globals.clock self._debug_lines[0].text = "{:3.0f} fps | {:3.1f} ms | {:3.1f} ms max".format( clock.get_average_frame_rate(), 1000.0 / max(0.001, clock.get_average_frame_rate()), clock.get_max_frame_duration() * 1000.0) text = "{:4d} render states | {:4d} transforms" text += " | {:4d} commands | {:4d} lights | {:5d} shadow sources " text += "| {:3.1f}% atlas usage" self._debug_lines[1].text = text.format( RenderState.get_num_states(), TransformState.get_num_states(), self._pipeline.light_mgr.cmd_queue.num_processed_commands, self._pipeline.light_mgr.num_lights, self._pipeline.light_mgr.num_shadow_sources, self._pipeline.light_mgr.shadow_atlas_coverage) text = "Pipeline: {:3.0f} MiB VRAM | {:5d} images | {:5d} textures | " text += "{:5d} render targets | {:3d} plugins" tex_memory, tex_count = self._buffer_viewer.stage_information self._debug_lines[2].text = text.format( tex_memory / (1024**2), len(Image.REGISTERED_IMAGES), tex_count, RenderTarget.NUM_ALLOCATED_BUFFERS, len(self._pipeline.plugin_mgr.enabled_plugins)) text = "Scene: {:4.0f} MiB VRAM | {:3d} textures | {:4d} geoms " text += "| {:4d} nodes | {:7,.0f} vertices | {:5.0f} MiB vTX data " scene_tex_size = 0 for tex in TexturePool.find_all_textures(): scene_tex_size += tex.estimate_texture_memory() self._debug_lines[3].text = text.format( scene_tex_size / (1024**2), len(TexturePool.find_all_textures()), self._analyzer.get_num_geoms(), self._analyzer.get_num_nodes(), self._analyzer.get_num_vertices(), self._analyzer.get_vertex_data_size() / (1024**2), ) text = "{} ({:1.3f}) | {:3d} daytime settings | X {:3.1f} Y {:3.1f} Z {:3.1f}" self._debug_lines[4].text = text.format( self._pipeline.daytime_mgr.formatted_time, self._pipeline.daytime_mgr.time, len(self._pipeline.plugin_mgr.day_settings), Globals.base.camera.get_x(Globals.base.render), Globals.base.camera.get_y(Globals.base.render), Globals.base.camera.get_z(Globals.base.render),) if task: return task.again def _create_debugger(self): """ Creates the debugger contents """ self._debugger_node = self._fullscreen_node.attach_new_node("DebuggerNode") self._debugger_node.set_pos(30, 0, -Globals.base.win.get_y_size()//self._gui_scale + 820.0) self._debugger_bg_img = Sprite( image="/$$rp/data/gui/debugger_background.png", x=0, y=0, parent=self._debugger_node, any_filter=False) self._create_debugger_content() def set_reload_hint_visible(self, flag): """ Sets whether the shader reload hint is visible """ if flag: self._hint_reloading.show() else: self._hint_reloading.hide() def _create_debugger_content(self): """ Internal method to create the content of the debugger """ debugger_content = self._debugger_node.attach_new_node("DebuggerContent") debugger_content.set_z(-20) debugger_content.set_x(20) render_modes = [("Default", "", False, "")] # Read modes from configuration for mode in self._config["render_modes"]: data = [mode["name"], mode["key"]] data.append("cxx_only" in mode and mode["cxx_only"]) data.append(mode["requires"] if "requires" in mode else "") render_modes.append(data) collection = CheckboxCollection() for idx, (mode, mode_id, requires_cxx, requires_plugin) in enumerate(render_modes): offs_y = idx * 24 + 45 offs_x = 0 enabled = True if requires_cxx and not NATIVE_CXX_LOADED: enabled = False if requires_plugin: if not self._pipeline.plugin_mgr.is_plugin_enabled(requires_plugin): enabled = False box = LabeledCheckbox( parent=debugger_content, x=offs_x, y=offs_y, text=mode.upper(), text_color=Vec3(0.4), radio=True, chb_checked=(mode == "Default"), chb_callback=partial(self._set_render_mode, mode_id), text_size=14, expand_width=230, enabled=enabled) collection.add(box.checkbox) def _set_render_mode(self, mode_id, value): """ Callback which gets called when a render mode got selected """ if not value: return # Clear old defines self._pipeline.stage_mgr.remove_define_if(lambda name: name.startswith("_RM__")) if mode_id == "": self._pipeline.stage_mgr.defines["ANY_DEBUG_MODE"] = 0 else: self._pipeline.stage_mgr.defines["ANY_DEBUG_MODE"] = 1 self._pipeline.stage_mgr.define["_RM_" + mode_id] = 1 # Reload all shaders self._pipeline.reload_shaders() def _init_keybindings(self): """ Inits the debugger keybindings """ Globals.base.accept("v", self._buffer_viewer.toggle) Globals.base.accept("c", self._pipe_viewer.toggle) Globals.base.accept("f5", self._toggle_gui_visible) Globals.base.accept("f6", self._toggle_fps_visible) def _toggle_gui_visible(self): """ Shows / Hides the gui """ if not self._fullscreen_node.is_hidden(): self._fullscreen_node.hide() self._overlay_node.hide() else: self._fullscreen_node.show() self._overlay_node.show() def _toggle_fps_visible(self): """ Shows / Hides the FPS graph """ if not self._fps_node.is_hidden(): self._fps_node.hide() else: self._fps_node.show()
class Debugger(RPObject): """ This class manages the onscreen gui and """ def __init__(self, pipeline): RPObject.__init__(self) self.debug("Creating debugger") self._pipeline = pipeline self._analyzer = SceneGraphAnalyzer() self._load_config() self._fullscreen_node = Globals.base.pixel2d.attach_new_node( "PipelineDebugger") self._create_components() self._init_keybindings() self._init_notify() Globals.base.doMethodLater(0.5, lambda task: self._collect_scene_data(), "RPDebugger_collectSceneData_initial") Globals.base.doMethodLater(0.1, self._update_stats, "RPDebugger_updateStats") def _load_config(self): """ Loads the gui configuration from config/debugging.yaml """ def _create_components(self): """ Creates the gui components """ # When using small resolutions, scale the GUI so its still useable, # otherwise the sub-windows are bigger than the main window scale_factor = min(1.0, Globals.base.win.get_x_size() / 1920.0) self._fullscreen_node.set_scale(scale_factor) self._gui_scale = scale_factor # Component values self._debugger_width = 460 # Create states self._debugger_visible = False # Create intervals self._debugger_interval = None # Create the actual GUI self._create_topbar() self._create_stats() self._create_hints() self._exposure_node = self._fullscreen_node.attach_new_node( "ExposureWidget") self._exposure_node.set_pos( Globals.base.win.get_x_size() / self._gui_scale - 200, 1, -Globals.base.win.get_y_size() / self._gui_scale + 120) self._exposure_widget = ExposureWidget(self._pipeline, self._exposure_node) self._fps_node = self._fullscreen_node.attach_new_node("FPSChart") self._fps_node.set_pos(Vec3(21, 1, -108 - 40)) self._fps_widget = FPSChart(self._pipeline, self._fps_node) self._pixel_widget = PixelInspector(self._pipeline) self._buffer_viewer = BufferViewer(self._pipeline, self._fullscreen_node) self._pipe_viewer = PipeViewer(self._pipeline, self._fullscreen_node) self._rm_selector = RenderModeSelector(self._pipeline, self._fullscreen_node) def _init_notify(self): """ Inits the notify stream which gets all output from panda and parses it """ self._error_msg_handler = ErrorMessageDisplay() def update(self): """ Updates the gui """ self._error_msg_handler.update() self._pixel_widget.update() def get_error_msg_handler(self): """ Returns the error message handler """ return self._error_msg_handler def _create_topbar(self): """ Creates the topbar """ self._pipeline_logo = Sprite( image="/$$rp/data/gui/pipeline_logo_text.png", x=30, y=30, parent=self._fullscreen_node) def _collect_scene_data(self, task=None): """ Analyzes the scene graph to provide useful information """ self._analyzer.clear() for geom_node in Globals.base.render.find_all_matches("**/+GeomNode"): self._analyzer.add_node(geom_node.node()) if task: return task.again def _create_stats(self): """ Creates the stats overlay """ self._overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self._overlay_node.set_pos(Globals.base.get_aspect_ratio() - 0.07, 1, 1.0 - 0.07) self._debug_lines = [] for i in range(6): self._debug_lines.append( TextNode(pos=Vec2(0, -i * 0.046), parent=self._overlay_node, pixel_size=16, align="right", color=Vec3(1))) def _create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self._hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", x=float((Globals.base.win.get_x_size()) // 2) / self._gui_scale - 465 // 2, y=220, parent=self._fullscreen_node) self.set_reload_hint_visible(False) if not NATIVE_CXX_LOADED: # Warning when using the python version python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", x=((Globals.base.win.get_x_size() / self._gui_scale) - 1054) // 2, y=(Globals.base.win.get_y_size() / self._gui_scale) - 118 - 40, parent=self._fullscreen_node) Sequence( python_warning.color_scale_interval(0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), python_warning.color_scale_interval(0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self._keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", x=30, y=Globals.base.win.get_y_size() // self._gui_scale - 510.0, parent=self._fullscreen_node, any_filter=False) def _update_stats(self, task=None): """ Updates the stats overlay """ clock = Globals.clock self._debug_lines[ 0].text = "{:3.0f} fps | {:3.1f} ms | {:3.1f} ms max".format( clock.get_average_frame_rate(), 1000.0 / max(0.001, clock.get_average_frame_rate()), clock.get_max_frame_duration() * 1000.0) text = "{:4d} render states | {:4d} transforms" text += " | {:4d} commands | {:4d} lights | {:5d} shadow sources " text += "| {:3.1f}% atlas usage" self._debug_lines[1].text = text.format( RenderState.get_num_states(), TransformState.get_num_states(), self._pipeline.light_mgr.cmd_queue.num_processed_commands, self._pipeline.light_mgr.num_lights, self._pipeline.light_mgr.num_shadow_sources, self._pipeline.light_mgr.shadow_atlas_coverage) text = "Pipeline: {:3.0f} MiB VRAM | {:5d} images | {:5d} textures | " text += "{:5d} render targets | {:3d} plugins | {:2d} views ({:2d} active)" tex_memory, tex_count = self._buffer_viewer.stage_information views, active_views = 0, 0 for target in RenderTarget.REGISTERED_TARGETS: if not target.create_default_region: num_regions = target.internal_buffer.get_num_display_regions() for i, region in enumerate( target.internal_buffer.get_display_regions()): if i == 0 and num_regions > 1: # Skip overlay display region continue views += 1 active_views += 1 if target.active and region.active else 0 self._debug_lines[2].text = text.format( tex_memory / (1024**2), len(Image.REGISTERED_IMAGES), tex_count, RenderTarget.NUM_ALLOCATED_BUFFERS, len(self._pipeline.plugin_mgr.enabled_plugins), views, active_views) text = "Scene: {:4.0f} MiB VRAM | {:3d} textures | {:4d} geoms " text += "| {:4d} nodes | {:7,.0f} vertices | {:5.0f} MiB vTX data " scene_tex_size = 0 for tex in TexturePool.find_all_textures(): scene_tex_size += tex.estimate_texture_memory() self._debug_lines[3].text = text.format( scene_tex_size / (1024**2), len(TexturePool.find_all_textures()), self._analyzer.get_num_geoms(), self._analyzer.get_num_nodes(), self._analyzer.get_num_vertices(), self._analyzer.get_vertex_data_size() / (1024**2), ) sun_vector = Vec3(0) if self._pipeline.plugin_mgr.is_plugin_enabled("scattering"): sun_vector = self._pipeline.plugin_mgr.instances[ "scattering"].sun_vector text = "{} ({:1.3f}) | {:0.2f} {:0.2f} {:0.2f} | {:3d} daytime settings | X {:3.1f} Y {:3.1f} Z {:3.1f}" text += " | Total tasks: {:2d} | scheduled: {:2d}" self._debug_lines[4].text = text.format( self._pipeline.daytime_mgr.formatted_time, self._pipeline.daytime_mgr.time, sun_vector.x, sun_vector.y, sun_vector.z, len(self._pipeline.plugin_mgr.day_settings), Globals.base.camera.get_x(Globals.base.render), Globals.base.camera.get_y(Globals.base.render), Globals.base.camera.get_z(Globals.base.render), self._pipeline.task_scheduler.num_tasks, self._pipeline.task_scheduler.num_scheduled_tasks, ) text = "Scene shadows: " if "pssm" in self._pipeline.plugin_mgr.enabled_plugins: focus = self._pipeline.plugin_mgr.instances[ "pssm"].scene_shadow_stage.last_focus if focus is not None: text += "{:3.1f} {:3.1f} {:3.1f} r {:3.1f}".format( focus[0].x, focus[0].y, focus[0].z, focus[1]) else: text += "none" else: text += "inactive" text += " | H {:3.1f} P {:3.1f} R {:3.1f}" self._debug_lines[5].text = text.format( Globals.base.camera.get_h(Globals.base.render), Globals.base.camera.get_p(Globals.base.render), Globals.base.camera.get_r(Globals.base.render), ) if task: return task.again def set_reload_hint_visible(self, flag): """ Sets whether the shader reload hint is visible """ if flag: self._hint_reloading.show() else: self._hint_reloading.hide() def _init_keybindings(self): """ Inits the debugger keybindings """ Globals.base.accept("v", self._buffer_viewer.toggle) Globals.base.accept("c", self._pipe_viewer.toggle) Globals.base.accept("z", self._rm_selector.toggle) Globals.base.accept("f5", self._toggle_gui_visible) Globals.base.accept("f6", self._toggle_fps_visible) def _toggle_gui_visible(self): """ Shows / Hides the gui """ if not self._fullscreen_node.is_hidden(): self._fullscreen_node.hide() self._overlay_node.hide() else: self._fullscreen_node.show() self._overlay_node.show() def _toggle_fps_visible(self): """ Shows / Hides the FPS graph """ if not self._fps_node.is_hidden(): self._fps_node.hide() else: self._fps_node.show()
def present(self, tex): """ "Presents" a given texture and shows the window """ self._current_tex = tex self.set_title(tex.get_name()) # tex.write(tex.get_name() + ".png") # Remove old content self._content_node.node().remove_all_children() w, h = tex.get_x_size(), tex.get_y_size() if h > 1: scale_x = (self._width - 40.0) / w scale_y = (self._height - 110.0) / h scale_f = min(scale_x, scale_y) display_w = scale_f * w display_h = scale_f * h else: display_w = self._width - 40 display_h = self._height - 110 image = Sprite( image=tex, parent=self._content_node, x=20, y=90, w=display_w, h=display_h, any_filter=False, transparent=False) description = "" # Image size description += "{:d} x {:d} x {:d}".format( tex.get_x_size(), tex.get_y_size(), tex.get_z_size()) # Image type description += ", {:s}, {:s}".format( Image.format_format(tex.get_format()).upper(), Image.format_component_type(tex.get_component_type()).upper()) Text(text=description, parent=self._content_node, x=17, y=70, size=16, color=Vec3(0.6, 0.6, 0.6)) estimated_bytes = tex.estimate_texture_memory() size_desc = "Estimated memory: {:2.2f} MB".format( estimated_bytes / (1024.0 ** 2)) Text(text=size_desc, parent=self._content_node, x=self._width - 20.0, y=70, size=18, color=Vec3(0.34, 0.564, 0.192), align="right") x_pos = len(size_desc) * 9 + 140 # Slider for viewing different mipmaps if tex.uses_mipmaps(): max_mips = tex.get_expected_num_mipmap_levels() - 1 self._mip_slider = Slider( parent=self._content_node, size=140, min_value=0, max_value=max_mips, callback=self._set_mip, x=x_pos, y=65, value=0) x_pos += 140 + 5 self._mip_text = Text( text="MIP: 5", parent=self._content_node, x=x_pos, y=72, size=18, color=Vec3(1, 0.4, 0.4), may_change=1) x_pos += 50 + 30 # Slider for viewing different Z-layers if tex.get_z_size() > 1: self._slice_slider = Slider( parent=self._content_node, size=250, min_value=0, max_value=tex.get_z_size() - 1, callback=self._set_slice, x=x_pos, y=65, value=0) x_pos += 250 + 5 self._slice_text = Text( text="Z: 5", parent=self._content_node, x=x_pos, y=72, size=18, color=Vec3(0.4, 1, 0.4), may_change=1) x_pos += 50 + 30 # Slider to adjust brightness self._bright_slider = Slider( parent=self._content_node, size=140, min_value=-14, max_value=14, callback=self._set_brightness, x=x_pos, y=65, value=0) x_pos += 140 + 5 self._bright_text = Text( text="Bright: 1", parent=self._content_node, x=x_pos, y=72, size=18, color=Vec3(0.4, 0.4, 1), may_change=1) x_pos += 100 + 30 # Slider to enable reinhard tonemapping self._tonemap_box = LabeledCheckbox( parent=self._content_node, x=x_pos, y=60, text="Tonemap", text_color=Vec3(1, 0.4, 0.4), chb_checked=False, chb_callback=self._set_enable_tonemap, text_size=18, expand_width=90) x_pos += 90 + 30 image.set_shader_input("slice", 0) image.set_shader_input("mipmap", 0) image.set_shader_input("brightness", 1) image.set_shader_input("tonemap", False) preview_shader = DisplayShaderBuilder.build(tex, display_w, display_h) image.set_shader(preview_shader) self._preview_image = image self.show()
def present(self, tex): """ "Presents" a given texture and shows the window """ self._current_tex = tex self.set_title(tex.get_name()) # tex.write(tex.get_name() + ".png") # Remove old content self._content_node.node().remove_all_children() w, h = tex.get_x_size(), tex.get_y_size() if h > 1: scale_x = (self._width - 40.0) / w scale_y = (self._height - 110.0) / h scale_f = min(scale_x, scale_y) display_w = scale_f * w display_h = scale_f * h else: display_w = self._width - 40 display_h = self._height - 110 image = Sprite( image=tex, parent=self._content_node, x=20, y=90, w=display_w, h=display_h, any_filter=False, transparent=False) description = "" # Image size description += "{:d} x {:d} x {:d}".format( tex.get_x_size(), tex.get_y_size(), tex.get_z_size()) # Image type description += ", {:s}, {:s}".format( Image.format_format(tex.get_format()).upper(), Image.format_component_type(tex.get_component_type()).upper()) Text(text=description, parent=self._content_node, x=17, y=70, size=16, color=Vec3(0.6, 0.6, 0.6)) estimated_bytes = tex.estimate_texture_memory() size_desc = "Estimated memory: {:2.2f} MB".format( estimated_bytes / (1024.0 ** 2)) Text(text=size_desc, parent=self._content_node, x=self._width - 20.0, y=70, size=18, color=Vec3(0.34, 0.564, 0.192), align="right") x_pos = len(size_desc) * 9 + 140 # Slider for viewing different mipmaps if tex.uses_mipmaps(): max_mips = tex.get_expected_num_mipmap_levels() - 1 self._mip_slider = Slider( parent=self._content_node, size=140, min_value=0, max_value=max_mips, callback=self._set_mip, x=x_pos, y=65, value=0) x_pos += 140 + 5 self._mip_text = Text( text="MIP: 5", parent=self._content_node, x=x_pos, y=72, size=18, color=Vec3(1, 0.4, 0.4), may_change=1) x_pos += 50 + 30 # Slider for viewing different Z-layers if tex.get_z_size() > 1: self._slice_slider = Slider( parent=self._content_node, size=250, min_value=0, max_value=tex.get_z_size() - 1, callback=self._set_slice, x=x_pos, y=65, value=0) x_pos += 250 + 5 self._slice_text = Text( text="Z: 5", parent=self._content_node, x=x_pos, y=72, size=18, color=Vec3(0.4, 1, 0.4), may_change=1) x_pos += 50 + 30 # Slider to adjust brightness self._bright_slider = Slider( parent=self._content_node, size=140, min_value=-14, max_value=14, callback=self._set_brightness, x=x_pos, y=65, value=0) x_pos += 140 + 5 self._bright_text = Text( text="Bright: 1", parent=self._content_node, x=x_pos, y=72, size=18, color=Vec3(0.4, 0.4, 1), may_change=1) x_pos += 100 + 30 # Slider to enable reinhard tonemapping self._tonemap_box = LabeledCheckbox( parent=self._content_node, x=x_pos, y=60, text="Tonemap", text_color=Vec3(1, 0.4, 0.4), chb_checked=False, chb_callback=self._set_enable_tonemap, text_size=18, expand_width=90) x_pos += 90 + 30 image.set_shader_inputs( slice=0, mipmap=0, brightness=1, tonemap=False) preview_shader = DisplayShaderBuilder.build(tex, display_w, display_h) image.set_shader(preview_shader) self._preview_image = image self.show()
class Debugger(RPObject): """ This class manages the onscreen control, and displays statistics. """ def __init__(self, pipeline): RPObject.__init__(self) self.debug("Creating debugger") self.pipeline = pipeline self.analyzer = SceneGraphAnalyzer() self.fullscreen_node = Globals.base.pixel2d.attach_new_node("rp_debugger") self.create_components() self.init_keybindings() if self.advanced_info: Globals.base.doMethodLater( 0.5, lambda task: self.collect_scene_data(), "RPDebugger_collectSceneData_initial") Globals.base.doMethodLater(0.1, self.update_stats, "RPDebugger_updateStats") @property def advanced_info(self): return self.pipeline.settings["pipeline.advanced_debugging_info"] def create_components(self): """ Creates the gui components """ self.debugger_width = 460 self.debugger_visible = False self.debugger_interval = None self.create_stats() self.create_hints() self.pipeline_logo = Sprite( image="/$$rp/data/gui/pipeline_logo_text.png", x=30, y=30, parent=self.fullscreen_node) if self.advanced_info: self.exposure_node = self.fullscreen_node.attach_new_node("ExposureWidget") self.exposure_widget = ExposureWidget(self.pipeline, self.exposure_node) self.fps_node = self.fullscreen_node.attach_new_node("FPSChart") self.fps_node.set_pos(Vec3(21, 1, -108 - 40)) self.fps_widget = FPSChart(self.pipeline, self.fps_node) self.pixel_widget = PixelInspector(self.pipeline) self.buffer_viewer = BufferViewer(self.pipeline, self.fullscreen_node) self.pipe_viewer = PipeViewer(self.pipeline, self.fullscreen_node) self.rm_selector = RenderModeSelector(self.pipeline, self.fullscreen_node) self.error_msg_handler = ErrorMessageDisplay() self.handle_window_resize() def update(self): """ Updates the gui """ self.error_msg_handler.update() self.pixel_widget.update() def collect_scene_data(self, task=None): """ Analyzes the scene graph to provide useful information """ self.analyzer.clear() for geom_node in Globals.base.render.find_all_matches("**/+GeomNode"): self.analyzer.add_node(geom_node.node()) if task: return task.again def create_stats(self): """ Creates the stats overlay """ self.overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self.debug_lines = [] num_lines = 6 if self.advanced_info else 1 for i in range(num_lines): self.debug_lines.append(TextNode( pos=Vec2(0, -i * 0.046), parent=self.overlay_node, align="right", color=Vec3(0.7, 1, 1))) self.debug_lines[0].color = Vec4(1, 1, 0, 1) def create_hints(self): """ Creates the hints like keybindings and when reloading shaders """ self.hint_reloading = Sprite( image="/$$rp/data/gui/shader_reload_hint.png", parent=self.fullscreen_node) self.set_reload_hint_visible(False) self.python_warning = None if not NATIVE_CXX_LOADED: # Warning when using the python version self.python_warning = Sprite( image="/$$rp/data/gui/python_warning.png", parent=self.fullscreen_node) Sequence( self.python_warning.color_scale_interval( 0.7, Vec4(0.3, 1, 1, 0.7), blendType="easeOut"), self.python_warning.color_scale_interval( 0.7, Vec4(1, 1, 1, 1.0), blendType="easeOut"), ).loop() # Keybinding hints self.keybinding_instructions = Sprite( image="/$$rp/data/gui/keybindings.png", parent=self.fullscreen_node, any_filter=False) def set_reload_hint_visible(self, flag): """ Sets whether the shader reload hint is visible """ if flag: self.hint_reloading.show() else: self.hint_reloading.hide() def handle_window_resize(self): """ Handles the window resize, repositions the GUI elements to fit on screen. """ # When using small resolutions, scale the GUI so its still useable, # otherwise the sub-windows are bigger than the main window self.gui_scale = max(0.65, min(1.0, Globals.native_resolution.x / 1920.0)) self.fullscreen_node.set_scale(self.gui_scale) if self.advanced_info: self.exposure_node.set_pos( Globals.native_resolution.x // self.gui_scale - 200, 1, -Globals.native_resolution.y // self.gui_scale + 120) self.hint_reloading.set_pos( float((Globals.native_resolution.x) // 2) / self.gui_scale - 465 // 2, 220) self.keybinding_instructions.set_pos( 30, Globals.native_resolution.y // self.gui_scale - 510.0,) self.overlay_node.set_pos(Globals.base.get_aspect_ratio() - 0.07, 1, 1.0 - 0.07) if self.python_warning: self.python_warning.set_pos( (Globals.native_resolution.x // self.gui_scale - 1054) // 2, (Globals.native_resolution.y // self.gui_scale - 118 - 40)) for text in self.debug_lines: text.set_pixel_size(16 * max(0.8, self.gui_scale)) self.buffer_viewer.center_on_screen() self.pipe_viewer.center_on_screen() self.rm_selector.center_on_screen() def init_keybindings(self): """ Inits the debugger keybindings """ Globals.base.accept("v", self.buffer_viewer.toggle) Globals.base.accept("c", self.pipe_viewer.toggle) Globals.base.accept("z", self.rm_selector.toggle) Globals.base.accept("f5", self.toggle_gui_visible) Globals.base.accept("f6", self.toggle_keybindings_visible) Globals.base.accept("r", self.pipeline.reload_shaders) Globals.base.accept("m", self.start_material_editor) def start_material_editor(self): """ Starts the material editor """ self.debug("Starting material editor") pth = sys.executable editor = os.path.dirname(os.path.realpath(__file__)) editor = os.path.join(editor, "..", "..", "toolkit", "material_editor", "main.py") subprocess.Popen([pth, editor], shell=True) def toggle_gui_visible(self): """ Shows / Hides the gui """ if not self.fullscreen_node.is_hidden(): self.fullscreen_node.hide() self.overlay_node.hide() self.error_msg_handler.hide() else: self.fullscreen_node.show() self.overlay_node.show() self.error_msg_handler.show() def toggle_keybindings_visible(self): """ Shows / Hides the FPS graph """ if not self.keybinding_instructions.is_hidden(): self.keybinding_instructions.hide() else: self.keybinding_instructions.show() def update_stats(self, task=None): """ Updates the stats overlay """ clock = Globals.clock self.debug_lines[0].text = "{:3.0f} fps | {:3.1f} ms | {:3.1f} ms max".format( clock.get_average_frame_rate(), 1000.0 / max(0.001, clock.get_average_frame_rate()), clock.get_max_frame_duration() * 1000.0) if not self.advanced_info: return task.again if task else None text = "{:4d} states | {:4d} transforms " text += "| {:4d} cmds | {:4d} lights | {:4d} shadows " text += "| {:5.1f}% atlas usage" self.debug_lines[1].text = text.format( RenderState.get_num_states(), TransformState.get_num_states(), self.pipeline.light_mgr.cmd_queue.num_processed_commands, self.pipeline.light_mgr.num_lights, self.pipeline.light_mgr.num_shadow_sources, self.pipeline.light_mgr.shadow_atlas_coverage) text = "Internal: {:3.0f} MB VRAM | {:5d} img | {:5d} tex | " text += "{:5d} fbos | {:3d} plugins | {:2d} views ({:2d} active)" tex_memory, tex_count = self.buffer_viewer.stage_information views, active_views = 0, 0 for target in RenderTarget.REGISTERED_TARGETS: if not target.create_default_region: num_regions = target.internal_buffer.get_num_display_regions() for i, region in enumerate(target.internal_buffer.get_display_regions()): # Skip overlay display region if i == 0 and num_regions > 1: continue views += 1 active_views += 1 if target.active and region.active else 0 self.debug_lines[2].text = text.format( tex_memory / (1024**2), len(Image.REGISTERED_IMAGES), tex_count, RenderTarget.NUM_ALLOCATED_BUFFERS, len(self.pipeline.plugin_mgr.enabled_plugins), views, active_views) text = "Scene: {:4.0f} MB VRAM | {:3d} tex | {:4d} geoms " text += "| {:4d} nodes | {:7,.0f} vertices" scene_tex_size = 0 for tex in TexturePool.find_all_textures(): scene_tex_size += tex.estimate_texture_memory() self.debug_lines[3].text = text.format( scene_tex_size / (1024**2), len(TexturePool.find_all_textures()), self.analyzer.get_num_geoms(), self.analyzer.get_num_nodes(), self.analyzer.get_num_vertices(), ) sun_vector = Vec3(0) if self.pipeline.plugin_mgr.is_plugin_enabled("scattering"): sun_vector = self.pipeline.plugin_mgr.instances["scattering"].sun_vector text = "Time: {} ({:1.3f}) | Sun {:0.2f} {:0.2f} {:0.2f}" text += " | X {:3.1f} Y {:3.1f} Z {:3.1f}" text += " | {:2d} tasks | scheduled: {:2d}" self.debug_lines[4].text = text.format( self.pipeline.daytime_mgr.formatted_time, self.pipeline.daytime_mgr.time, sun_vector.x, sun_vector.y, sun_vector.z, Globals.base.camera.get_x(Globals.base.render), Globals.base.camera.get_y(Globals.base.render), Globals.base.camera.get_z(Globals.base.render), self.pipeline.task_scheduler.num_tasks, self.pipeline.task_scheduler.num_scheduled_tasks,) text = "Scene shadows: " if "pssm" in self.pipeline.plugin_mgr.enabled_plugins: focus = self.pipeline.plugin_mgr.instances["pssm"].scene_shadow_stage.last_focus if focus is not None: text += "{:3.1f} {:3.1f} {:3.1f} r {:3.1f}".format( focus[0].x, focus[0].y, focus[0].z, focus[1]) else: text += "none" else: text += "inactive" text += " | HPR ({:3.1f}, {:3.1f}, {:3.1f}) | {:4d} x {:4d} pixels @ {:3.1f} %" text += " | {:3d} x {:3d} tiles" self.debug_lines[5].text = text.format( Globals.base.camera.get_h(Globals.base.render), Globals.base.camera.get_p(Globals.base.render), Globals.base.camera.get_r(Globals.base.render), Globals.native_resolution.x, Globals.native_resolution.y, self.pipeline.settings["pipeline.resolution_scale"] * 100.0, self.pipeline.light_mgr.num_tiles.x, self.pipeline.light_mgr.num_tiles.y,) if task: return task.again
def _render_stages(self): """ Renders the stages to the window """ self._remove_components() entries_per_row = 6 aspect = Globals.native_resolution.y / Globals.native_resolution.x entry_width = 235 entry_height = (entry_width - 20) * aspect + 55 # Store already processed images processed = set() index = -1 # Iterate over all stages for stage_tex in self._stages: if stage_tex in processed: continue processed.add(stage_tex) index += 1 stage_name = stage_tex.get_name() xoffs = index % entries_per_row yoffs = index // entries_per_row node = self._content_node.attach_new_node("Preview") node.set_sz(-1) node.set_pos(10 + xoffs * (entry_width - 14), 1, yoffs * (entry_height - 14 + 10)) r, g, b = 0.2, 0.2, 0.2 if isinstance(stage_tex, Image): r, g, b = 0.2, 0.4, 0.6 stage_name = stage_name.replace("render_pipeline_internal:", "") parts = stage_name.split(":") stage_name = parts[-1] DirectFrame(parent=node, frameSize=(7, entry_width - 17, -7, -entry_height + 17), frameColor=(r, g, b, 1.0), pos=(0, 0, 0)) frame_hover = DirectFrame(parent=node, frameSize=(0, entry_width - 10, 0, -entry_height + 10), frameColor=(0, 0, 0, 0), pos=(0, 0, 0), state=DGG.NORMAL) frame_hover.bind(DGG.ENTER, partial(self._on_texture_hovered, frame_hover)) frame_hover.bind(DGG.EXIT, partial(self._on_texture_blurred, frame_hover)) frame_hover.bind(DGG.B1PRESS, partial(self._on_texture_clicked, stage_tex)) Text(text=stage_name, x=15, y=29, parent=node, size=12, color=Vec3(0.8)) # Scale image so it always fits w, h = stage_tex.get_x_size(), stage_tex.get_y_size() padd_x, padd_y = 24, 57 scale_x = (entry_width - padd_x) / max(1, w) scale_y = (entry_height - padd_y) / max(1, h) scale_factor = min(scale_x, scale_y) if stage_tex.get_texture_type() == Image.TT_buffer_texture: scale_factor = 1 w = entry_width - padd_x h = entry_height - padd_y preview = Sprite(image=stage_tex, w=scale_factor * w, h=scale_factor * h, any_filter=False, parent=node, x=7, y=40, transparent=False) preview.set_shader_input("mipmap", 0) preview.set_shader_input("slice", 0) preview.set_shader_input("brightness", 1) preview.set_shader_input("tonemap", False) preview_shader = DisplayShaderBuilder.build( stage_tex, scale_factor * w, scale_factor * h) preview.set_shader(preview_shader) num_rows = (index + entries_per_row) // entries_per_row self._set_scroll_height(50 + (entry_height - 14 + 10) * num_rows)
def _populate_content(self): # pylint: disable=too-many-branches,too-many-statements """ Reads the pipes and stages from the stage manager and renders those into the window """ self._created = True self._pipe_node = self._content_node.attach_new_node("pipes") self._pipe_node.set_scale(1, 1, -1) self._stage_node = self._content_node.attach_new_node("stages") current_pipes = [] pipe_pixel_size = 3 pipe_height = 100 # Generate stages for offs, stage in enumerate(self._STAGE_MGR.stages): node = self._content_node.attach_new_node("stage") node.set_pos(220 + offs * 200.0, 0, 20) node.set_scale(1, 1, -1) DirectFrame(parent=node, frameSize=(10, 150, 0, -3600), frameColor=(0.2, 0.2, 0.2, 1)) Text(text=str(stage.debug_name.replace("Stage", "")), parent=node, x=20, y=25, size=15) for output_pipe, pipe_tex in iteritems(stage.produced_pipes): pipe_idx = 0 r, g, b = rgb_from_string(output_pipe) if output_pipe in current_pipes: pipe_idx = current_pipes.index(output_pipe) else: current_pipes.append(output_pipe) pipe_idx = len(current_pipes) - 1 DirectFrame(parent=node, frameSize=(0, 8000, pipe_pixel_size / 2, -pipe_pixel_size / 2), frameColor=(r, g, b, 1), pos=(10, 1, -95 - pipe_idx * pipe_height)) w = 160 h = Globals.native_resolution.y /\ float(Globals.native_resolution.x) * w DirectFrame(parent=node, frameSize=(-pipe_pixel_size, w + pipe_pixel_size, h / 2 + pipe_pixel_size, -h / 2 - pipe_pixel_size), frameColor=(r, g, b, 1), pos=(0, 1, -95 - pipe_idx * pipe_height)) if isinstance(pipe_tex, (list, tuple)): pipe_tex = pipe_tex[0] if isinstance(pipe_tex, (SimpleInputBlock, GroupedInputBlock)): icon_file = "/$$rp/data/gui/icon_ubo.png" elif pipe_tex.get_z_size() > 1: icon_file = "/$$rp/data/gui/icon_texture.png" elif pipe_tex.get_texture_type() == Texture.TT_buffer_texture: icon_file = "/$$rp/data/gui/icon_buffer_texture.png" else: icon_file = None preview = Sprite(image=pipe_tex, parent=node, x=0, y=50 + pipe_idx * pipe_height, w=w, h=h, any_filter=False, transparent=False) preview_shader = DisplayShaderBuilder.build( pipe_tex, int(w), int(h)) preview.set_shader(preview_shader) preview.set_shader_inputs(mipmap=0, slice=0, brightness=1, tonemap=False) if icon_file: Sprite(image=icon_file, parent=node, x=55, y=65 + pipe_idx * pipe_height, w=48, h=48, near_filter=False, transparent=True) if isinstance(pipe_tex, (SimpleInputBlock, GroupedInputBlock)): tex_desc = "UBO" else: tex_desc = pipe_tex.format_texture_type( pipe_tex.get_texture_type()) tex_desc += " - " + pipe_tex.format_format( pipe_tex.get_format()).upper() Text(text=tex_desc, parent=node, x=55 + 48 / 2, y=130 + pipe_idx * pipe_height, color=Vec3(0.2), size=12, align="center") for input_pipe in stage.required_pipes: if input_pipe not in current_pipes: self.warn("Pipe not found:", input_pipe) continue idx = current_pipes.index(input_pipe) r, g, b = rgb_from_string(input_pipe) DirectFrame(parent=node, frameSize=(0, 10, 40, -40), frameColor=(r, g, b, 1), pos=(5, 1, -95 - idx * pipe_height)) self._pipe_descriptions = self._content_node.attach_new_node( "PipeDescriptions") self._pipe_descriptions.set_scale(1, 1, -1) DirectFrame(parent=self._pipe_descriptions, frameSize=(0, 190, 0, -5000), frameColor=(0.1, 0.1, 0.1, 1.0)) # Generate the pipe descriptions for idx, pipe in enumerate(current_pipes): r, g, b = rgb_from_string(pipe) DirectFrame(parent=self._pipe_descriptions, frameSize=(0, 180, -95, -135), frameColor=(r, g, b, 1.0), pos=(0, 1, -idx * pipe_height)) Text(parent=self._pipe_descriptions, text=pipe, x=42, y=121 + idx * pipe_height, size=15, color=Vec3(0.1)) Sprite(parent=self._pipe_descriptions, x=9, y=103 + idx * pipe_height, image="/$$rp/data/gui/icon_pipe.png", transparent=True, near_filter=False)
def _populate_content(self): # pylint: disable=too-many-branches,too-many-statements """ Reads the pipes and stages from the stage manager and renders those into the window """ self._created = True self._pipe_node = self._content_node.attach_new_node("pipes") self._pipe_node.set_scale(1, 1, -1) self._stage_node = self._content_node.attach_new_node("stages") current_pipes = [] pipe_pixel_size = 3 pipe_height = 100 # Generate stages for offs, stage in enumerate(self._STAGE_MGR.stages): node = self._content_node.attach_new_node("stage") node.set_pos(220 + offs * 200.0, 0, 20) node.set_scale(1, 1, -1) DirectFrame(parent=node, frameSize=(10, 150, 0, -3600), frameColor=(0.2, 0.2, 0.2, 1)) Text(text=str(stage.debug_name.replace("Stage", "")), parent=node, x=20, y=25, size=15) for output_pipe, pipe_tex in iteritems(stage.produced_pipes): pipe_idx = 0 r, g, b = rgb_from_string(output_pipe) if output_pipe in current_pipes: pipe_idx = current_pipes.index(output_pipe) else: current_pipes.append(output_pipe) pipe_idx = len(current_pipes) - 1 DirectFrame(parent=node, frameSize=(0, 8000, pipe_pixel_size / 2, -pipe_pixel_size / 2), frameColor=(r, g, b, 1), pos=(10, 1, -95 - pipe_idx * pipe_height)) w = 160 h = Globals.native_resolution.y /\ float(Globals.native_resolution.x) * w DirectFrame(parent=node, frameSize=(-pipe_pixel_size, w + pipe_pixel_size, h / 2 + pipe_pixel_size, -h / 2 - pipe_pixel_size), frameColor=(r, g, b, 1), pos=(0, 1, -95 - pipe_idx * pipe_height)) if isinstance(pipe_tex, (list, tuple)): pipe_tex = pipe_tex[0] if isinstance(pipe_tex, (SimpleInputBlock, GroupedInputBlock)): icon_file = "/$$rp/data/gui/icon_ubo.png" elif pipe_tex.get_z_size() > 1: icon_file = "/$$rp/data/gui/icon_texture.png" elif pipe_tex.get_texture_type() == Texture.TT_buffer_texture: icon_file = "/$$rp/data/gui/icon_buffer_texture.png" else: icon_file = None preview = Sprite( image=pipe_tex, parent=node, x=0, y=50 + pipe_idx * pipe_height, w=w, h=h, any_filter=False, transparent=False) preview_shader = DisplayShaderBuilder.build(pipe_tex, int(w), int(h)) preview.set_shader(preview_shader) preview.set_shader_inputs( mipmap=0, slice=0, brightness=1, tonemap=False) if icon_file: Sprite(image=icon_file, parent=node, x=55, y=65 + pipe_idx * pipe_height, w=48, h=48, near_filter=False, transparent=True) if isinstance(pipe_tex, (SimpleInputBlock, GroupedInputBlock)): tex_desc = "UBO" else: tex_desc = pipe_tex.format_texture_type(pipe_tex.get_texture_type()) tex_desc += " - " + pipe_tex.format_format(pipe_tex.get_format()).upper() Text(text=tex_desc, parent=node, x=55 + 48 / 2, y=130 + pipe_idx * pipe_height, color=Vec3(0.2), size=12, align="center") for input_pipe in stage.required_pipes: if input_pipe not in current_pipes: self.warn("Pipe not found:", input_pipe) continue idx = current_pipes.index(input_pipe) r, g, b = rgb_from_string(input_pipe) DirectFrame(parent=node, frameSize=(0, 10, 40, -40), frameColor=(r, g, b, 1), pos=(5, 1, -95 - idx * pipe_height)) self._pipe_descriptions = self._content_node.attach_new_node( "PipeDescriptions") self._pipe_descriptions.set_scale(1, 1, -1) DirectFrame(parent=self._pipe_descriptions, frameSize=(0, 190, 0, -5000), frameColor=(0.1, 0.1, 0.1, 1.0)) # Generate the pipe descriptions for idx, pipe in enumerate(current_pipes): r, g, b = rgb_from_string(pipe) DirectFrame(parent=self._pipe_descriptions, frameSize=(0, 180, -95, -135), frameColor=(r, g, b, 1.0), pos=(0, 1, -idx * pipe_height)) Text(parent=self._pipe_descriptions, text=pipe, x=42, y=121 + idx * pipe_height, size=15, color=Vec3(0.1)) Sprite(parent=self._pipe_descriptions, x=9, y=103 + idx * pipe_height, image="/$$rp/data/gui/icon_pipe.png", transparent=True, near_filter=False)