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.
Files changed (372) hide show
  1. meltygui/__init__.py +107 -0
  2. meltygui/accounts/__init__.py +0 -0
  3. meltygui/accounts/internet_accounts.py +1355 -0
  4. meltygui/chat/__init__.py +91 -0
  5. meltygui/chat/activity.py +75 -0
  6. meltygui/chat/backends.py +36 -0
  7. meltygui/chat/chat_interface.py +732 -0
  8. meltygui/chat/chat_proxy.py +352 -0
  9. meltygui/chat/codex_proxy.py +592 -0
  10. meltygui/chat/codex_settings.py +100 -0
  11. meltygui/chat/codex_transport.py +60 -0
  12. meltygui/chat/command_parser.py +204 -0
  13. meltygui/chat/images.py +227 -0
  14. meltygui/chat/messages.py +417 -0
  15. meltygui/chat/metadata.py +139 -0
  16. meltygui/chat/writer_locks.py +64 -0
  17. meltygui/code/__init__.py +0 -0
  18. meltygui/code/basic_converters.py +533 -0
  19. meltygui/code/chain_converters.py +2111 -0
  20. meltygui/code/code_checks.py +2209 -0
  21. meltygui/code/core_syntax.py +1430 -0
  22. meltygui/code/file_converters.py +1933 -0
  23. meltygui/code/fileref.py +702 -0
  24. meltygui/code/hotswap_guard.py +144 -0
  25. meltygui/code/libcst_conversion.py +9724 -0
  26. meltygui/code/live_instrument.py +392 -0
  27. meltygui/code/live_view.py +2490 -0
  28. meltygui/code/melty_scan.py +2684 -0
  29. meltygui/code/new_codecs.py +1255 -0
  30. meltygui/code/new_converters.py +3017 -0
  31. meltygui/code/project_code.py +278 -0
  32. meltygui/code/source_context.py +63 -0
  33. meltygui/code/symbol_roster.py +1588 -0
  34. meltygui/code/syntax_check.py +34 -0
  35. meltygui/code/syntax_check_worker.py +114 -0
  36. meltygui/completion/__init__.py +0 -0
  37. meltygui/completion/fim.py +1232 -0
  38. meltygui/completion/fim_context.py +481 -0
  39. meltygui/completion/providers/__init__.py +0 -0
  40. meltygui/completion/providers/anthropic_oauth.py +446 -0
  41. meltygui/completion/providers/anthropic_requests.py +69 -0
  42. meltygui/completion/providers/claude.py +203 -0
  43. meltygui/completion/providers/claude_usage.py +499 -0
  44. meltygui/completion/providers/codex_accounts.py +180 -0
  45. meltygui/completion/providers/copilot.py +617 -0
  46. meltygui/completion/providers/oauth_popup.py +220 -0
  47. meltygui/completion/providers/ollama.py +320 -0
  48. meltygui/completion/providers/profiles.py +23 -0
  49. meltygui/core/README.md +88 -0
  50. meltygui/core/__init__.py +1 -0
  51. meltygui/core/automation/__init__.py +1 -0
  52. meltygui/core/automation/action_core.py +153 -0
  53. meltygui/core/automation/collection_action.py +45 -0
  54. meltygui/core/automation/mcp_eval.py +167 -0
  55. meltygui/core/automation/mcp_hotswap.py +107 -0
  56. meltygui/core/automation/mcp_query.py +552 -0
  57. meltygui/core/automation/mcp_server.py +657 -0
  58. meltygui/core/automation/orchestration_core.py +2636 -0
  59. meltygui/core/automation/query_core.py +124 -0
  60. meltygui/core/automation/search_core.py +26 -0
  61. meltygui/core/automation/selector_core.py +340 -0
  62. meltygui/core/automation/value_core.py +1752 -0
  63. meltygui/core/cache/__init__.py +1 -0
  64. meltygui/core/cache/cache_diagnostics.py +0 -0
  65. meltygui/core/cache/invalidation_decoration.py +153 -0
  66. meltygui/core/cache/invalidation_tracker.py +43 -0
  67. meltygui/core/cache/tile_cache.py +5968 -0
  68. meltygui/core/conversion/__init__.py +1 -0
  69. meltygui/core/conversion/bubbling.py +599 -0
  70. meltygui/core/conversion/cache_tree.py +188 -0
  71. meltygui/core/conversion/chain.py +113 -0
  72. meltygui/core/conversion/converter_register.py +145 -0
  73. meltygui/core/conversion/data_decoration.py +67 -0
  74. meltygui/core/conversion/dict_conversion.py +1815 -0
  75. meltygui/core/conversion/dict_conversion_util.py +177 -0
  76. meltygui/core/conversion/dynamic_obj.py +89 -0
  77. meltygui/core/conversion/graph_compare.py +183 -0
  78. meltygui/core/conversion/load_save_v2.py +1032 -0
  79. meltygui/core/conversion/missing_saved_class.py +39 -0
  80. meltygui/core/conversion/path_finder.py +606 -0
  81. meltygui/core/conversion/render_host.py +1083 -0
  82. meltygui/core/core_render.py +6537 -0
  83. meltygui/core/definition_hotswap.py +231 -0
  84. meltygui/core/diagnostics/__init__.py +1 -0
  85. meltygui/core/diagnostics/attribute_churn.py +38 -0
  86. meltygui/core/diagnostics/fps_counter.py +39 -0
  87. meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
  88. meltygui/core/diagnostics/inspection_core.py +169 -0
  89. meltygui/core/diagnostics/monitor_core.py +105 -0
  90. meltygui/core/diagnostics/notifications.py +706 -0
  91. meltygui/core/diagnostics/perf_trace.py +281 -0
  92. meltygui/core/diagnostics/profile_decoration.py +88 -0
  93. meltygui/core/diagnostics/resize_trace.py +62 -0
  94. meltygui/core/diagnostics/screenshot_core.py +247 -0
  95. meltygui/core/diagnostics/session_status.py +98 -0
  96. meltygui/core/diagnostics/trace_core.py +445 -0
  97. meltygui/core/files/__init__.py +1 -0
  98. meltygui/core/files/file_core.py +208 -0
  99. meltygui/core/files/file_explorer_core.py +104 -0
  100. meltygui/core/files/file_tree_core.py +198 -0
  101. meltygui/core/files/file_watch_core.py +43 -0
  102. meltygui/core/files/import_graph_core.py +43 -0
  103. meltygui/core/files/metadata_core.py +51 -0
  104. meltygui/core/graphics/__init__.py +1 -0
  105. meltygui/core/graphics/cuda_context_core.py +166 -0
  106. meltygui/core/graphics/cuda_interop_core.py +136 -0
  107. meltygui/core/graphics/cuda_kernel_core.py +91 -0
  108. meltygui/core/graphics/framebuffer_recorder.py +337 -0
  109. meltygui/core/graphics/gl_state.py +658 -0
  110. meltygui/core/graphics/lut_core.py +52 -0
  111. meltygui/core/graphics/overlay_renderer.py +984 -0
  112. meltygui/core/graphics/scene_target.py +180 -0
  113. meltygui/core/graphics/screenshot.py +439 -0
  114. meltygui/core/graphics/shader_func.py +478 -0
  115. meltygui/core/graphics/tensor_core.py +45 -0
  116. meltygui/core/graphics/text_texture.py +329 -0
  117. meltygui/core/graphics/wayland_color.py +635 -0
  118. meltygui/core/input/__init__.py +1 -0
  119. meltygui/core/input/collision.py +165 -0
  120. meltygui/core/input/drag_drop_core.py +1525 -0
  121. meltygui/core/input/hypr_left_drag.py +323 -0
  122. meltygui/core/input/input_core.py +245 -0
  123. meltygui/core/input/input_handler.py +1101 -0
  124. meltygui/core/input/mouse_cursor.py +355 -0
  125. meltygui/core/input/pynput_backend.py +1054 -0
  126. meltygui/core/input/space_mouse.py +338 -0
  127. meltygui/core/input/touchpad_backend.py +393 -0
  128. meltygui/core/input/view_selection.py +177 -0
  129. meltygui/core/layout/__init__.py +1 -0
  130. meltygui/core/layout/column_core.py +2153 -0
  131. meltygui/core/layout/cursor_core.py +161 -0
  132. meltygui/core/layout/dropdown_core.py +278 -0
  133. meltygui/core/layout/edge_constraints.py +155 -0
  134. meltygui/core/layout/grid_core.py +117 -0
  135. meltygui/core/layout/header_core.py +23 -0
  136. meltygui/core/layout/header_runtime.py +67 -0
  137. meltygui/core/layout/layout_core.py +87 -0
  138. meltygui/core/layout/tile_manager_core.py +591 -0
  139. meltygui/core/melty.py +6726 -0
  140. meltygui/core/module_map.json +896 -0
  141. meltygui/core/module_names.py +19 -0
  142. meltygui/core/rendering/__init__.py +1 -0
  143. meltygui/core/rendering/core_decoration.py +431 -0
  144. meltygui/core/rendering/core_render_helpers.py +328 -0
  145. meltygui/core/rendering/func_metadata.py +398 -0
  146. meltygui/core/rendering/mode.py +818 -0
  147. meltygui/core/rendering/mode_defaults.py +41 -0
  148. meltygui/core/rendering/modes.py +136 -0
  149. meltygui/core/rendering/parameter_core.py +1665 -0
  150. meltygui/core/rendering/render_dispatch.py +1891 -0
  151. meltygui/core/rendering/render_funcs.py +273 -0
  152. meltygui/core/rendering/shaped.py +312 -0
  153. meltygui/core/rendering/window_decoration.py +25 -0
  154. meltygui/core/runtime/__init__.py +1 -0
  155. meltygui/core/runtime/app.py +735 -0
  156. meltygui/core/runtime/app_session.py +140 -0
  157. meltygui/core/runtime/background.py +564 -0
  158. meltygui/core/runtime/extensions.py +53 -0
  159. meltygui/core/runtime/gc_manager.py +1163 -0
  160. meltygui/core/runtime/lifecycle.py +21 -0
  161. meltygui/core/runtime/paths.py +27 -0
  162. meltygui/core/runtime/settings.py +14 -0
  163. meltygui/core/runtime/singleton.py +16 -0
  164. meltygui/core/runtime/thread_safe_bool.py +24 -0
  165. meltygui/core/runtime/thread_signal.py +30 -0
  166. meltygui/core/runtime/toggles.py +3115 -0
  167. meltygui/core/services/__init__.py +1 -0
  168. meltygui/core/services/account_core.py +10 -0
  169. meltygui/core/services/chat_core.py +19 -0
  170. meltygui/core/services/claude_terminal_core.py +346 -0
  171. meltygui/core/services/terminal_core.py +458 -0
  172. meltygui/core/services/terminal_runtime.py +78 -0
  173. meltygui/core/styling/__init__.py +1 -0
  174. meltygui/core/styling/color_core.py +46 -0
  175. meltygui/core/styling/fonts.py +639 -0
  176. meltygui/core/styling/global_style.py +338 -0
  177. meltygui/core/styling/style.py +198 -0
  178. meltygui/core/styling/style_core.py +522 -0
  179. meltygui/core/styling/warm_start.py +149 -0
  180. meltygui/core/windowing/__init__.py +1 -0
  181. meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
  182. meltygui/core/windowing/backends/__init__.py +1 -0
  183. meltygui/core/windowing/backends/imgui_renderer.py +138 -0
  184. meltygui/core/windowing/backends/native_wayland.py +850 -0
  185. meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
  186. meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
  187. meltygui/core/windowing/backends/wayland_protocol.py +101 -0
  188. meltygui/core/windowing/dock_core.py +182 -0
  189. meltygui/core/windowing/frame_geometry.py +42 -0
  190. meltygui/core/windowing/geometry_feed.py +864 -0
  191. meltygui/core/windowing/glfw_utils.py +1343 -0
  192. meltygui/core/windowing/os_frame.py +1552 -0
  193. meltygui/core/windowing/surface.py +643 -0
  194. meltygui/core/windowing/titlebar.py +1560 -0
  195. meltygui/core/windowing/titlebar_buttons.py +281 -0
  196. meltygui/core/windowing/wayland_move.py +932 -0
  197. meltygui/core/windowing/window_api.py +62 -0
  198. meltygui/core/windowing/window_constants.py +339 -0
  199. meltygui/core/windowing/window_visibility.py +162 -0
  200. meltygui/debug/__init__.py +0 -0
  201. meltygui/debug/app_view_utils.py +9 -0
  202. meltygui/editor/__init__.py +0 -0
  203. meltygui/editor/bash_syntax.py +30 -0
  204. meltygui/editor/code_line_fast.py +152 -0
  205. meltygui/editor/diff.py +139 -0
  206. meltygui/editor/external_changes.py +159 -0
  207. meltygui/editor/file_header.py +41 -0
  208. meltygui/editor/live_usage.py +169 -0
  209. meltygui/editor/live_view_views.py +1803 -0
  210. meltygui/editor/pending_save.py +1351 -0
  211. meltygui/editor/roster_tints.py +547 -0
  212. meltygui/editor/source_preview.py +16 -0
  213. meltygui/editor/source_tools.py +10 -0
  214. meltygui/editor/source_ui.py +70 -0
  215. meltygui/editor/spell_check.py +77 -0
  216. meltygui/editor/text_editor.py +9076 -0
  217. meltygui/editor/usage_picker.py +538 -0
  218. meltygui/events/__init__.py +0 -0
  219. meltygui/events/example.py +118 -0
  220. meltygui/examples/__init__.py +0 -0
  221. meltygui/examples/columns_demo.py +33 -0
  222. meltygui/examples/columns_window_demo.py +60 -0
  223. meltygui/examples/context_menu_demo.py +24 -0
  224. meltygui/examples/context_menu_window_demo.py +49 -0
  225. meltygui/examples/gui_playground.py +127 -0
  226. meltygui/examples/live_view_playground.py +256 -0
  227. meltygui/examples/lora.py +43 -0
  228. meltygui/examples/lora_data.py +59 -0
  229. meltygui/examples/lora_policies.py +67 -0
  230. meltygui/examples/mode_demo.py +49 -0
  231. meltygui/examples/modifies_demo.py +119 -0
  232. meltygui/examples/scalar_policies.py +60 -0
  233. meltygui/examples/style_layouts.py +149 -0
  234. meltygui/examples/tile_manager_demo.py +74 -0
  235. meltygui/examples/tint_demo.py +160 -0
  236. meltygui/examples/tint_functions.py +56 -0
  237. meltygui/examples/trace_demo.py +88 -0
  238. meltygui/examples/two_windows.py +36 -0
  239. meltygui/files/__init__.py +0 -0
  240. meltygui/files/fast_file_explorer.py +439 -0
  241. meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
  242. meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
  243. meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
  244. meltygui/graphics/__init__.py +6 -0
  245. meltygui/graphics/base.py +85 -0
  246. meltygui/graphics/examples.py +507 -0
  247. meltygui/graphics/executor.py +520 -0
  248. meltygui/graphics/filter.py +667 -0
  249. meltygui/graphics/filter.pyi +856 -0
  250. meltygui/graphics/generate_stubs.py +22 -0
  251. meltygui/graphics/registry.py +203 -0
  252. meltygui/graphics/shader_compiler.py +155 -0
  253. meltygui/graphics/shaders.py +1302 -0
  254. meltygui/graphics/stub_generator.py +250 -0
  255. meltygui/graphics/texture_manager.py +170 -0
  256. meltygui/graphics/texture_min_max.py +295 -0
  257. meltygui/hdr_color.py +757 -0
  258. meltygui/image_load.py +308 -0
  259. meltygui/model/__init__.py +1 -0
  260. meltygui/model/account_model.py +142 -0
  261. meltygui/model/camera_model.py +159 -0
  262. meltygui/model/chat_model.py +86 -0
  263. meltygui/model/code_model.py +62 -0
  264. meltygui/model/code_proxy_model.py +876 -0
  265. meltygui/model/collection_model.py +41 -0
  266. meltygui/model/color_model.py +127 -0
  267. meltygui/model/cuda_tensor_model.py +36 -0
  268. meltygui/model/cuda_texture_model.py +149 -0
  269. meltygui/model/dropdown_model.py +137 -0
  270. meltygui/model/file_metadata_model.py +73 -0
  271. meltygui/model/file_model.py +289 -0
  272. meltygui/model/format_model.py +390 -0
  273. meltygui/model/graph_model.py +98 -0
  274. meltygui/model/icon_model.py +1024 -0
  275. meltygui/model/import_graph_model.py +468 -0
  276. meltygui/model/layout_model.py +15 -0
  277. meltygui/model/lut_model.py +266 -0
  278. meltygui/model/search_model.py +173 -0
  279. meltygui/model/tensor_model.py +402 -0
  280. meltygui/model/terminal_model.py +329 -0
  281. meltygui/model/texture_model.py +146 -0
  282. meltygui/model/tile_model.py +24 -0
  283. meltygui/model/trace_model.py +198 -0
  284. meltygui/model/trace_report_model.py +146 -0
  285. meltygui/models/__init__.py +0 -0
  286. meltygui/models/file_meta.py +613 -0
  287. meltygui/models/function_console.py +184 -0
  288. meltygui/models/orchestration.py +48 -0
  289. meltygui/pbr.py +1576 -0
  290. meltygui/png_unfilter.c +49 -0
  291. meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
  292. meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
  293. meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
  294. meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  295. meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
  296. meltygui/resources/dejavu/LICENSE.txt +78 -0
  297. meltygui/resources/fontawesome-LICENSE.txt +121 -0
  298. meltygui/resources/fontawesome-webfont.ttf +0 -0
  299. meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
  300. meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
  301. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
  302. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
  303. meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
  304. meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
  305. meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
  306. meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
  307. meltygui/resources/jetbrains-weights/OFL.txt +93 -0
  308. meltygui/resources/jetbrains-weights/README.md +2 -0
  309. meltygui/state/__init__.py +0 -0
  310. meltygui/state/account_state.py +22 -0
  311. meltygui/state/animation_state.py +97 -0
  312. meltygui/state/annotation_state.py +22 -0
  313. meltygui/state/chat_state.py +36 -0
  314. meltygui/state/code_state.py +10 -0
  315. meltygui/state/core_enums.py +59 -0
  316. meltygui/state/core_markers.py +115 -0
  317. meltygui/state/core_undo.py +1075 -0
  318. meltygui/state/file_state.py +75 -0
  319. meltygui/state/graph_state.py +26 -0
  320. meltygui/state/inspection_state.py +47 -0
  321. meltygui/state/menu_state.py +10 -0
  322. meltygui/state/model_enums.py +11 -0
  323. meltygui/state/new_core_model.py +2394 -0
  324. meltygui/state/orchestration_state.py +14 -0
  325. meltygui/state/query_state.py +24 -0
  326. meltygui/state/tensor_state.py +10 -0
  327. meltygui/state/terminal_state.py +23 -0
  328. meltygui/state/trace_state.py +32 -0
  329. meltygui/state/voxel_state.py +12 -0
  330. meltygui/text_index.py +816 -0
  331. meltygui/utils/__init__.py +0 -0
  332. meltygui/utils/jump_to_code.py +344 -0
  333. meltygui/utils/pkl_inspect.py +90 -0
  334. meltygui/utils/render_utils.py +1419 -0
  335. meltygui/view/__init__.py +1 -0
  336. meltygui/view/account_view.py +524 -0
  337. meltygui/view/action_view.py +73 -0
  338. meltygui/view/chat_decoration_view.py +112 -0
  339. meltygui/view/chat_view.py +1703 -0
  340. meltygui/view/code_view.py +2677 -0
  341. meltygui/view/collection_view.py +1185 -0
  342. meltygui/view/color_view.py +824 -0
  343. meltygui/view/control_view.py +559 -0
  344. meltygui/view/decoration_view.py +439 -0
  345. meltygui/view/diagnostic_view.py +177 -0
  346. meltygui/view/dropdown_view.py +1082 -0
  347. meltygui/view/file_view.py +1599 -0
  348. meltygui/view/graph_cuda_view.py +105 -0
  349. meltygui/view/graph_view.py +882 -0
  350. meltygui/view/header_view.py +848 -0
  351. meltygui/view/input_view.py +238 -0
  352. meltygui/view/inspection_view.py +1783 -0
  353. meltygui/view/layout_view.py +596 -0
  354. meltygui/view/lut_view.py +45 -0
  355. meltygui/view/menu_view.py +212 -0
  356. meltygui/view/orchestration_view.py +658 -0
  357. meltygui/view/query_view.py +118 -0
  358. meltygui/view/search_view.py +342 -0
  359. meltygui/view/tab_view.py +258 -0
  360. meltygui/view/tensor_view.py +430 -0
  361. meltygui/view/terminal_view.py +355 -0
  362. meltygui/view/text_view.py +8031 -0
  363. meltygui/view/texture_view.py +480 -0
  364. meltygui/view/tile_view.py +38 -0
  365. meltygui/view/trace_view.py +923 -0
  366. meltygui/view/voxel_cuda_view.py +824 -0
  367. meltygui/view/voxel_view.py +1860 -0
  368. meltygui/view/window_view.py +111 -0
  369. meltygui-0.1.0.dist-info/METADATA +143 -0
  370. meltygui-0.1.0.dist-info/RECORD +372 -0
  371. meltygui-0.1.0.dist-info/WHEEL +4 -0
  372. meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,295 @@
1
+ import numpy as np
2
+ from OpenGL.GL import *
3
+ from OpenGL.GL import shaders
4
+ import ctypes
5
+
6
+ # Module-level state
7
+ _program = None
8
+ _ssbo = None
9
+ _ssbo_size = 0
10
+
11
+ # Stage-2 (final reduction) state. _reduce_program collapses the per-workgroup
12
+ # partials into a single (min, max) written into a 1x1 RGBA32F texture via
13
+ # imageStore, so the value never leaves the GPU (no glMapBufferRange readback /
14
+ # CPU-GPU sync stall). _result_textures holds one such texture per input texture
15
+ # id - keyed so two normalize calls in the same frame don't write/read the same
16
+ # result texel.
17
+ _reduce_program = None
18
+ _result_textures = {}
19
+
20
+ _COMPUTE_SHADER_SOURCE = """
21
+ #version 430
22
+ layout(local_size_x = 16, local_size_y = 16) in;
23
+
24
+ uniform sampler2D inputTexture;
25
+
26
+ layout(std430, binding = 1) buffer ResultBuffer {
27
+ vec2 workgroupResults[]; // Each workgroup writes (min, max)
28
+ };
29
+
30
+ uniform ivec2 numWorkgroups;
31
+
32
+ shared float localMin[256];
33
+ shared float localMax[256];
34
+
35
+ void main() {
36
+ ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
37
+ ivec2 size = textureSize(inputTexture, 0);
38
+ uint lid = gl_LocalInvocationIndex;
39
+
40
+ // Initialize with extreme values
41
+ float myMin = 1e38;
42
+ float myMax = -1e38;
43
+
44
+ if (pos.x < size.x && pos.y < size.y) {
45
+ vec4 pixel = texelFetch(inputTexture, pos, 0);
46
+ // Get min/max across RGB channels (ignore alpha)
47
+ myMin = min(min(pixel.r, pixel.g), pixel.b);
48
+ myMax = max(max(pixel.r, pixel.g), pixel.b);
49
+ }
50
+
51
+ localMin[lid] = myMin;
52
+ localMax[lid] = myMax;
53
+ barrier();
54
+
55
+ // Parallel reduction in shared memory
56
+ for (uint s = 128; s > 0; s >>= 1) {
57
+ if (lid < s) {
58
+ localMin[lid] = min(localMin[lid], localMin[lid + s]);
59
+ localMax[lid] = max(localMax[lid], localMax[lid + s]);
60
+ }
61
+ barrier();
62
+ }
63
+
64
+ // Workgroup leader writes result
65
+ if (lid == 0) {
66
+ uint workgroupIndex = gl_WorkGroupID.y * numWorkgroups.x + gl_WorkGroupID.x;
67
+ workgroupResults[workgroupIndex] = vec2(localMin[0], localMax[0]);
68
+ }
69
+ }
70
+ """
71
+
72
+ # Stage 2: a single workgroup reduces the per-workgroup partials (binding 1, the
73
+ # same SSBO stage 1 filled) down to one (min, max) and stores it into the 0,0
74
+ # texel of a 1x1 RGBA32F texture. The normalize_remap fragment shader just samples
75
+ # that texel - the range is read straight off the GPU, never mapped to the CPU.
76
+ _REDUCE_SHADER_SOURCE = """
77
+ #version 430
78
+ layout(local_size_x = 256) in;
79
+
80
+ layout(std430, binding = 1) readonly buffer ResultBuffer {
81
+ vec2 workgroupResults[];
82
+ };
83
+
84
+ layout(rgba32f, binding = 0) writeonly uniform image2D outImage;
85
+
86
+ uniform int count;
87
+
88
+ shared float localMin[256];
89
+ shared float localMax[256];
90
+
91
+ void main() {
92
+ uint lid = gl_LocalInvocationIndex;
93
+
94
+ float myMin = 1e38;
95
+ float myMax = -1e38;
96
+ for (uint i = lid; i < uint(count); i += 256u) {
97
+ myMin = min(myMin, workgroupResults[i].x);
98
+ myMax = max(myMax, workgroupResults[i].y);
99
+ }
100
+
101
+ localMin[lid] = myMin;
102
+ localMax[lid] = myMax;
103
+ barrier();
104
+
105
+ for (uint s = 128u; s > 0u; s >>= 1u) {
106
+ if (lid < s) {
107
+ localMin[lid] = min(localMin[lid], localMin[lid + s]);
108
+ localMax[lid] = max(localMax[lid], localMax[lid + s]);
109
+ }
110
+ barrier();
111
+ }
112
+
113
+ if (lid == 0u) {
114
+ imageStore(outImage, ivec2(0, 0), vec4(localMin[0], localMax[0], 0.0, 1.0));
115
+ }
116
+ }
117
+ """
118
+
119
+
120
+ def _ensure_initialized():
121
+ """Lazy initialization of shader and buffer."""
122
+ global _program, _ssbo, _ssbo_size
123
+
124
+ if _program is not None:
125
+ return
126
+
127
+ compute_shader = shaders.compileShader(_COMPUTE_SHADER_SOURCE, GL_COMPUTE_SHADER)
128
+ _program = shaders.compileProgram(compute_shader)
129
+ _ssbo = glGenBuffers(1)
130
+ _ssbo_size = 0
131
+
132
+
133
+ def _ensure_reduce_initialized():
134
+ """Lazy initialization of the stage-2 reduction program."""
135
+ global _reduce_program
136
+
137
+ if _reduce_program is not None:
138
+ return
139
+
140
+ reduce_shader = shaders.compileShader(_REDUCE_SHADER_SOURCE, GL_COMPUTE_SHADER)
141
+ _reduce_program = shaders.compileProgram(reduce_shader)
142
+
143
+
144
+ def _dispatch_stage1(texture_id: int) -> tuple[int, int]:
145
+ """Run the per-workgroup reduction into the SSBO (no readback).
146
+
147
+ Returns (total_workgroups, required_size) describing the SSBO contents, so
148
+ callers can either map it (CPU path) or reduce it further on the GPU.
149
+ """
150
+ global _ssbo_size
151
+
152
+ _ensure_initialized()
153
+
154
+ # Query texture dimensions
155
+ glBindTexture(GL_TEXTURE_2D, texture_id)
156
+ width = glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH)
157
+ height = glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT)
158
+
159
+ # Calculate workgroup dimensions
160
+ workgroup_size = 16
161
+ num_workgroups_x = (width + workgroup_size - 1) // workgroup_size
162
+ num_workgroups_y = (height + workgroup_size - 1) // workgroup_size
163
+ total_workgroups = num_workgroups_x * num_workgroups_y
164
+
165
+ # Resize SSBO if needed (2 floats per workgroup: min and max)
166
+ required_size = total_workgroups * 2 * 4
167
+ if required_size > _ssbo_size:
168
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, _ssbo)
169
+ glBufferData(GL_SHADER_STORAGE_BUFFER, required_size, None, GL_DYNAMIC_READ)
170
+ _ssbo_size = required_size
171
+
172
+ # Bind resources
173
+ glUseProgram(_program)
174
+
175
+ # Bind texture to texture unit 0
176
+ glActiveTexture(GL_TEXTURE0)
177
+ glBindTexture(GL_TEXTURE_2D, texture_id)
178
+ glUniform1i(glGetUniformLocation(_program, "inputTexture"), 0)
179
+
180
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, _ssbo)
181
+
182
+ # Set uniforms
183
+ loc = glGetUniformLocation(_program, "numWorkgroups")
184
+ glUniform2i(loc, num_workgroups_x, num_workgroups_y)
185
+
186
+ # Dispatch compute shader
187
+ glDispatchCompute(num_workgroups_x, num_workgroups_y, 1)
188
+ glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT)
189
+
190
+ return total_workgroups, required_size
191
+
192
+
193
+ def get_texture_min_max(texture_id: int) -> tuple[float, float]:
194
+ """
195
+ Compute min/max pixel values of an OpenGL texture (CPU readback).
196
+
197
+ Reads the result back to the CPU via glMapBufferRange — this synchronizes
198
+ the GPU and stalls. Prefer compute_min_max_to_texture() in hot paths where
199
+ the value only needs to feed another GPU pass.
200
+
201
+ Args:
202
+ texture_id: OpenGL texture ID (any format - will be converted to float)
203
+
204
+ Returns:
205
+ Tuple of (min_value, max_value) across RGB channels
206
+ """
207
+ total_workgroups, required_size = _dispatch_stage1(texture_id)
208
+
209
+ # Read back results with glMapBufferRange
210
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, _ssbo)
211
+ ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, required_size, GL_MAP_READ_BIT)
212
+ results = np.ctypeslib.as_array(ctypes.cast(ptr, ctypes.POINTER(ctypes.c_float)),
213
+ shape=(total_workgroups * 2,)).copy()
214
+ glUnmapBuffer(GL_SHADER_STORAGE_BUFFER)
215
+
216
+ # Final reduction on CPU
217
+ results = results.reshape(-1, 2)
218
+ min_val = float(np.min(results[:, 0]))
219
+ max_val = float(np.max(results[:, 1]))
220
+
221
+ return min_val, max_val
222
+
223
+
224
+ def _get_result_tex(input_texture_id: int) -> int:
225
+ """Get or create the 1x1 RGBA32F texture that holds (min, max) for an input."""
226
+ tex = _result_textures.get(input_texture_id)
227
+ if tex is not None:
228
+ return tex
229
+
230
+ tex = glGenTextures(1)
231
+ glBindTexture(GL_TEXTURE_2D, tex)
232
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, None)
233
+ # texelFetch ignores filtering, but NEAREST makes a float texture valid as a
234
+ # sampler everywhere (linear filtering of 32F is an optional GL extension).
235
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
236
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
237
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
238
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
239
+ _result_textures[input_texture_id] = tex
240
+ return tex
241
+
242
+
243
+ def compute_min_max_to_texture(input_texture_id: int) -> int:
244
+ """
245
+ Compute (min, max) of a texture entirely on the GPU and store it in the 0,0
246
+ texel of a 1x1 RGBA32F texture (.r = min, .g = max). No CPU readback.
247
+
248
+ Returns the result texture id, ready to be sampled by a downstream shader.
249
+ """
250
+ total_workgroups, _ = _dispatch_stage1(input_texture_id)
251
+
252
+ _ensure_reduce_initialized()
253
+ out_tex = _get_result_tex(input_texture_id)
254
+
255
+ glUseProgram(_reduce_program)
256
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, _ssbo)
257
+ glBindImageTexture(0, out_tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F)
258
+ glUniform1i(glGetUniformLocation(_reduce_program, "count"), total_workgroups)
259
+ glDispatchCompute(1, 1, 1)
260
+ # Make the imageStore visible before the sampler fetch in the following draw.
261
+ glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT)
262
+
263
+ return out_tex
264
+
265
+
266
+ def set_min_max_texture(input_texture_id: int, min_value: float, max_value: float) -> int:
267
+ """
268
+ Write an explicit (min, max) into the 1x1 result texture (no GPU reduction,
269
+ no readback — a tiny upload). Returns the result texture id so the explicit
270
+ path feeds normalize_remap through the same sampler as the computed path.
271
+ """
272
+ out_tex = _get_result_tex(input_texture_id)
273
+ data = np.array([min_value, max_value, 0.0, 1.0], dtype=np.float32)
274
+ glBindTexture(GL_TEXTURE_2D, out_tex)
275
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, data)
276
+ return out_tex
277
+
278
+
279
+ def cleanup():
280
+ """Delete OpenGL resources. Call before destroying GL context."""
281
+ global _program, _ssbo, _ssbo_size, _reduce_program, _result_textures
282
+
283
+ if _ssbo is not None:
284
+ glDeleteBuffers(1, [_ssbo])
285
+ _ssbo = None
286
+ if _program is not None:
287
+ glDeleteProgram(_program)
288
+ _program = None
289
+ if _reduce_program is not None:
290
+ glDeleteProgram(_reduce_program)
291
+ _reduce_program = None
292
+ if _result_textures:
293
+ glDeleteTextures(list(_result_textures.values()))
294
+ _result_textures = {}
295
+ _ssbo_size = 0