luvatrix 0.2.4__tar.gz → 0.2.6__tar.gz
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.
- {luvatrix-0.2.4/luvatrix.egg-info → luvatrix-0.2.6}/PKG-INFO +1 -1
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix/app.py +62 -19
- {luvatrix-0.2.4 → luvatrix-0.2.6/luvatrix.egg-info}/PKG-INFO +1 -1
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix.egg-info/SOURCES.txt +14 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/__init__.py +1 -1
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/accel.py +8 -4
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/display_runtime.py +26 -2
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/unified_runtime.py +38 -9
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/window_matrix.py +99 -14
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/android/runner.py +20 -5
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/android/scene_target.py +81 -9
- luvatrix-0.2.6/luvatrix_core/platform/android/vulkan_target.py +93 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/targets/base.py +3 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/build.gradle.kts +18 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/proguard-rules.pro +4 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/AndroidManifest.xml +1 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/cpp/CMakeLists.txt +4 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_primitive_batch.frag +8 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_primitive_batch.vert +23 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_primitive_batch_shaders.h +249 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_vulkan_renderer.cpp +837 -44
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/CameraBridge.kt +4 -1
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LatestFrameMailbox.kt +23 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LaunchPerformance.kt +73 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LuvatrixApplication.kt +82 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LuvatrixVulkanView.kt +378 -79
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/MainActivity.kt +6 -16
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/NativeVulkan.kt +17 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/StartupResources.kt +215 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/python/luvatrix_android_boot.py +61 -25
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/release/generated/baselineProfiles/baseline-prof.txt +877 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/release/generated/baselineProfiles/startup-prof.txt +877 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/test/java/com/luvatrix/app/BootstrapStateTest.kt +93 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/app/src/test/java/com/luvatrix/app/LaunchTimelineTest.kt +94 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/benchmark/build.gradle.kts +41 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/benchmark/src/main/java/com/luvatrix/benchmark/BaselineProfileGenerator.kt +23 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/benchmark/src/main/java/com/luvatrix/benchmark/StartupBenchmark.kt +40 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/benchmark/src/main/java/com/luvatrix/benchmark/StartupJourney.kt +16 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/build.gradle.kts +2 -0
- luvatrix-0.2.6/luvatrix_core/templates/native/android/gradle.properties +2 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/settings.gradle.kts +1 -0
- luvatrix-0.2.6/luvatrix_ui/__init__.py +122 -0
- luvatrix-0.2.6/luvatrix_ui/text/__init__.py +38 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/pyproject.toml +1 -1
- luvatrix-0.2.4/luvatrix_core/platform/android/vulkan_target.py +0 -50
- luvatrix-0.2.4/luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LaunchPerformance.kt +0 -38
- luvatrix-0.2.4/luvatrix_core/templates/native/android/app/src/test/java/com/luvatrix/app/LaunchTimelineTest.kt +0 -40
- luvatrix-0.2.4/luvatrix_core/templates/native/android/gradle.properties +0 -1
- luvatrix-0.2.4/luvatrix_ui/__init__.py +0 -205
- luvatrix-0.2.4/luvatrix_ui/text/__init__.py +0 -32
- {luvatrix-0.2.4 → luvatrix-0.2.6}/LICENSE +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/MANIFEST.in +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/README.md +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix/auth/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix/auth/calendar.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix/auth/google.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix/auth/sign_in.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix/auth/ui.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix.egg-info/dependency_links.txt +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix.egg-info/entry_points.txt +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix.egg-info/requires.txt +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix.egg-info/top_level.txt +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/_accel_native.c +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/_accel_native.pyx +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/app_runtime.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/audit.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/coordinates.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/debug_capture.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/debug_menu.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/energy_safety.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/engine.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/events.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/frame_rate_controller.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/hdi_thread.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/interaction_work.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/process_runtime.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/process_sdk.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/protocol_governance.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/scene_display_runtime.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/scene_graph.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/scene_rasterizer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/sensor_manager.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/core/ui_frame_renderer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/perf/copy_telemetry.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/android/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/android/camera_processing_contract.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/android/camera_style.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/android/hdi_source.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/android/sensors.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/frame_pipeline.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/ios/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/ios/hdi_source.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/ios/lifecycle.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/ios/metal_backend.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/ios/runner.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/ios/scene_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/ios/window_system.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/hdi_source.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/iohid_source.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/metal_backend.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/metal_presenter.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/metal_scene_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/sensors.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/vulkan_backend.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/vulkan_presenter.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/macos/window_system.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/package_sync.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/vulkan_compat.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/vulkan_scaling.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/vulkan_setup.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/build.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/command_buffer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/python_shim/luvatrix/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/python_shim/luvatrix/app.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/runtime_assets/index.html +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/runtime_assets/runtime/command-buffer.js +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/runtime_assets/runtime/input.js +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/runtime_assets/runtime/luvatrix-web.js +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/runtime_assets/runtime/package.json +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/runtime_assets/runtime/renderers.js +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/runtime_assets/runtime/spotify-bridge.js +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/server.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/platform/web/websocket_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/render/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/render/framebuffer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/render/svg.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/scaffold.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/targets/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/targets/cpu_scene_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/targets/metal_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/targets/scene_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/targets/vulkan_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/targets/web_target.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/.gitignore +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/assets/luvatrix_bitmap_font.txt +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/assets/luvatrix_launch_config.json +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/assets/luvatrix_matrix_font_alpha.txt +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_camera_preview.frag +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_camera_preview_shaders.h +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/python/.gitignore +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/python/luvatrix_launch_config.json +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/app/src/main/res/values/styles.xml +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/gradlew +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/scripts/emulator_acceptance.sh +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/android/scripts/sync_python_assets.sh +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/AppDelegate.swift +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/Assets.xcassets/AppIcon.appiconset/icon.png +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/Assets.xcassets/Contents.json +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/Info.plist +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/Luvatrix-Bridging-Header.h +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/Luvatrix.entitlements +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/PythonBridge.h +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/PythonBridge.m +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/Luvatrix/main.swift +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/project.yml +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/templates/native/ios/scripts/setup_ios.sh +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/ui/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/ui/element.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_core/ui/page_loader.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/adapters/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/adapters/normalize.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/api.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/compile/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/compile/app_protocol.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/display.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/dynamic_axis.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/errors.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/figure.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/live.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/raster/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/raster/canvas.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/raster/draw_lines.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/raster/draw_markers.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/raster/draw_text.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/raster/layers.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/scales.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_plot/series.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/component_schema.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/controls/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/controls/button.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/controls/interaction.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/controls/stained_glass_button.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/controls/svg_component.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/controls/svg_renderer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planes_protocol.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planes_runtime.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planes_v2_validator.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planning/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planning/agile_renderer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planning/exporters.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planning/gantt_renderer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planning/interaction.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planning/schema.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/planning/validation.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/style/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/style/theme.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/table/__init__.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/table/component.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/text/component.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/text/renderer.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/text/wrapping.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/luvatrix_ui/ui_ir.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/main.py +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/setup.cfg +0 -0
- {luvatrix-0.2.4 → luvatrix-0.2.6}/setup.py +0 -0
|
@@ -3,31 +3,33 @@ from __future__ import annotations
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
4
|
from contextlib import AbstractContextManager
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
|
-
from importlib import resources
|
|
6
|
+
from importlib import import_module, resources
|
|
7
7
|
import importlib.util
|
|
8
8
|
import math
|
|
9
9
|
import platform
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any
|
|
12
|
-
|
|
13
|
-
from luvatrix_core.core.
|
|
14
|
-
APP_PROTOCOL_VERSION,
|
|
15
|
-
AppContext,
|
|
16
|
-
AppLifecycle,
|
|
17
|
-
AppManifest,
|
|
18
|
-
AppRuntime,
|
|
19
|
-
AppUIRenderer,
|
|
20
|
-
AppVariant,
|
|
21
|
-
AppWebConfig,
|
|
22
|
-
ResolvedAppVariant,
|
|
23
|
-
)
|
|
24
|
-
from luvatrix_core.core.hdi_thread import HDIEvent, HDIThread
|
|
25
|
-
from luvatrix_core.core.interaction_work import InteractionAwareWorkScheduler
|
|
26
|
-
from luvatrix_core.core.sensor_manager import SensorManagerThread, SensorSample
|
|
27
|
-
from luvatrix_core.core.window_matrix import FullRewrite, WriteBatch, WindowMatrix
|
|
28
|
-
from luvatrix_core import accel
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
from luvatrix_core.core.protocol_governance import CURRENT_PROTOCOL_VERSION
|
|
29
14
|
from luvatrix_ui.text.wrapping import TextWrapping, layout_text, prepare_text
|
|
30
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from luvatrix_core.core.app_runtime import (
|
|
18
|
+
AppContext,
|
|
19
|
+
AppLifecycle,
|
|
20
|
+
AppManifest,
|
|
21
|
+
AppRuntime,
|
|
22
|
+
AppUIRenderer,
|
|
23
|
+
AppVariant,
|
|
24
|
+
AppWebConfig,
|
|
25
|
+
ResolvedAppVariant,
|
|
26
|
+
)
|
|
27
|
+
from luvatrix_core.core.hdi_thread import HDIEvent
|
|
28
|
+
from luvatrix_core.core.sensor_manager import SensorSample
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
APP_PROTOCOL_VERSION = CURRENT_PROTOCOL_VERSION
|
|
32
|
+
|
|
31
33
|
PLATFORM_MACOS = "macos"
|
|
32
34
|
PLATFORM_IOS = "ios"
|
|
33
35
|
PLATFORM_ANDROID = "android"
|
|
@@ -1353,6 +1355,8 @@ def draw_rounded_rect_to_matrix(
|
|
|
1353
1355
|
antialias: bool = True,
|
|
1354
1356
|
):
|
|
1355
1357
|
"""Source-over rasterize a rounded rectangle into an RGBA matrix."""
|
|
1358
|
+
from luvatrix_core import accel
|
|
1359
|
+
|
|
1356
1360
|
width = int(width)
|
|
1357
1361
|
height = int(height)
|
|
1358
1362
|
if width <= 0 or height <= 0:
|
|
@@ -1508,6 +1512,8 @@ class MatrixFrame(AbstractContextManager["MatrixFrame"]):
|
|
|
1508
1512
|
self._frame = None
|
|
1509
1513
|
|
|
1510
1514
|
def __enter__(self) -> "MatrixFrame":
|
|
1515
|
+
from luvatrix_core import accel
|
|
1516
|
+
|
|
1511
1517
|
snap = self._ctx.read_matrix_snapshot()
|
|
1512
1518
|
h, w, _ = snap.shape
|
|
1513
1519
|
self._frame = accel.zeros((h, w, 4))
|
|
@@ -1516,6 +1522,8 @@ class MatrixFrame(AbstractContextManager["MatrixFrame"]):
|
|
|
1516
1522
|
|
|
1517
1523
|
def __exit__(self, exc_type: object, exc: object, tb: object) -> bool:
|
|
1518
1524
|
if exc_type is None and self._frame is not None:
|
|
1525
|
+
from luvatrix_core.core.window_matrix import FullRewrite, WriteBatch
|
|
1526
|
+
|
|
1519
1527
|
self._ctx.submit_write_batch(WriteBatch([FullRewrite(self._frame, take_ownership=True)]))
|
|
1520
1528
|
return False
|
|
1521
1529
|
|
|
@@ -1785,6 +1793,11 @@ def validate_app_install(
|
|
|
1785
1793
|
|
|
1786
1794
|
|
|
1787
1795
|
def _manifest_runtime(*, host_os: str | None = None, host_arch: str | None = None) -> AppRuntime:
|
|
1796
|
+
from luvatrix_core.core.app_runtime import AppRuntime
|
|
1797
|
+
from luvatrix_core.core.hdi_thread import HDIThread
|
|
1798
|
+
from luvatrix_core.core.sensor_manager import SensorManagerThread
|
|
1799
|
+
from luvatrix_core.core.window_matrix import WindowMatrix
|
|
1800
|
+
|
|
1788
1801
|
return AppRuntime(
|
|
1789
1802
|
matrix=WindowMatrix(1, 1),
|
|
1790
1803
|
hdi=HDIThread(source=_NoopHDISource()),
|
|
@@ -1835,6 +1848,36 @@ def _normalize_host_os(value: str) -> str:
|
|
|
1835
1848
|
return aliases[normalized]
|
|
1836
1849
|
|
|
1837
1850
|
|
|
1851
|
+
_LAZY_EXPORTS = {
|
|
1852
|
+
"AppContext": ("luvatrix_core.core.app_runtime", "AppContext"),
|
|
1853
|
+
"AppLifecycle": ("luvatrix_core.core.app_runtime", "AppLifecycle"),
|
|
1854
|
+
"AppManifest": ("luvatrix_core.core.app_runtime", "AppManifest"),
|
|
1855
|
+
"AppRuntime": ("luvatrix_core.core.app_runtime", "AppRuntime"),
|
|
1856
|
+
"AppUIRenderer": ("luvatrix_core.core.app_runtime", "AppUIRenderer"),
|
|
1857
|
+
"AppVariant": ("luvatrix_core.core.app_runtime", "AppVariant"),
|
|
1858
|
+
"AppWebConfig": ("luvatrix_core.core.app_runtime", "AppWebConfig"),
|
|
1859
|
+
"ResolvedAppVariant": ("luvatrix_core.core.app_runtime", "ResolvedAppVariant"),
|
|
1860
|
+
"InteractionAwareWorkScheduler": (
|
|
1861
|
+
"luvatrix_core.core.interaction_work",
|
|
1862
|
+
"InteractionAwareWorkScheduler",
|
|
1863
|
+
),
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
|
|
1867
|
+
def __getattr__(name: str) -> object:
|
|
1868
|
+
target = _LAZY_EXPORTS.get(name)
|
|
1869
|
+
if target is None:
|
|
1870
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
1871
|
+
module_name, attribute_name = target
|
|
1872
|
+
value = getattr(import_module(module_name), attribute_name)
|
|
1873
|
+
globals()[name] = value
|
|
1874
|
+
return value
|
|
1875
|
+
|
|
1876
|
+
|
|
1877
|
+
def __dir__() -> list[str]:
|
|
1878
|
+
return sorted(set(globals()) | _LAZY_EXPORTS.keys())
|
|
1879
|
+
|
|
1880
|
+
|
|
1838
1881
|
__all__ = [
|
|
1839
1882
|
"APP_PROTOCOL_VERSION",
|
|
1840
1883
|
"App",
|
|
@@ -107,6 +107,7 @@ luvatrix_core/templates/native/android/gradle.properties
|
|
|
107
107
|
luvatrix_core/templates/native/android/gradlew
|
|
108
108
|
luvatrix_core/templates/native/android/settings.gradle.kts
|
|
109
109
|
luvatrix_core/templates/native/android/app/build.gradle.kts
|
|
110
|
+
luvatrix_core/templates/native/android/app/proguard-rules.pro
|
|
110
111
|
luvatrix_core/templates/native/android/app/src/main/AndroidManifest.xml
|
|
111
112
|
luvatrix_core/templates/native/android/app/src/main/assets/luvatrix_bitmap_font.txt
|
|
112
113
|
luvatrix_core/templates/native/android/app/src/main/assets/luvatrix_launch_config.json
|
|
@@ -114,12 +115,18 @@ luvatrix_core/templates/native/android/app/src/main/assets/luvatrix_matrix_font_
|
|
|
114
115
|
luvatrix_core/templates/native/android/app/src/main/cpp/CMakeLists.txt
|
|
115
116
|
luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_camera_preview.frag
|
|
116
117
|
luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_camera_preview_shaders.h
|
|
118
|
+
luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_primitive_batch.frag
|
|
119
|
+
luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_primitive_batch.vert
|
|
120
|
+
luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_primitive_batch_shaders.h
|
|
117
121
|
luvatrix_core/templates/native/android/app/src/main/cpp/luvatrix_vulkan_renderer.cpp
|
|
118
122
|
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/CameraBridge.kt
|
|
123
|
+
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LatestFrameMailbox.kt
|
|
119
124
|
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LaunchPerformance.kt
|
|
125
|
+
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LuvatrixApplication.kt
|
|
120
126
|
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/LuvatrixVulkanView.kt
|
|
121
127
|
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/MainActivity.kt
|
|
122
128
|
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/NativeVulkan.kt
|
|
129
|
+
luvatrix_core/templates/native/android/app/src/main/java/com/luvatrix/app/StartupResources.kt
|
|
123
130
|
luvatrix_core/templates/native/android/app/src/main/python/.gitignore
|
|
124
131
|
luvatrix_core/templates/native/android/app/src/main/python/luvatrix_android_boot.py
|
|
125
132
|
luvatrix_core/templates/native/android/app/src/main/python/luvatrix_launch_config.json
|
|
@@ -129,7 +136,14 @@ luvatrix_core/templates/native/android/app/src/main/res/mipmap-xhdpi/ic_launcher
|
|
|
129
136
|
luvatrix_core/templates/native/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
|
|
130
137
|
luvatrix_core/templates/native/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
|
|
131
138
|
luvatrix_core/templates/native/android/app/src/main/res/values/styles.xml
|
|
139
|
+
luvatrix_core/templates/native/android/app/src/release/generated/baselineProfiles/baseline-prof.txt
|
|
140
|
+
luvatrix_core/templates/native/android/app/src/release/generated/baselineProfiles/startup-prof.txt
|
|
141
|
+
luvatrix_core/templates/native/android/app/src/test/java/com/luvatrix/app/BootstrapStateTest.kt
|
|
132
142
|
luvatrix_core/templates/native/android/app/src/test/java/com/luvatrix/app/LaunchTimelineTest.kt
|
|
143
|
+
luvatrix_core/templates/native/android/benchmark/build.gradle.kts
|
|
144
|
+
luvatrix_core/templates/native/android/benchmark/src/main/java/com/luvatrix/benchmark/BaselineProfileGenerator.kt
|
|
145
|
+
luvatrix_core/templates/native/android/benchmark/src/main/java/com/luvatrix/benchmark/StartupBenchmark.kt
|
|
146
|
+
luvatrix_core/templates/native/android/benchmark/src/main/java/com/luvatrix/benchmark/StartupJourney.kt
|
|
133
147
|
luvatrix_core/templates/native/android/scripts/emulator_acceptance.sh
|
|
134
148
|
luvatrix_core/templates/native/android/scripts/sync_python_assets.sh
|
|
135
149
|
luvatrix_core/templates/native/ios/project.yml
|
|
@@ -266,6 +266,12 @@ def _single_roll_shift(shifts) -> int:
|
|
|
266
266
|
return int(shifts)
|
|
267
267
|
|
|
268
268
|
|
|
269
|
+
def filled_rgba(height: int, width: int, rgba: tuple[int, int, int, int]):
|
|
270
|
+
"""Allocate isolated uint8 RGBA storage filled with one color."""
|
|
271
|
+
pixel = from_sequence(list(rgba), (1, 1, 4))
|
|
272
|
+
return broadcast_to_clone(pixel, (int(height), int(width), 4))
|
|
273
|
+
|
|
274
|
+
|
|
269
275
|
def _normalize_roll_specs(shifts, dims, ndim: int) -> tuple[tuple[int, int], ...]:
|
|
270
276
|
if isinstance(dims, int):
|
|
271
277
|
axes = (dims,)
|
|
@@ -921,10 +927,8 @@ else:
|
|
|
921
927
|
|
|
922
928
|
def broadcast_to_clone(x: _PureArray, shape: tuple[int, ...]) -> _PureArray:
|
|
923
929
|
H, W, C = shape
|
|
924
|
-
|
|
925
|
-
data =
|
|
926
|
-
for i in range(H * W):
|
|
927
|
-
data[i * C:(i + 1) * C] = src
|
|
930
|
+
pixel_bytes = C * _pure_item_size(x)
|
|
931
|
+
data = x._data[:pixel_bytes] * (H * W)
|
|
928
932
|
return _PureArray(data, shape, x.dtype)
|
|
929
933
|
|
|
930
934
|
def roll(x: _PureArray, shifts, dims=None) -> _PureArray:
|
|
@@ -31,6 +31,7 @@ class DisplayRuntime:
|
|
|
31
31
|
self._target_started = False
|
|
32
32
|
self._last_error: Exception | None = None
|
|
33
33
|
self._last_copy_telemetry: dict[str, int] = {}
|
|
34
|
+
self._last_presented_revision: int | None = None
|
|
34
35
|
|
|
35
36
|
def start(self) -> None:
|
|
36
37
|
if self._thread and self._thread.is_alive():
|
|
@@ -79,17 +80,22 @@ class DisplayRuntime:
|
|
|
79
80
|
return None
|
|
80
81
|
|
|
81
82
|
# Coalesce queued blits to newest revision so frame data and revision stay aligned.
|
|
83
|
+
dirty_rect = event.dirty_rect
|
|
82
84
|
while True:
|
|
83
85
|
newer = self._matrix.pop_call_blit(timeout=None)
|
|
84
86
|
if newer is None:
|
|
85
87
|
break
|
|
88
|
+
dirty_rect = _union_dirty_rects(dirty_rect, newer.dirty_rect)
|
|
86
89
|
event = newer
|
|
87
90
|
|
|
88
91
|
snapshot = self._matrix.read_revision_snapshot(event.revision)
|
|
89
92
|
if snapshot is None:
|
|
90
93
|
snapshot = self._matrix.read_snapshot()
|
|
91
|
-
|
|
94
|
+
if self._last_presented_revision is None:
|
|
95
|
+
dirty_rect = None
|
|
96
|
+
frame = _build_frame(snapshot=snapshot, revision=event.revision, dirty_rect=dirty_rect)
|
|
92
97
|
self._target.present_frame(frame)
|
|
98
|
+
self._last_presented_revision = event.revision
|
|
93
99
|
self._last_copy_telemetry = snapshot_copy_telemetry()
|
|
94
100
|
return RenderTick(event=event, frame=frame)
|
|
95
101
|
|
|
@@ -122,7 +128,11 @@ class DisplayRuntime:
|
|
|
122
128
|
return dict(self._last_copy_telemetry)
|
|
123
129
|
|
|
124
130
|
|
|
125
|
-
def _build_frame(
|
|
131
|
+
def _build_frame(
|
|
132
|
+
snapshot: object,
|
|
133
|
+
revision: int,
|
|
134
|
+
dirty_rect: tuple[int, int, int, int] | None = None,
|
|
135
|
+
) -> DisplayFrame:
|
|
126
136
|
if snapshot.ndim != 3 or snapshot.shape[2] != 4:
|
|
127
137
|
raise ValueError(f"invalid snapshot shape: {tuple(snapshot.shape)}")
|
|
128
138
|
if not accel.is_uint8(snapshot):
|
|
@@ -133,4 +143,18 @@ def _build_frame(snapshot: object, revision: int) -> DisplayFrame:
|
|
|
133
143
|
width=int(width),
|
|
134
144
|
height=int(height),
|
|
135
145
|
rgba=snapshot,
|
|
146
|
+
dirty_rect=dirty_rect,
|
|
136
147
|
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _union_dirty_rects(
|
|
151
|
+
first: tuple[int, int, int, int] | None,
|
|
152
|
+
second: tuple[int, int, int, int] | None,
|
|
153
|
+
) -> tuple[int, int, int, int] | None:
|
|
154
|
+
if first is None or second is None:
|
|
155
|
+
return None
|
|
156
|
+
left = min(first[0], second[0])
|
|
157
|
+
top = min(first[1], second[1])
|
|
158
|
+
right = max(first[0] + first[2], second[0] + second[2])
|
|
159
|
+
bottom = max(first[1] + first[3], second[1] + second[3])
|
|
160
|
+
return (left, top, right - left, bottom - top)
|
|
@@ -166,6 +166,8 @@ class UnifiedRuntime:
|
|
|
166
166
|
present_fps: int | None = None,
|
|
167
167
|
display_timeout: float = 0.0,
|
|
168
168
|
on_targets_started: "Callable[[], None] | None" = None,
|
|
169
|
+
before_lifecycle_init: "Callable[[], None] | None" = None,
|
|
170
|
+
defer_hdi_polling_until_first_frame: bool = False,
|
|
169
171
|
defer_optional_sensor_polling_until_first_frame: bool = False,
|
|
170
172
|
) -> UnifiedRunResult:
|
|
171
173
|
if max_ticks is not None and max_ticks <= 0:
|
|
@@ -173,6 +175,12 @@ class UnifiedRuntime:
|
|
|
173
175
|
rate = FrameRateController(target_fps=target_fps, present_fps=present_fps)
|
|
174
176
|
app_path = Path(app_dir).resolve()
|
|
175
177
|
manifest = self._app_runtime.load_manifest(app_path)
|
|
178
|
+
effective_render_mode = self._render_mode
|
|
179
|
+
if effective_render_mode == "auto":
|
|
180
|
+
if manifest.render_preferred == "matrix":
|
|
181
|
+
effective_render_mode = "matrix"
|
|
182
|
+
elif manifest.render_preferred == "scene" and self._scene_target is not None:
|
|
183
|
+
effective_render_mode = "scene"
|
|
176
184
|
debug_policy_profile = self._app_runtime.resolve_debug_policy_profile(manifest)
|
|
177
185
|
granted = self._app_runtime.resolve_capabilities(manifest)
|
|
178
186
|
ctx = self._app_runtime.build_context(granted_capabilities=granted, manifest=manifest)
|
|
@@ -196,9 +204,9 @@ class UnifiedRuntime:
|
|
|
196
204
|
stopped_by_energy_safety = False
|
|
197
205
|
started = False
|
|
198
206
|
active_targets: list[object] = []
|
|
199
|
-
if self._scene_target is not None and
|
|
207
|
+
if self._scene_target is not None and effective_render_mode in ("auto", "scene"):
|
|
200
208
|
active_targets.append(self._scene_target)
|
|
201
|
-
if
|
|
209
|
+
if effective_render_mode == "matrix" or (effective_render_mode == "auto" and self._scene_target is None):
|
|
202
210
|
active_targets.append(self._target)
|
|
203
211
|
self._configure_target_debug_menu(
|
|
204
212
|
manifest.app_id,
|
|
@@ -218,7 +226,12 @@ class UnifiedRuntime:
|
|
|
218
226
|
repeat_latest=False,
|
|
219
227
|
)
|
|
220
228
|
scene_present_loop_started = True
|
|
221
|
-
|
|
229
|
+
required_hdi_capability = any(
|
|
230
|
+
capability.startswith("hdi.") for capability in manifest.required_capabilities
|
|
231
|
+
)
|
|
232
|
+
hdi_started = not (defer_hdi_polling_until_first_frame and not required_hdi_capability)
|
|
233
|
+
if hdi_started:
|
|
234
|
+
ctx.hdi.start()
|
|
222
235
|
required_sensor_capability = any(
|
|
223
236
|
capability.startswith("sensor.") for capability in manifest.required_capabilities
|
|
224
237
|
)
|
|
@@ -228,15 +241,22 @@ class UnifiedRuntime:
|
|
|
228
241
|
if sensor_manager_started:
|
|
229
242
|
ctx.sensor_manager.start()
|
|
230
243
|
deferred_sensor_deadline = time.perf_counter() + 0.1
|
|
244
|
+
deferred_hdi_deadline = time.perf_counter() + 0.1
|
|
231
245
|
last = time.perf_counter()
|
|
232
246
|
was_active = self._is_active()
|
|
233
247
|
try:
|
|
248
|
+
if before_lifecycle_init is not None:
|
|
249
|
+
before_lifecycle_init()
|
|
234
250
|
lifecycle.init(ctx)
|
|
235
251
|
tick_idx = 0
|
|
236
252
|
while max_ticks is None or tick_idx < max_ticks:
|
|
237
253
|
for active_target in active_targets:
|
|
238
254
|
active_target.pump_events()
|
|
239
|
-
if
|
|
255
|
+
if (
|
|
256
|
+
scene_present_loop_started
|
|
257
|
+
and self._scene_display_runtime is not None
|
|
258
|
+
and self._scene_display_runtime.last_error is not None
|
|
259
|
+
):
|
|
240
260
|
raise self._scene_display_runtime.last_error
|
|
241
261
|
if any(bool(active_target.should_close()) for active_target in active_targets):
|
|
242
262
|
stopped_by_target_close = True
|
|
@@ -265,8 +285,7 @@ class UnifiedRuntime:
|
|
|
265
285
|
self._app_loop_rate.mark()
|
|
266
286
|
ticks_run += 1
|
|
267
287
|
tick_idx += 1
|
|
268
|
-
if
|
|
269
|
-
tick = None
|
|
288
|
+
if not scene_present_loop_started and rate.should_present(now):
|
|
270
289
|
matrix_tick = self._display_runtime.run_once(timeout=display_timeout)
|
|
271
290
|
if matrix_tick is not None:
|
|
272
291
|
frames_presented += 1
|
|
@@ -274,12 +293,21 @@ class UnifiedRuntime:
|
|
|
274
293
|
if not sensor_manager_started:
|
|
275
294
|
scene_frames = (
|
|
276
295
|
self._scene_display_runtime.frames_presented
|
|
277
|
-
if self._scene_display_runtime is not None
|
|
296
|
+
if scene_present_loop_started and self._scene_display_runtime is not None
|
|
278
297
|
else 0
|
|
279
298
|
)
|
|
280
299
|
if frames_presented > 0 or scene_frames > 0 or time.perf_counter() >= deferred_sensor_deadline:
|
|
281
300
|
ctx.sensor_manager.start()
|
|
282
301
|
sensor_manager_started = True
|
|
302
|
+
if not hdi_started:
|
|
303
|
+
scene_frames = (
|
|
304
|
+
self._scene_display_runtime.frames_presented
|
|
305
|
+
if scene_present_loop_started and self._scene_display_runtime is not None
|
|
306
|
+
else 0
|
|
307
|
+
)
|
|
308
|
+
if frames_presented > 0 or scene_frames > 0 or time.perf_counter() >= deferred_hdi_deadline:
|
|
309
|
+
ctx.hdi.start()
|
|
310
|
+
hdi_started = True
|
|
283
311
|
sleep_for = rate.compute_sleep(
|
|
284
312
|
loop_started_at=now,
|
|
285
313
|
loop_finished_at=time.perf_counter(),
|
|
@@ -298,7 +326,8 @@ class UnifiedRuntime:
|
|
|
298
326
|
try:
|
|
299
327
|
lifecycle.stop(ctx)
|
|
300
328
|
finally:
|
|
301
|
-
|
|
329
|
+
if hdi_started:
|
|
330
|
+
ctx.hdi.stop()
|
|
302
331
|
ctx.sensor_manager.stop()
|
|
303
332
|
if started:
|
|
304
333
|
if scene_present_loop_started and self._scene_display_runtime is not None:
|
|
@@ -307,7 +336,7 @@ class UnifiedRuntime:
|
|
|
307
336
|
self._scene_display_runtime.run_once(timeout=0.0, repeat_latest=True)
|
|
308
337
|
for active_target in reversed(active_targets):
|
|
309
338
|
active_target.stop()
|
|
310
|
-
if self._scene_display_runtime is not None:
|
|
339
|
+
if scene_present_loop_started and self._scene_display_runtime is not None:
|
|
311
340
|
frames_presented = self._scene_display_runtime.frames_presented
|
|
312
341
|
self._frames_presented = frames_presented
|
|
313
342
|
return UnifiedRunResult(
|
|
@@ -84,16 +84,31 @@ class CallBlitEvent:
|
|
|
84
84
|
event_id: int
|
|
85
85
|
revision: int
|
|
86
86
|
ts_ns: int
|
|
87
|
+
# ``None`` denotes a global rewrite or transform that invalidates all pixels.
|
|
88
|
+
dirty_rect: tuple[int, int, int, int] | None = None
|
|
87
89
|
|
|
88
90
|
|
|
89
91
|
class WindowMatrix:
|
|
90
|
-
"""Canonical RGBA255 matrix with atomic write-batch commits.
|
|
92
|
+
"""Canonical RGBA255 matrix with atomic write-batch commits.
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
``lazy=True`` keeps dimensions and event state available for scene-only
|
|
95
|
+
runtimes without allocating RGBA storage. The first matrix read or write
|
|
96
|
+
materializes storage; a first full rewrite directly becomes that storage.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
height: int,
|
|
102
|
+
width: int,
|
|
103
|
+
background: tuple[int, int, int, int] = (0, 0, 0, 255),
|
|
104
|
+
*,
|
|
105
|
+
lazy: bool = False,
|
|
106
|
+
) -> None:
|
|
93
107
|
if height <= 0 or width <= 0:
|
|
94
108
|
raise ValueError("height and width must be > 0")
|
|
95
109
|
self.height = height
|
|
96
110
|
self.width = width
|
|
111
|
+
self._background = tuple(background)
|
|
97
112
|
self._write_lock = threading.Lock()
|
|
98
113
|
self._event_lock = threading.Lock()
|
|
99
114
|
self._event_cv = threading.Condition(self._event_lock)
|
|
@@ -103,20 +118,26 @@ class WindowMatrix:
|
|
|
103
118
|
self._revision_snapshot_enabled = os.getenv("LUVATRIX_ENABLE_REVISIONED_SNAPSHOT", "0").strip() == "1"
|
|
104
119
|
self._revision_snapshot: object | None = None
|
|
105
120
|
self._revision_snapshot_revision = 0
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
self._revision_snapshot = accel.clone(self._matrix)
|
|
121
|
+
self._matrix: object | None = None
|
|
122
|
+
if not lazy:
|
|
123
|
+
self._materialize_locked()
|
|
110
124
|
|
|
111
125
|
@property
|
|
112
126
|
def revision(self) -> int:
|
|
113
127
|
return self._revision
|
|
114
128
|
|
|
129
|
+
@property
|
|
130
|
+
def is_materialized(self) -> bool:
|
|
131
|
+
"""Whether the RGBA backing store has been allocated."""
|
|
132
|
+
with self._write_lock:
|
|
133
|
+
return self._matrix is not None
|
|
134
|
+
|
|
115
135
|
def read_snapshot(self) -> object:
|
|
116
136
|
"""Safe read view for external consumers."""
|
|
117
137
|
with self._write_lock:
|
|
138
|
+
matrix = self._materialize_locked()
|
|
118
139
|
started = time.perf_counter_ns()
|
|
119
|
-
out = accel.clone(
|
|
140
|
+
out = accel.clone(matrix)
|
|
120
141
|
add_copy_telemetry(
|
|
121
142
|
copy_count=1,
|
|
122
143
|
copy_bytes=accel.numel(out),
|
|
@@ -129,6 +150,7 @@ class WindowMatrix:
|
|
|
129
150
|
with self._write_lock:
|
|
130
151
|
if not self._revision_snapshot_enabled:
|
|
131
152
|
return None
|
|
153
|
+
self._materialize_locked()
|
|
132
154
|
if self._revision_snapshot is None:
|
|
133
155
|
return None
|
|
134
156
|
if int(revision) != int(self._revision_snapshot_revision):
|
|
@@ -137,13 +159,26 @@ class WindowMatrix:
|
|
|
137
159
|
|
|
138
160
|
def _unsafe_matrix_view(self) -> object:
|
|
139
161
|
"""Internal-only no-copy handle."""
|
|
140
|
-
|
|
162
|
+
with self._write_lock:
|
|
163
|
+
return self._materialize_locked()
|
|
164
|
+
|
|
165
|
+
def _materialize_locked(self) -> object:
|
|
166
|
+
matrix = self._matrix
|
|
167
|
+
if matrix is not None:
|
|
168
|
+
return matrix
|
|
169
|
+
matrix = accel.filled_rgba(self.height, self.width, self._background)
|
|
170
|
+
self._matrix = matrix
|
|
171
|
+
if self._revision_snapshot_enabled:
|
|
172
|
+
self._revision_snapshot = accel.clone(matrix)
|
|
173
|
+
self._revision_snapshot_revision = int(self._revision)
|
|
174
|
+
return matrix
|
|
141
175
|
|
|
142
176
|
def submit_write_batch(self, batch: WriteBatch) -> CallBlitEvent:
|
|
143
177
|
if not batch.operations:
|
|
144
178
|
raise ValueError("write batch must include at least one operation")
|
|
145
179
|
|
|
146
180
|
with self._write_lock:
|
|
181
|
+
dirty_rect = _dirty_rect_for_operations(batch.operations, self.width, self.height)
|
|
147
182
|
offending_pixels = 0
|
|
148
183
|
if len(batch.operations) == 1 and isinstance(batch.operations[0], FullRewrite):
|
|
149
184
|
op = batch.operations[0]
|
|
@@ -154,10 +189,10 @@ class WindowMatrix:
|
|
|
154
189
|
)
|
|
155
190
|
elif self._is_localized_commit_batch(batch.operations):
|
|
156
191
|
prepared, offending_pixels = self._prepare_localized_ops(batch.operations)
|
|
157
|
-
self._apply_prepared_localized_ops(prepared)
|
|
192
|
+
self._apply_prepared_localized_ops(self._materialize_locked(), prepared)
|
|
158
193
|
else:
|
|
159
194
|
stage_started = time.perf_counter_ns()
|
|
160
|
-
staged = accel.clone(self.
|
|
195
|
+
staged = accel.clone(self._materialize_locked())
|
|
161
196
|
add_copy_telemetry(
|
|
162
197
|
copy_count=1,
|
|
163
198
|
copy_bytes=accel.numel(staged),
|
|
@@ -180,6 +215,7 @@ class WindowMatrix:
|
|
|
180
215
|
event_id=self._next_event_id,
|
|
181
216
|
revision=self._revision,
|
|
182
217
|
ts_ns=time.time_ns(),
|
|
218
|
+
dirty_rect=dirty_rect,
|
|
183
219
|
)
|
|
184
220
|
self._next_event_id += 1
|
|
185
221
|
|
|
@@ -299,22 +335,28 @@ class WindowMatrix:
|
|
|
299
335
|
raise TypeError(f"Unsupported localized write op: {type(op)!r}")
|
|
300
336
|
return prepared, offending_pixels
|
|
301
337
|
|
|
302
|
-
def _apply_prepared_localized_ops(
|
|
338
|
+
def _apply_prepared_localized_ops(
|
|
339
|
+
self,
|
|
340
|
+
matrix: object,
|
|
341
|
+
prepared: list[tuple[str, int, int, int, int, object]],
|
|
342
|
+
) -> None:
|
|
303
343
|
for kind, x, y, width, height, payload in prepared:
|
|
304
344
|
if kind == "column":
|
|
305
|
-
|
|
345
|
+
matrix[:, x, :] = payload
|
|
306
346
|
continue
|
|
307
347
|
if kind == "row":
|
|
308
|
-
|
|
348
|
+
matrix[y, :, :] = payload
|
|
309
349
|
continue
|
|
310
350
|
if kind == "rect":
|
|
311
|
-
|
|
351
|
+
matrix[y : y + height, x : x + width, :] = payload
|
|
312
352
|
continue
|
|
313
353
|
raise RuntimeError(f"Unsupported prepared localized write kind: {kind}")
|
|
314
354
|
|
|
315
355
|
def _refresh_revision_snapshot(self) -> None:
|
|
316
356
|
if not self._revision_snapshot_enabled:
|
|
317
357
|
return
|
|
358
|
+
if self._matrix is None:
|
|
359
|
+
return
|
|
318
360
|
started = time.perf_counter_ns()
|
|
319
361
|
self._revision_snapshot = accel.clone(self._matrix)
|
|
320
362
|
self._revision_snapshot_revision = int(self._revision)
|
|
@@ -339,6 +381,49 @@ def _validate_rect(x: int, y: int, width: int, height: int, matrix_width: int, m
|
|
|
339
381
|
raise ValueError("rect exceeds matrix bounds")
|
|
340
382
|
|
|
341
383
|
|
|
384
|
+
def _dirty_rect_for_operations(
|
|
385
|
+
operations: list[WriteOp],
|
|
386
|
+
matrix_width: int,
|
|
387
|
+
matrix_height: int,
|
|
388
|
+
) -> tuple[int, int, int, int] | None:
|
|
389
|
+
"""Return the affected pixel bounds, or ``None`` when every pixel changed."""
|
|
390
|
+
rect: tuple[int, int, int, int] | None = (0, 0, 0, 0)
|
|
391
|
+
for operation in operations:
|
|
392
|
+
if isinstance(operation, (FullRewrite, ShiftFrame, Multiply)):
|
|
393
|
+
return None
|
|
394
|
+
if isinstance(operation, ReplaceRect):
|
|
395
|
+
candidate = (operation.x, operation.y, operation.width, operation.height)
|
|
396
|
+
elif isinstance(operation, ReplaceColumn):
|
|
397
|
+
candidate = (operation.index, 0, 1, matrix_height)
|
|
398
|
+
elif isinstance(operation, PushColumn):
|
|
399
|
+
candidate = (operation.index, 0, matrix_width - operation.index, matrix_height)
|
|
400
|
+
elif isinstance(operation, ReplaceRow):
|
|
401
|
+
candidate = (0, operation.index, matrix_width, 1)
|
|
402
|
+
elif isinstance(operation, PushRow):
|
|
403
|
+
candidate = (0, operation.index, matrix_width, matrix_height - operation.index)
|
|
404
|
+
else:
|
|
405
|
+
return None
|
|
406
|
+
rect = _union_dirty_rect(rect, candidate)
|
|
407
|
+
return rect
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _union_dirty_rect(
|
|
411
|
+
first: tuple[int, int, int, int] | None,
|
|
412
|
+
second: tuple[int, int, int, int] | None,
|
|
413
|
+
) -> tuple[int, int, int, int] | None:
|
|
414
|
+
if first is None or second is None:
|
|
415
|
+
return None
|
|
416
|
+
if first[2] == 0 or first[3] == 0:
|
|
417
|
+
return second
|
|
418
|
+
if second[2] == 0 or second[3] == 0:
|
|
419
|
+
return first
|
|
420
|
+
left = min(first[0], second[0])
|
|
421
|
+
top = min(first[1], second[1])
|
|
422
|
+
right = max(first[0] + first[2], second[0] + second[2])
|
|
423
|
+
bottom = max(first[1] + first[3], second[1] + second[3])
|
|
424
|
+
return (left, top, right - left, bottom - top)
|
|
425
|
+
|
|
426
|
+
|
|
342
427
|
def _coerce_float32(value: object, expected_shape: tuple[int, ...], label: str) -> object:
|
|
343
428
|
return accel.coerce_float32(value, expected_shape, label)
|
|
344
429
|
|
|
@@ -86,12 +86,14 @@ def write_android_launch_config(
|
|
|
86
86
|
_ensure_android_generated_gitignore(project)
|
|
87
87
|
dest = project / "app" / "src" / "main" / "assets" / "luvatrix_launch_config.json"
|
|
88
88
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
89
|
-
|
|
89
|
+
manifest = _read_app_manifest(app_dir)
|
|
90
|
+
display = _read_app_display_config(manifest)
|
|
91
|
+
resolved_render_mode = _resolve_manifest_render_mode(render_mode, manifest)
|
|
90
92
|
data = {
|
|
91
93
|
"app_dir": "luvatrix_app",
|
|
92
94
|
"source_app_dir": str(app_dir),
|
|
93
95
|
"render_scale": float(render_scale),
|
|
94
|
-
"render_mode":
|
|
96
|
+
"render_mode": resolved_render_mode,
|
|
95
97
|
"target_fps": target_fps,
|
|
96
98
|
"present_fps": present_fps,
|
|
97
99
|
"low_latency_mode": bool(low_latency_mode),
|
|
@@ -126,12 +128,15 @@ def _ensure_android_generated_gitignore(project_dir: Path) -> Path:
|
|
|
126
128
|
return gitignore
|
|
127
129
|
|
|
128
130
|
|
|
129
|
-
def
|
|
130
|
-
manifest = app_dir / "app.toml"
|
|
131
|
+
def _read_app_manifest(app_dir: Path) -> dict[str, object]:
|
|
131
132
|
try:
|
|
132
|
-
raw = tomllib.loads(
|
|
133
|
+
raw = tomllib.loads((app_dir / "app.toml").read_text(encoding="utf-8"))
|
|
133
134
|
except Exception:
|
|
134
135
|
return {}
|
|
136
|
+
return raw if isinstance(raw, dict) else {}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _read_app_display_config(raw: dict[str, object]) -> dict[str, int]:
|
|
135
140
|
display = raw.get("display")
|
|
136
141
|
if not isinstance(display, dict):
|
|
137
142
|
return {}
|
|
@@ -146,6 +151,16 @@ def _read_app_display_config(app_dir: Path) -> dict[str, int]:
|
|
|
146
151
|
return out
|
|
147
152
|
|
|
148
153
|
|
|
154
|
+
def _resolve_manifest_render_mode(requested: str, raw: dict[str, object]) -> str:
|
|
155
|
+
if requested != "auto":
|
|
156
|
+
return requested
|
|
157
|
+
render = raw.get("render")
|
|
158
|
+
if not isinstance(render, dict):
|
|
159
|
+
return requested
|
|
160
|
+
preferred = str(render.get("preferred") or "").strip()
|
|
161
|
+
return preferred if preferred in ("matrix", "scene") else requested
|
|
162
|
+
|
|
163
|
+
|
|
149
164
|
def _android_device_logical_display_config(*, device_id: str | None = None) -> dict[str, int]:
|
|
150
165
|
adb = shutil.which("adb")
|
|
151
166
|
if adb is None:
|