meltygui 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meltygui/__init__.py +107 -0
- meltygui/accounts/__init__.py +0 -0
- meltygui/accounts/internet_accounts.py +1355 -0
- meltygui/chat/__init__.py +91 -0
- meltygui/chat/activity.py +75 -0
- meltygui/chat/backends.py +36 -0
- meltygui/chat/chat_interface.py +732 -0
- meltygui/chat/chat_proxy.py +352 -0
- meltygui/chat/codex_proxy.py +592 -0
- meltygui/chat/codex_settings.py +100 -0
- meltygui/chat/codex_transport.py +60 -0
- meltygui/chat/command_parser.py +204 -0
- meltygui/chat/images.py +227 -0
- meltygui/chat/messages.py +417 -0
- meltygui/chat/metadata.py +139 -0
- meltygui/chat/writer_locks.py +64 -0
- meltygui/code/__init__.py +0 -0
- meltygui/code/basic_converters.py +533 -0
- meltygui/code/chain_converters.py +2111 -0
- meltygui/code/code_checks.py +2209 -0
- meltygui/code/core_syntax.py +1430 -0
- meltygui/code/file_converters.py +1933 -0
- meltygui/code/fileref.py +702 -0
- meltygui/code/hotswap_guard.py +144 -0
- meltygui/code/libcst_conversion.py +9724 -0
- meltygui/code/live_instrument.py +392 -0
- meltygui/code/live_view.py +2490 -0
- meltygui/code/melty_scan.py +2684 -0
- meltygui/code/new_codecs.py +1255 -0
- meltygui/code/new_converters.py +3017 -0
- meltygui/code/project_code.py +278 -0
- meltygui/code/source_context.py +63 -0
- meltygui/code/symbol_roster.py +1588 -0
- meltygui/code/syntax_check.py +34 -0
- meltygui/code/syntax_check_worker.py +114 -0
- meltygui/completion/__init__.py +0 -0
- meltygui/completion/fim.py +1232 -0
- meltygui/completion/fim_context.py +481 -0
- meltygui/completion/providers/__init__.py +0 -0
- meltygui/completion/providers/anthropic_oauth.py +446 -0
- meltygui/completion/providers/anthropic_requests.py +69 -0
- meltygui/completion/providers/claude.py +203 -0
- meltygui/completion/providers/claude_usage.py +499 -0
- meltygui/completion/providers/codex_accounts.py +180 -0
- meltygui/completion/providers/copilot.py +617 -0
- meltygui/completion/providers/oauth_popup.py +220 -0
- meltygui/completion/providers/ollama.py +320 -0
- meltygui/completion/providers/profiles.py +23 -0
- meltygui/core/README.md +88 -0
- meltygui/core/__init__.py +1 -0
- meltygui/core/automation/__init__.py +1 -0
- meltygui/core/automation/action_core.py +153 -0
- meltygui/core/automation/collection_action.py +45 -0
- meltygui/core/automation/mcp_eval.py +167 -0
- meltygui/core/automation/mcp_hotswap.py +107 -0
- meltygui/core/automation/mcp_query.py +552 -0
- meltygui/core/automation/mcp_server.py +657 -0
- meltygui/core/automation/orchestration_core.py +2636 -0
- meltygui/core/automation/query_core.py +124 -0
- meltygui/core/automation/search_core.py +26 -0
- meltygui/core/automation/selector_core.py +340 -0
- meltygui/core/automation/value_core.py +1752 -0
- meltygui/core/cache/__init__.py +1 -0
- meltygui/core/cache/cache_diagnostics.py +0 -0
- meltygui/core/cache/invalidation_decoration.py +153 -0
- meltygui/core/cache/invalidation_tracker.py +43 -0
- meltygui/core/cache/tile_cache.py +5968 -0
- meltygui/core/conversion/__init__.py +1 -0
- meltygui/core/conversion/bubbling.py +599 -0
- meltygui/core/conversion/cache_tree.py +188 -0
- meltygui/core/conversion/chain.py +113 -0
- meltygui/core/conversion/converter_register.py +145 -0
- meltygui/core/conversion/data_decoration.py +67 -0
- meltygui/core/conversion/dict_conversion.py +1815 -0
- meltygui/core/conversion/dict_conversion_util.py +177 -0
- meltygui/core/conversion/dynamic_obj.py +89 -0
- meltygui/core/conversion/graph_compare.py +183 -0
- meltygui/core/conversion/load_save_v2.py +1032 -0
- meltygui/core/conversion/missing_saved_class.py +39 -0
- meltygui/core/conversion/path_finder.py +606 -0
- meltygui/core/conversion/render_host.py +1083 -0
- meltygui/core/core_render.py +6537 -0
- meltygui/core/definition_hotswap.py +231 -0
- meltygui/core/diagnostics/__init__.py +1 -0
- meltygui/core/diagnostics/attribute_churn.py +38 -0
- meltygui/core/diagnostics/fps_counter.py +39 -0
- meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
- meltygui/core/diagnostics/inspection_core.py +169 -0
- meltygui/core/diagnostics/monitor_core.py +105 -0
- meltygui/core/diagnostics/notifications.py +706 -0
- meltygui/core/diagnostics/perf_trace.py +281 -0
- meltygui/core/diagnostics/profile_decoration.py +88 -0
- meltygui/core/diagnostics/resize_trace.py +62 -0
- meltygui/core/diagnostics/screenshot_core.py +247 -0
- meltygui/core/diagnostics/session_status.py +98 -0
- meltygui/core/diagnostics/trace_core.py +445 -0
- meltygui/core/files/__init__.py +1 -0
- meltygui/core/files/file_core.py +208 -0
- meltygui/core/files/file_explorer_core.py +104 -0
- meltygui/core/files/file_tree_core.py +198 -0
- meltygui/core/files/file_watch_core.py +43 -0
- meltygui/core/files/import_graph_core.py +43 -0
- meltygui/core/files/metadata_core.py +51 -0
- meltygui/core/graphics/__init__.py +1 -0
- meltygui/core/graphics/cuda_context_core.py +166 -0
- meltygui/core/graphics/cuda_interop_core.py +136 -0
- meltygui/core/graphics/cuda_kernel_core.py +91 -0
- meltygui/core/graphics/framebuffer_recorder.py +337 -0
- meltygui/core/graphics/gl_state.py +658 -0
- meltygui/core/graphics/lut_core.py +52 -0
- meltygui/core/graphics/overlay_renderer.py +984 -0
- meltygui/core/graphics/scene_target.py +180 -0
- meltygui/core/graphics/screenshot.py +439 -0
- meltygui/core/graphics/shader_func.py +478 -0
- meltygui/core/graphics/tensor_core.py +45 -0
- meltygui/core/graphics/text_texture.py +329 -0
- meltygui/core/graphics/wayland_color.py +635 -0
- meltygui/core/input/__init__.py +1 -0
- meltygui/core/input/collision.py +165 -0
- meltygui/core/input/drag_drop_core.py +1525 -0
- meltygui/core/input/hypr_left_drag.py +323 -0
- meltygui/core/input/input_core.py +245 -0
- meltygui/core/input/input_handler.py +1101 -0
- meltygui/core/input/mouse_cursor.py +355 -0
- meltygui/core/input/pynput_backend.py +1054 -0
- meltygui/core/input/space_mouse.py +338 -0
- meltygui/core/input/touchpad_backend.py +393 -0
- meltygui/core/input/view_selection.py +177 -0
- meltygui/core/layout/__init__.py +1 -0
- meltygui/core/layout/column_core.py +2153 -0
- meltygui/core/layout/cursor_core.py +161 -0
- meltygui/core/layout/dropdown_core.py +278 -0
- meltygui/core/layout/edge_constraints.py +155 -0
- meltygui/core/layout/grid_core.py +117 -0
- meltygui/core/layout/header_core.py +23 -0
- meltygui/core/layout/header_runtime.py +67 -0
- meltygui/core/layout/layout_core.py +87 -0
- meltygui/core/layout/tile_manager_core.py +591 -0
- meltygui/core/melty.py +6726 -0
- meltygui/core/module_map.json +896 -0
- meltygui/core/module_names.py +19 -0
- meltygui/core/rendering/__init__.py +1 -0
- meltygui/core/rendering/core_decoration.py +431 -0
- meltygui/core/rendering/core_render_helpers.py +328 -0
- meltygui/core/rendering/func_metadata.py +398 -0
- meltygui/core/rendering/mode.py +818 -0
- meltygui/core/rendering/mode_defaults.py +41 -0
- meltygui/core/rendering/modes.py +136 -0
- meltygui/core/rendering/parameter_core.py +1665 -0
- meltygui/core/rendering/render_dispatch.py +1891 -0
- meltygui/core/rendering/render_funcs.py +273 -0
- meltygui/core/rendering/shaped.py +312 -0
- meltygui/core/rendering/window_decoration.py +25 -0
- meltygui/core/runtime/__init__.py +1 -0
- meltygui/core/runtime/app.py +735 -0
- meltygui/core/runtime/app_session.py +140 -0
- meltygui/core/runtime/background.py +564 -0
- meltygui/core/runtime/extensions.py +53 -0
- meltygui/core/runtime/gc_manager.py +1163 -0
- meltygui/core/runtime/lifecycle.py +21 -0
- meltygui/core/runtime/paths.py +27 -0
- meltygui/core/runtime/settings.py +14 -0
- meltygui/core/runtime/singleton.py +16 -0
- meltygui/core/runtime/thread_safe_bool.py +24 -0
- meltygui/core/runtime/thread_signal.py +30 -0
- meltygui/core/runtime/toggles.py +3115 -0
- meltygui/core/services/__init__.py +1 -0
- meltygui/core/services/account_core.py +10 -0
- meltygui/core/services/chat_core.py +19 -0
- meltygui/core/services/claude_terminal_core.py +346 -0
- meltygui/core/services/terminal_core.py +458 -0
- meltygui/core/services/terminal_runtime.py +78 -0
- meltygui/core/styling/__init__.py +1 -0
- meltygui/core/styling/color_core.py +46 -0
- meltygui/core/styling/fonts.py +639 -0
- meltygui/core/styling/global_style.py +338 -0
- meltygui/core/styling/style.py +198 -0
- meltygui/core/styling/style_core.py +522 -0
- meltygui/core/styling/warm_start.py +149 -0
- meltygui/core/windowing/__init__.py +1 -0
- meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
- meltygui/core/windowing/backends/__init__.py +1 -0
- meltygui/core/windowing/backends/imgui_renderer.py +138 -0
- meltygui/core/windowing/backends/native_wayland.py +850 -0
- meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
- meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
- meltygui/core/windowing/backends/wayland_protocol.py +101 -0
- meltygui/core/windowing/dock_core.py +182 -0
- meltygui/core/windowing/frame_geometry.py +42 -0
- meltygui/core/windowing/geometry_feed.py +864 -0
- meltygui/core/windowing/glfw_utils.py +1343 -0
- meltygui/core/windowing/os_frame.py +1552 -0
- meltygui/core/windowing/surface.py +643 -0
- meltygui/core/windowing/titlebar.py +1560 -0
- meltygui/core/windowing/titlebar_buttons.py +281 -0
- meltygui/core/windowing/wayland_move.py +932 -0
- meltygui/core/windowing/window_api.py +62 -0
- meltygui/core/windowing/window_constants.py +339 -0
- meltygui/core/windowing/window_visibility.py +162 -0
- meltygui/debug/__init__.py +0 -0
- meltygui/debug/app_view_utils.py +9 -0
- meltygui/editor/__init__.py +0 -0
- meltygui/editor/bash_syntax.py +30 -0
- meltygui/editor/code_line_fast.py +152 -0
- meltygui/editor/diff.py +139 -0
- meltygui/editor/external_changes.py +159 -0
- meltygui/editor/file_header.py +41 -0
- meltygui/editor/live_usage.py +169 -0
- meltygui/editor/live_view_views.py +1803 -0
- meltygui/editor/pending_save.py +1351 -0
- meltygui/editor/roster_tints.py +547 -0
- meltygui/editor/source_preview.py +16 -0
- meltygui/editor/source_tools.py +10 -0
- meltygui/editor/source_ui.py +70 -0
- meltygui/editor/spell_check.py +77 -0
- meltygui/editor/text_editor.py +9076 -0
- meltygui/editor/usage_picker.py +538 -0
- meltygui/events/__init__.py +0 -0
- meltygui/events/example.py +118 -0
- meltygui/examples/__init__.py +0 -0
- meltygui/examples/columns_demo.py +33 -0
- meltygui/examples/columns_window_demo.py +60 -0
- meltygui/examples/context_menu_demo.py +24 -0
- meltygui/examples/context_menu_window_demo.py +49 -0
- meltygui/examples/gui_playground.py +127 -0
- meltygui/examples/live_view_playground.py +256 -0
- meltygui/examples/lora.py +43 -0
- meltygui/examples/lora_data.py +59 -0
- meltygui/examples/lora_policies.py +67 -0
- meltygui/examples/mode_demo.py +49 -0
- meltygui/examples/modifies_demo.py +119 -0
- meltygui/examples/scalar_policies.py +60 -0
- meltygui/examples/style_layouts.py +149 -0
- meltygui/examples/tile_manager_demo.py +74 -0
- meltygui/examples/tint_demo.py +160 -0
- meltygui/examples/tint_functions.py +56 -0
- meltygui/examples/trace_demo.py +88 -0
- meltygui/examples/two_windows.py +36 -0
- meltygui/files/__init__.py +0 -0
- meltygui/files/fast_file_explorer.py +439 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
- meltygui/graphics/__init__.py +6 -0
- meltygui/graphics/base.py +85 -0
- meltygui/graphics/examples.py +507 -0
- meltygui/graphics/executor.py +520 -0
- meltygui/graphics/filter.py +667 -0
- meltygui/graphics/filter.pyi +856 -0
- meltygui/graphics/generate_stubs.py +22 -0
- meltygui/graphics/registry.py +203 -0
- meltygui/graphics/shader_compiler.py +155 -0
- meltygui/graphics/shaders.py +1302 -0
- meltygui/graphics/stub_generator.py +250 -0
- meltygui/graphics/texture_manager.py +170 -0
- meltygui/graphics/texture_min_max.py +295 -0
- meltygui/hdr_color.py +757 -0
- meltygui/image_load.py +308 -0
- meltygui/model/__init__.py +1 -0
- meltygui/model/account_model.py +142 -0
- meltygui/model/camera_model.py +159 -0
- meltygui/model/chat_model.py +86 -0
- meltygui/model/code_model.py +62 -0
- meltygui/model/code_proxy_model.py +876 -0
- meltygui/model/collection_model.py +41 -0
- meltygui/model/color_model.py +127 -0
- meltygui/model/cuda_tensor_model.py +36 -0
- meltygui/model/cuda_texture_model.py +149 -0
- meltygui/model/dropdown_model.py +137 -0
- meltygui/model/file_metadata_model.py +73 -0
- meltygui/model/file_model.py +289 -0
- meltygui/model/format_model.py +390 -0
- meltygui/model/graph_model.py +98 -0
- meltygui/model/icon_model.py +1024 -0
- meltygui/model/import_graph_model.py +468 -0
- meltygui/model/layout_model.py +15 -0
- meltygui/model/lut_model.py +266 -0
- meltygui/model/search_model.py +173 -0
- meltygui/model/tensor_model.py +402 -0
- meltygui/model/terminal_model.py +329 -0
- meltygui/model/texture_model.py +146 -0
- meltygui/model/tile_model.py +24 -0
- meltygui/model/trace_model.py +198 -0
- meltygui/model/trace_report_model.py +146 -0
- meltygui/models/__init__.py +0 -0
- meltygui/models/file_meta.py +613 -0
- meltygui/models/function_console.py +184 -0
- meltygui/models/orchestration.py +48 -0
- meltygui/pbr.py +1576 -0
- meltygui/png_unfilter.c +49 -0
- meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
- meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
- meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
- meltygui/resources/dejavu/LICENSE.txt +78 -0
- meltygui/resources/fontawesome-LICENSE.txt +121 -0
- meltygui/resources/fontawesome-webfont.ttf +0 -0
- meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
- meltygui/resources/jetbrains-weights/OFL.txt +93 -0
- meltygui/resources/jetbrains-weights/README.md +2 -0
- meltygui/state/__init__.py +0 -0
- meltygui/state/account_state.py +22 -0
- meltygui/state/animation_state.py +97 -0
- meltygui/state/annotation_state.py +22 -0
- meltygui/state/chat_state.py +36 -0
- meltygui/state/code_state.py +10 -0
- meltygui/state/core_enums.py +59 -0
- meltygui/state/core_markers.py +115 -0
- meltygui/state/core_undo.py +1075 -0
- meltygui/state/file_state.py +75 -0
- meltygui/state/graph_state.py +26 -0
- meltygui/state/inspection_state.py +47 -0
- meltygui/state/menu_state.py +10 -0
- meltygui/state/model_enums.py +11 -0
- meltygui/state/new_core_model.py +2394 -0
- meltygui/state/orchestration_state.py +14 -0
- meltygui/state/query_state.py +24 -0
- meltygui/state/tensor_state.py +10 -0
- meltygui/state/terminal_state.py +23 -0
- meltygui/state/trace_state.py +32 -0
- meltygui/state/voxel_state.py +12 -0
- meltygui/text_index.py +816 -0
- meltygui/utils/__init__.py +0 -0
- meltygui/utils/jump_to_code.py +344 -0
- meltygui/utils/pkl_inspect.py +90 -0
- meltygui/utils/render_utils.py +1419 -0
- meltygui/view/__init__.py +1 -0
- meltygui/view/account_view.py +524 -0
- meltygui/view/action_view.py +73 -0
- meltygui/view/chat_decoration_view.py +112 -0
- meltygui/view/chat_view.py +1703 -0
- meltygui/view/code_view.py +2677 -0
- meltygui/view/collection_view.py +1185 -0
- meltygui/view/color_view.py +824 -0
- meltygui/view/control_view.py +559 -0
- meltygui/view/decoration_view.py +439 -0
- meltygui/view/diagnostic_view.py +177 -0
- meltygui/view/dropdown_view.py +1082 -0
- meltygui/view/file_view.py +1599 -0
- meltygui/view/graph_cuda_view.py +105 -0
- meltygui/view/graph_view.py +882 -0
- meltygui/view/header_view.py +848 -0
- meltygui/view/input_view.py +238 -0
- meltygui/view/inspection_view.py +1783 -0
- meltygui/view/layout_view.py +596 -0
- meltygui/view/lut_view.py +45 -0
- meltygui/view/menu_view.py +212 -0
- meltygui/view/orchestration_view.py +658 -0
- meltygui/view/query_view.py +118 -0
- meltygui/view/search_view.py +342 -0
- meltygui/view/tab_view.py +258 -0
- meltygui/view/tensor_view.py +430 -0
- meltygui/view/terminal_view.py +355 -0
- meltygui/view/text_view.py +8031 -0
- meltygui/view/texture_view.py +480 -0
- meltygui/view/tile_view.py +38 -0
- meltygui/view/trace_view.py +923 -0
- meltygui/view/voxel_cuda_view.py +824 -0
- meltygui/view/voxel_view.py +1860 -0
- meltygui/view/window_view.py +111 -0
- meltygui-0.1.0.dist-info/METADATA +143 -0
- meltygui-0.1.0.dist-info/RECORD +372 -0
- meltygui-0.1.0.dist-info/WHEEL +4 -0
- meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
"""@shader_func — kwargs-driven GLSL uniform injection.
|
|
2
|
+
|
|
3
|
+
Melty's parameter injection means a value travels by NAME from the call site
|
|
4
|
+
into whatever declares that name. shader_func extends the same idea into the
|
|
5
|
+
shader: the GLSL never declares plumbing uniforms — any kwarg whose name
|
|
6
|
+
appears as an identifier in a stage's source gets a `uniform <type> <name>;`
|
|
7
|
+
spliced in (type inferred from the Python value), and is set every call via
|
|
8
|
+
the program's own reflection table.
|
|
9
|
+
|
|
10
|
+
VOXEL_FRAG = '''
|
|
11
|
+
#version 330 core
|
|
12
|
+
out vec4 FragColor;
|
|
13
|
+
void main() { FragColor = vec4(uv * brightness, tilt, 1.0); }
|
|
14
|
+
'''
|
|
15
|
+
|
|
16
|
+
@shader_func(fragment=VOXEL_FRAG)
|
|
17
|
+
def voxel_pass(gl_state: GLState = None, brightness=0.64, tilt=0.0,
|
|
18
|
+
volume_texture=None, **kwargs):
|
|
19
|
+
# program is already bound, uniforms already set — body is draw calls
|
|
20
|
+
gl.glBindVertexArray(gl_state.vao("fs_triangle"))
|
|
21
|
+
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3)
|
|
22
|
+
|
|
23
|
+
# inside a @render_func, with gl_state auto-injected:
|
|
24
|
+
voxel_pass(gl_state, brightness=brightness, tilt=tilt)
|
|
25
|
+
|
|
26
|
+
How it decides what to declare/set, per call:
|
|
27
|
+
- candidate values = body signature defaults overlaid with call kwargs
|
|
28
|
+
- a candidate becomes a uniform iff its name appears in a stage's
|
|
29
|
+
(comment-stripped) identifiers AND its value maps to a GLSL type
|
|
30
|
+
(infer_glsl_type — strings/objects return None and are simply body kwargs)
|
|
31
|
+
- already-declared uniforms in the source are never re-declared, just set
|
|
32
|
+
- setting is driven by glGetActiveUniform reflection, so over-matching is
|
|
33
|
+
harmless: a name the compiler optimized out has no location and is skipped
|
|
34
|
+
|
|
35
|
+
There is deliberately no GLSL parser here. The transformation is purely
|
|
36
|
+
additive (declarations after the #version/#extension block) and the GL
|
|
37
|
+
compiler is the validator — its errors are surfaced with line numbers
|
|
38
|
+
remapped to the original source.
|
|
39
|
+
|
|
40
|
+
Compile lifecycle rides GLState.get with deps = the generated sources:
|
|
41
|
+
editing the GLSL (hotswap re-runs the decorator) changes the deps, so the
|
|
42
|
+
program recompiles next call and the old one is queued for deletion. A failed
|
|
43
|
+
compile keeps the last good program running (GLState's last-good semantics),
|
|
44
|
+
stores the remapped log on `.last_error`, and doesn't retry until the source
|
|
45
|
+
actually changes.
|
|
46
|
+
|
|
47
|
+
Threading: GL is main-thread-only in this app. Off the main thread (chain
|
|
48
|
+
converters, Background runs) a shader_func call is a silent no-op returning
|
|
49
|
+
None.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
import inspect
|
|
53
|
+
import re
|
|
54
|
+
|
|
55
|
+
import numpy as np
|
|
56
|
+
import OpenGL.GL as gl
|
|
57
|
+
|
|
58
|
+
from meltygui.core.graphics.gl_state import GLState
|
|
59
|
+
from meltygui.core.graphics.gl_state import GLTexture
|
|
60
|
+
from meltygui.core.graphics.gl_state import _scalar
|
|
61
|
+
from meltygui.core.graphics.gl_state import is_gl_thread
|
|
62
|
+
from meltygui.model.texture_model import TextureId
|
|
63
|
+
|
|
64
|
+
# Fullscreen triangle for fragment-only shader_funcs (no VBO needed; core
|
|
65
|
+
# profile still requires a VAO bound - gl_state.vao(key) provides an empty one).
|
|
66
|
+
DEFAULT_VERTEX_FULLSCREEN = """
|
|
67
|
+
#version 330 core
|
|
68
|
+
out vec2 uv;
|
|
69
|
+
void main() {
|
|
70
|
+
vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
71
|
+
uv = pos;
|
|
72
|
+
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
|
|
73
|
+
}
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ShaderError(RuntimeError):
|
|
78
|
+
"""Shader compile/link failure, message already remapped to original
|
|
79
|
+
source line numbers."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ── GLSL source analysis (pure logic, unit-tested without GL) ────────────
|
|
83
|
+
|
|
84
|
+
_DIRECTIVE_RE = re.compile(r"^\s*#\s*(version|extension)\b")
|
|
85
|
+
_IDENT_RE = re.compile(r"\b[A-Za-z_]\w*\b")
|
|
86
|
+
# `uniform ... name;` - possibly prefixed by layout/precision qualifiers,
|
|
87
|
+
# possibly an array. [^;{}]* keeps us out of interface blocks. Multi-declarator
|
|
88
|
+
# lines (`uniform float a, b;`) only register the last name - declare such
|
|
89
|
+
# uniforms yourself.
|
|
90
|
+
_UNIFORM_DECL_RE = re.compile(r"\buniform\b[^;{}]*?(\w+)\s*(?:\[[^\]]*\])?\s*;")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def strip_comments(source):
|
|
94
|
+
"""GLSL minus // and /* */ comments, line structure preserved (GLSL has
|
|
95
|
+
no string literals, so this can't over-strip)."""
|
|
96
|
+
source = re.sub(r"/\*.*?\*/", lambda m: "\n" * m.group(0).count("\n"), source, flags=re.S)
|
|
97
|
+
return re.sub(r"//[^\n]*", "", source)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def glsl_identifiers(source):
|
|
101
|
+
return frozenset(_IDENT_RE.findall(strip_comments(source)))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def declared_uniform_names(source):
|
|
105
|
+
return frozenset(m.group(1) for m in _UNIFORM_DECL_RE.finditer(strip_comments(source)))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def infer_glsl_type(value):
|
|
109
|
+
"""GLSL uniform type for a Python value, or None if the value isn't
|
|
110
|
+
uniform-able (strings, objects, None — those stay plain body kwargs)."""
|
|
111
|
+
if isinstance(value, (bool, np.bool_)):
|
|
112
|
+
return "bool"
|
|
113
|
+
if isinstance(value, (int, np.integer)):
|
|
114
|
+
return "int"
|
|
115
|
+
if isinstance(value, (float, np.floating)):
|
|
116
|
+
return "float"
|
|
117
|
+
# Name-compared (not isinstance) so textures created before a hotswap of
|
|
118
|
+
# gl_state.py keep working the same logic as set_default's type check.
|
|
119
|
+
if type(value).__name__ == "GLTexture" or isinstance(value, TextureId):
|
|
120
|
+
if value.target == int(gl.GL_TEXTURE_3D):
|
|
121
|
+
return "sampler3D"
|
|
122
|
+
if value.target == int(gl.GL_TEXTURE_1D):
|
|
123
|
+
return "sampler1D"
|
|
124
|
+
if value.target == int(gl.GL_TEXTURE_CUBE_MAP):
|
|
125
|
+
return "samplerCube"
|
|
126
|
+
return "sampler2D"
|
|
127
|
+
tname = type(value).__name__
|
|
128
|
+
if type(value).__module__ == "glm":
|
|
129
|
+
if tname.startswith("d"): # dvec3/dmat4x4 - setter upcasts to f32
|
|
130
|
+
tname = tname[1:]
|
|
131
|
+
if tname in ("vec2", "vec3", "vec4",
|
|
132
|
+
"ivec2", "ivec3", "ivec4", "bvec2", "bvec3", "bvec4"):
|
|
133
|
+
return tname
|
|
134
|
+
square = re.fullmatch(r"mat(\d)(?:x(\d))?", tname)
|
|
135
|
+
if square and (square.group(2) is None or square.group(2) == square.group(1)):
|
|
136
|
+
return f"mat{square.group(1)}"
|
|
137
|
+
return None
|
|
138
|
+
if isinstance(value, (tuple, list)):
|
|
139
|
+
if 2 <= len(value) <= 4 and all(isinstance(v, (int, float, np.integer, np.floating))
|
|
140
|
+
for v in value):
|
|
141
|
+
return f"vec{len(value)}"
|
|
142
|
+
return None
|
|
143
|
+
if isinstance(value, np.ndarray):
|
|
144
|
+
if value.ndim == 1 and 2 <= value.shape[0] <= 4:
|
|
145
|
+
return f"vec{value.shape[0]}"
|
|
146
|
+
if value.ndim == 2 and value.shape[0] == value.shape[1] and 2 <= value.shape[0] <= 4:
|
|
147
|
+
return f"mat{value.shape[0]}"
|
|
148
|
+
return None
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def inject_uniforms(source, decls):
|
|
153
|
+
"""Splice `uniform <type> <name>;` lines (sorted, for stable deps hashing)
|
|
154
|
+
right after the #version/#extension block. Returns (generated_source,
|
|
155
|
+
insert_line_index, n_inserted_lines) — the last two drive error-log line
|
|
156
|
+
remapping."""
|
|
157
|
+
lines = source.split("\n")
|
|
158
|
+
idx = 0
|
|
159
|
+
for i, line in enumerate(lines):
|
|
160
|
+
if _DIRECTIVE_RE.match(line):
|
|
161
|
+
idx = i + 1
|
|
162
|
+
if not decls:
|
|
163
|
+
return source, idx, 0
|
|
164
|
+
block = ["// shader_func: auto-injected uniforms"]
|
|
165
|
+
block += [f"uniform {glsl_type} {name};" for name, glsl_type in sorted(decls)]
|
|
166
|
+
return "\n".join(lines[:idx] + block + lines[idx:]), idx, len(block)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _remap_log(log, insert_line, n_inserted):
|
|
170
|
+
"""Rewrite driver error line numbers (NVIDIA `0(57)`, Mesa `0:57(8)`)
|
|
171
|
+
back to the original, pre-injection source."""
|
|
172
|
+
if n_inserted == 0:
|
|
173
|
+
return log
|
|
174
|
+
|
|
175
|
+
def fix(ln):
|
|
176
|
+
ln = int(ln)
|
|
177
|
+
if ln > insert_line + n_inserted:
|
|
178
|
+
return str(ln - n_inserted)
|
|
179
|
+
if ln > insert_line:
|
|
180
|
+
return f"{ln}<injected uniform block>"
|
|
181
|
+
return str(ln)
|
|
182
|
+
|
|
183
|
+
new, count = re.subn(r"(\d+):(\d+)(\()",
|
|
184
|
+
lambda m: f"{m.group(1)}:{fix(m.group(2))}{m.group(3)}", log)
|
|
185
|
+
if count:
|
|
186
|
+
return new
|
|
187
|
+
new, _ = re.subn(r"(\d+)\((\d+)\)",
|
|
188
|
+
lambda m: f"{m.group(1)}({fix(m.group(2))})", log)
|
|
189
|
+
return new
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# ── compile / link / set ──────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
def _compile_stage(kind, enum, generated, insert_line, n_inserted):
|
|
195
|
+
shader = _scalar(gl.glCreateShader(enum))
|
|
196
|
+
gl.glShaderSource(shader, generated)
|
|
197
|
+
gl.glCompileShader(shader)
|
|
198
|
+
if not gl.glGetShaderiv(shader, gl.GL_COMPILE_STATUS):
|
|
199
|
+
log = gl.glGetShaderInfoLog(shader)
|
|
200
|
+
gl.glDeleteShader(shader)
|
|
201
|
+
log = log.decode(errors="replace") if isinstance(log, bytes) else str(log)
|
|
202
|
+
raise ShaderError(f"{kind} shader: {_remap_log(log.strip(), insert_line, n_inserted)}")
|
|
203
|
+
return shader
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _link_program(stage_shaders):
|
|
207
|
+
program = _scalar(gl.glCreateProgram())
|
|
208
|
+
for shader in stage_shaders:
|
|
209
|
+
gl.glAttachShader(program, shader)
|
|
210
|
+
gl.glLinkProgram(program)
|
|
211
|
+
for shader in stage_shaders:
|
|
212
|
+
gl.glDetachShader(program, shader)
|
|
213
|
+
gl.glDeleteShader(shader)
|
|
214
|
+
if not gl.glGetProgramiv(program, gl.GL_LINK_STATUS):
|
|
215
|
+
log = gl.glGetProgramInfoLog(program)
|
|
216
|
+
gl.glDeleteProgram(program)
|
|
217
|
+
log = log.decode(errors="replace") if isinstance(log, bytes) else str(log)
|
|
218
|
+
raise ShaderError(f"link: {log.strip()}")
|
|
219
|
+
return program
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
_SAMPLER_TARGETS = {
|
|
223
|
+
int(gl.GL_SAMPLER_1D): gl.GL_TEXTURE_1D,
|
|
224
|
+
int(gl.GL_SAMPLER_2D): gl.GL_TEXTURE_2D,
|
|
225
|
+
int(gl.GL_SAMPLER_3D): gl.GL_TEXTURE_3D,
|
|
226
|
+
int(gl.GL_SAMPLER_CUBE): gl.GL_TEXTURE_CUBE_MAP,
|
|
227
|
+
int(gl.GL_SAMPLER_2D_ARRAY): gl.GL_TEXTURE_2D_ARRAY,
|
|
228
|
+
int(gl.GL_SAMPLER_2D_SHADOW): gl.GL_TEXTURE_2D, # depth texture (pbr shadow maps)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def reflect_uniforms(program):
|
|
233
|
+
"""{name: (location, gl_type, size)} for every active uniform — the
|
|
234
|
+
ground truth for what to set. Anything the compiler optimized out simply
|
|
235
|
+
isn't here."""
|
|
236
|
+
out = {}
|
|
237
|
+
count = _scalar(gl.glGetProgramiv(program, gl.GL_ACTIVE_UNIFORMS))
|
|
238
|
+
for i in range(count):
|
|
239
|
+
name, size, utype = gl.glGetActiveUniform(program, i)
|
|
240
|
+
name = name.decode(errors="replace") if isinstance(name, bytes) else str(name)
|
|
241
|
+
name = name.split("[")[0]
|
|
242
|
+
loc = gl.glGetUniformLocation(program, name)
|
|
243
|
+
if loc >= 0:
|
|
244
|
+
out[name] = (int(loc), int(utype), int(size))
|
|
245
|
+
return out
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _as_f32(v):
|
|
249
|
+
return np.ascontiguousarray(v, dtype=np.float32)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _as_i32(v):
|
|
253
|
+
return np.ascontiguousarray(v, dtype=np.int32)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _set_matrix(fn, loc, val):
|
|
257
|
+
if type(val).__module__ == "glm":
|
|
258
|
+
# PyGLM uses the buffer protocol with column-major memory: upload
|
|
259
|
+
# as-is. Row-major numpy math matrices need the transpose flag.
|
|
260
|
+
fn(loc, 1, gl.GL_FALSE, np.asarray(val, dtype=np.float32))
|
|
261
|
+
else:
|
|
262
|
+
fn(loc, 1, gl.GL_TRUE, _as_f32(val))
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
_SETTERS = {
|
|
266
|
+
int(gl.GL_FLOAT): lambda loc, v: gl.glUniform1f(loc, float(v)),
|
|
267
|
+
int(gl.GL_INT): lambda loc, v: gl.glUniform1i(loc, int(v)),
|
|
268
|
+
int(gl.GL_UNSIGNED_INT): lambda loc, v: gl.glUniform1ui(loc, int(v)),
|
|
269
|
+
int(gl.GL_BOOL): lambda loc, v: gl.glUniform1i(loc, 1 if v else 0),
|
|
270
|
+
int(gl.GL_FLOAT_VEC2): lambda loc, v: gl.glUniform2fv(loc, 1, _as_f32(v)),
|
|
271
|
+
int(gl.GL_FLOAT_VEC3): lambda loc, v: gl.glUniform3fv(loc, 1, _as_f32(v)),
|
|
272
|
+
int(gl.GL_FLOAT_VEC4): lambda loc, v: gl.glUniform4fv(loc, 1, _as_f32(v)),
|
|
273
|
+
int(gl.GL_INT_VEC2): lambda loc, v: gl.glUniform2iv(loc, 1, _as_i32(v)),
|
|
274
|
+
int(gl.GL_INT_VEC3): lambda loc, v: gl.glUniform3iv(loc, 1, _as_i32(v)),
|
|
275
|
+
int(gl.GL_INT_VEC4): lambda loc, v: gl.glUniform4iv(loc, 1, _as_i32(v)),
|
|
276
|
+
int(gl.GL_FLOAT_MAT2): lambda loc, v: _set_matrix(gl.glUniformMatrix2fv, loc, v),
|
|
277
|
+
int(gl.GL_FLOAT_MAT3): lambda loc, v: _set_matrix(gl.glUniformMatrix3fv, loc, v),
|
|
278
|
+
int(gl.GL_FLOAT_MAT4): lambda loc, v: _set_matrix(gl.glUniformMatrix4fv, loc, v),
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class _Stage:
|
|
283
|
+
__slots__ = ("kind", "enum", "source", "identifiers", "predeclared")
|
|
284
|
+
|
|
285
|
+
def __init__(self, kind, enum, source):
|
|
286
|
+
self.kind = kind
|
|
287
|
+
self.enum = enum
|
|
288
|
+
self.source = source
|
|
289
|
+
self.identifiers = glsl_identifiers(source)
|
|
290
|
+
self.predeclared = declared_uniform_names(source)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class _ProgramRecord:
|
|
294
|
+
__slots__ = ("program", "uniforms", "sampler_units")
|
|
295
|
+
|
|
296
|
+
def __init__(self, program):
|
|
297
|
+
self.program = program
|
|
298
|
+
self.uniforms = reflect_uniforms(program)
|
|
299
|
+
# Texture units assigned by sorted uniform name - deterministic across
|
|
300
|
+
# recompiles, so a given sampler keeps its unit as the source evolves.
|
|
301
|
+
self.sampler_units = {
|
|
302
|
+
name: unit for unit, name in enumerate(sorted(
|
|
303
|
+
n for n, (_, t, _s) in self.uniforms.items() if t in _SAMPLER_TARGETS))
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class ShaderFunc:
|
|
308
|
+
|
|
309
|
+
def __init__(self, func, fragment, vertex=None, name=None):
|
|
310
|
+
if not fragment:
|
|
311
|
+
raise TypeError("@shader_func requires fragment GLSL: @shader_func(fragment=...)")
|
|
312
|
+
self._func = func
|
|
313
|
+
self._name = name or f"{func.__module__}.{func.__qualname__}"
|
|
314
|
+
self.__name__ = getattr(func, "__name__", self._name)
|
|
315
|
+
self.__doc__ = getattr(func, "__doc__", None)
|
|
316
|
+
self.stages = [
|
|
317
|
+
_Stage("vertex", gl.GL_VERTEX_SHADER, vertex or DEFAULT_VERTEX_FULLSCREEN),
|
|
318
|
+
_Stage("fragment", gl.GL_FRAGMENT_SHADER, fragment),
|
|
319
|
+
]
|
|
320
|
+
|
|
321
|
+
sig = inspect.signature(func)
|
|
322
|
+
self._wants_var_kwargs = any(p.kind is inspect.Parameter.VAR_KEYWORD
|
|
323
|
+
for p in sig.parameters.values())
|
|
324
|
+
self._body_params = {n for n, p in sig.parameters.items()
|
|
325
|
+
if p.kind not in (inspect.Parameter.VAR_KEYWORD,
|
|
326
|
+
inspect.Parameter.VAR_POSITIONAL)}
|
|
327
|
+
self._defaults = {n: p.default for n, p in sig.parameters.items()
|
|
328
|
+
if p.default is not inspect.Parameter.empty}
|
|
329
|
+
self._skip = {"gl_state", "program"}
|
|
330
|
+
|
|
331
|
+
self._gen_cache = {} # tuple of decl tuples -> [(src, idx, n)]
|
|
332
|
+
# Failure latch is per (deps, gl_state): a bad source is retried at
|
|
333
|
+
# most ONCE PER GLSTATE - never globally. A global latch starved every
|
|
334
|
+
# window created after one transient failure (no last-good record →
|
|
335
|
+
# silent None → permanently empty view) while older windows kept
|
|
336
|
+
# rendering on cached programs.
|
|
337
|
+
self._failed_deps = None
|
|
338
|
+
self._failed_states = set() # id(gl_state) that has tried _failed_deps
|
|
339
|
+
self.last_error = None
|
|
340
|
+
self.last_generated = {} # stage kind -> generated source (debugging)
|
|
341
|
+
|
|
342
|
+
def _generated_stages(self, stage_decls):
|
|
343
|
+
key = tuple(tuple(sorted(d)) for d in stage_decls)
|
|
344
|
+
gen = self._gen_cache.get(key)
|
|
345
|
+
if gen is None:
|
|
346
|
+
gen = [inject_uniforms(stage.source, decls)
|
|
347
|
+
for stage, decls in zip(self.stages, stage_decls)]
|
|
348
|
+
self._gen_cache[key] = gen
|
|
349
|
+
return gen
|
|
350
|
+
|
|
351
|
+
def __call__(self, gl_state: GLState = None, **kwargs):
|
|
352
|
+
# Duck-typed (hotswap of gl_state.py leaves live instances of the
|
|
353
|
+
# old class, so typing would spuriously fail).
|
|
354
|
+
if gl_state is None or not (hasattr(gl_state, "get") and hasattr(gl_state, "peek")):
|
|
355
|
+
raise TypeError(
|
|
356
|
+
f"shader_func {self.__name__!r} needs a GLState: declare "
|
|
357
|
+
f"`gl_state: GLState = None` on the enclosing @render_func and "
|
|
358
|
+
f"forward it — {self.__name__}(gl_state, ...)")
|
|
359
|
+
if not is_gl_thread():
|
|
360
|
+
return None # Render mode / Background thread - no GL here
|
|
361
|
+
|
|
362
|
+
merged = {**self._defaults, **kwargs}
|
|
363
|
+
|
|
364
|
+
# Which candidates become uniforms, per-stage.
|
|
365
|
+
stage_decls = [[] for _ in self.stages]
|
|
366
|
+
values = {}
|
|
367
|
+
for kw_name, val in merged.items():
|
|
368
|
+
if kw_name in self._skip:
|
|
369
|
+
continue
|
|
370
|
+
glsl_type = infer_glsl_type(val)
|
|
371
|
+
if glsl_type is None:
|
|
372
|
+
continue
|
|
373
|
+
used = False
|
|
374
|
+
for stage, decls in zip(self.stages, stage_decls):
|
|
375
|
+
if kw_name in stage.identifiers:
|
|
376
|
+
used = True
|
|
377
|
+
if kw_name not in stage.predeclared:
|
|
378
|
+
decls.append((kw_name, glsl_type))
|
|
379
|
+
if used:
|
|
380
|
+
values[kw_name] = val
|
|
381
|
+
|
|
382
|
+
gen = self._generated_stages(stage_decls)
|
|
383
|
+
self.last_generated = {stage.kind: g[0] for stage, g in zip(self.stages, gen)}
|
|
384
|
+
deps = tuple(g[0] for g in gen)
|
|
385
|
+
key = ("shader_func", self._name)
|
|
386
|
+
|
|
387
|
+
def create():
|
|
388
|
+
compiled = []
|
|
389
|
+
try:
|
|
390
|
+
for stage, (src, idx, n) in zip(self.stages, gen):
|
|
391
|
+
compiled.append(_compile_stage(stage.kind, stage.enum, src, idx, n))
|
|
392
|
+
except Exception:
|
|
393
|
+
for shader in compiled:
|
|
394
|
+
gl.glDeleteShader(shader)
|
|
395
|
+
raise
|
|
396
|
+
return _ProgramRecord(_link_program(compiled))
|
|
397
|
+
|
|
398
|
+
def delete(rec):
|
|
399
|
+
gl.glDeleteProgram(rec.program)
|
|
400
|
+
|
|
401
|
+
if deps == self._failed_deps and id(gl_state) in self._failed_states:
|
|
402
|
+
rec = gl_state.peek(key) # known failure FOR THIS STATE: no retry
|
|
403
|
+
else:
|
|
404
|
+
try:
|
|
405
|
+
rec = gl_state.get(key, create, delete, deps=deps)
|
|
406
|
+
self.last_error = None
|
|
407
|
+
self._failed_deps = None
|
|
408
|
+
self._failed_states.clear()
|
|
409
|
+
except ShaderError as e:
|
|
410
|
+
if str(e) != self.last_error:
|
|
411
|
+
print(f"[shader_func] {self._name}: {e}")
|
|
412
|
+
self.last_error = str(e)
|
|
413
|
+
if deps != self._failed_deps:
|
|
414
|
+
self._failed_deps = deps
|
|
415
|
+
self._failed_states = set()
|
|
416
|
+
self._failed_states.add(id(gl_state))
|
|
417
|
+
rec = gl_state.peek(key) # last good program, if any
|
|
418
|
+
if rec is None:
|
|
419
|
+
return None
|
|
420
|
+
|
|
421
|
+
prev_program = _scalar(gl.glGetIntegerv(gl.GL_CURRENT_PROGRAM))
|
|
422
|
+
gl.glUseProgram(rec.program)
|
|
423
|
+
try:
|
|
424
|
+
for uname, val in values.items():
|
|
425
|
+
info = rec.uniforms.get(uname)
|
|
426
|
+
if info is None:
|
|
427
|
+
continue # optimized out (or only matched a non-uniform use)
|
|
428
|
+
loc, utype, _size = info
|
|
429
|
+
target = _SAMPLER_TARGETS.get(utype)
|
|
430
|
+
if target is not None:
|
|
431
|
+
unit = rec.sampler_units.get(uname, 0)
|
|
432
|
+
gl.glActiveTexture(gl.GL_TEXTURE0 + unit)
|
|
433
|
+
if type(val).__name__ == "GLTexture":
|
|
434
|
+
gl.glBindTexture(val.target, val.texture_id)
|
|
435
|
+
else:
|
|
436
|
+
# Raw texture id for a predeclared sampler - bind
|
|
437
|
+
# to the target implied by the sampler type.
|
|
438
|
+
gl.glBindTexture(target, int(val))
|
|
439
|
+
gl.glUniform1i(loc, unit)
|
|
440
|
+
continue
|
|
441
|
+
setter = _SETTERS.get(utype)
|
|
442
|
+
if setter is None:
|
|
443
|
+
continue
|
|
444
|
+
try:
|
|
445
|
+
setter(loc, val)
|
|
446
|
+
except Exception as e:
|
|
447
|
+
print(f"[shader_func] {self._name}: setting uniform "
|
|
448
|
+
f"{uname!r} from {type(val).__name__} failed: {e}")
|
|
449
|
+
|
|
450
|
+
body_kwargs = dict(merged)
|
|
451
|
+
body_kwargs["gl_state"] = gl_state
|
|
452
|
+
body_kwargs["program"] = rec.program
|
|
453
|
+
if not self._wants_var_kwargs:
|
|
454
|
+
body_kwargs = {k: v for k, v in body_kwargs.items()
|
|
455
|
+
if k in self._body_params}
|
|
456
|
+
return self._func(**body_kwargs)
|
|
457
|
+
finally:
|
|
458
|
+
gl.glBindVertexArray(0)
|
|
459
|
+
gl.glActiveTexture(gl.GL_TEXTURE0)
|
|
460
|
+
gl.glUseProgram(prev_program)
|
|
461
|
+
|
|
462
|
+
def __repr__(self):
|
|
463
|
+
state = f"error: {self.last_error.splitlines()[0]}" if self.last_error else "ok"
|
|
464
|
+
return f"ShaderFunc({self._name}, {state})"
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def shader_func(fragment=None, vertex=None, name=None):
|
|
468
|
+
"""Decorator: `@shader_func(fragment=FRAG)` or
|
|
469
|
+
`@shader_func(fragment=FRAG, vertex=VERT)`. With no vertex source the
|
|
470
|
+
fullscreen-triangle vertex stage is used (body: bind an empty VAO via
|
|
471
|
+
gl_state.vao(key) and glDrawArrays(GL_TRIANGLES, 0, 3))."""
|
|
472
|
+
if callable(fragment):
|
|
473
|
+
raise TypeError("@shader_func needs GLSL — use @shader_func(fragment=...)")
|
|
474
|
+
|
|
475
|
+
def deco(func):
|
|
476
|
+
return ShaderFunc(func, fragment=fragment, vertex=vertex, name=name)
|
|
477
|
+
|
|
478
|
+
return deco
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Tensor core functions and supporting definitions."""
|
|
2
|
+
from meltygui.core.core_render import release_input_refs
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _voxels_cleanup(draw_state):
|
|
6
|
+
"""Melty.cleanup hook (@render_func(on_cleanup=…)) for draw_voxels: sever
|
|
7
|
+
every reference this draw_state holds to the source tensor and the
|
|
8
|
+
uploaded volume, so the session teardown's torch.cuda.empty_cache() can
|
|
9
|
+
actually return the VRAM. Three places hold it:
|
|
10
|
+
* gl_state — the 3-D texture / CUDA-registered PBO ("volume" /
|
|
11
|
+
"volume_cuda") and the label atlas: release() queues them for the
|
|
12
|
+
GL-thread delete the teardown flushes;
|
|
13
|
+
* the wrapper's input slots (_input_value & co.) — the tensor the view
|
|
14
|
+
rendered; release_input_refs resets them;
|
|
15
|
+
* anything else tensor-shaped that landed on the draw_state (misc or a
|
|
16
|
+
plain attribute) — scrubbed generically rather than by name, so a new
|
|
17
|
+
field can't silently pin a volume.
|
|
18
|
+
The draw_state itself survives (registry entries persist across a restart-
|
|
19
|
+
in-place); a re-render refills everything."""
|
|
20
|
+
from meltygui.model.tensor_model import _is_tensorish
|
|
21
|
+
|
|
22
|
+
misc = getattr(draw_state, "misc", None)
|
|
23
|
+
if isinstance(misc, dict):
|
|
24
|
+
gl_state = misc.get("gl_state")
|
|
25
|
+
if gl_state is not None:
|
|
26
|
+
try:
|
|
27
|
+
gl_state.release()
|
|
28
|
+
except Exception as e:
|
|
29
|
+
print(f"[draw_voxels] gl_state release failed: {e!r}")
|
|
30
|
+
for k in [k for k, v in misc.items() if _is_tensorish(v)]:
|
|
31
|
+
misc.pop(k, None)
|
|
32
|
+
release_input_refs(draw_state)
|
|
33
|
+
for k, v in list(vars(draw_state).items()):
|
|
34
|
+
if k != "misc" and _is_tensorish(v):
|
|
35
|
+
setattr(draw_state, k, None)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def source_identity(src):
|
|
39
|
+
"""Cache identity of a tensor source: id + in-place version + the live
|
|
40
|
+
publish generation (live_view._stamp_publish_gen). id()/_version alone
|
|
41
|
+
collide across runs — the released previous generation's address is
|
|
42
|
+
reused by the next run's tensor, _version 0 on both — and the cached
|
|
43
|
+
texture of the OLD run was served for the new value."""
|
|
44
|
+
from meltygui.code.live_view import publish_gen
|
|
45
|
+
return (id(src), getattr(src, "_version", 0), publish_gen(src))
|