1use crate::asset_manager::NativeAssetManager;
2use crate::audio::{RodioAudioEngine, VisualHapticEngine};
3use crate::main_loop::{App, AppEvent};
4use crate::window::{SafeAreaInsets, WindowManager};
5use cvkg_core::{
6 ColorTheme, DrawMaterial, FrameRenderer, Mesh, Rect, RenderIntensityMode, RenderStateSnapshot,
7 Renderer, TelemetryData,
8};
9use std::sync::Arc;
10use winit::event_loop::{ControlFlow, EventLoop};
11#[cfg(target_os = "linux")]
12use winit::platform::wayland::EventLoopBuilderExtWayland;
13use winit::window::Window;
14
15thread_local! {
20 pub static GPU_FRAME_PTR: std::cell::Cell<*mut cvkg_render_gpu::GpuRenderer> =
21 const { std::cell::Cell::new(std::ptr::null_mut()) };
22}
23
24pub struct NativeRenderer {
28 pub(crate) gpu: Arc<std::sync::Mutex<cvkg_render_gpu::GpuRenderer>>,
29 pub(crate) delta_time: f32,
30 pub(crate) elapsed_time: f32,
31 pub(crate) berserker_mode: RenderIntensityMode,
32 pub(crate) rage: f32,
33 pub(crate) window: Arc<Window>,
34}
35
36impl NativeRenderer {
37 #[inline(always)]
44 fn gpu_ref(&mut self) -> impl std::ops::DerefMut<Target = cvkg_render_gpu::GpuRenderer> + '_ {
45 GPU_FRAME_PTR.with(|ptr| {
46 let raw = ptr.get();
47 if !raw.is_null() {
48 GpuRef::Ptr(unsafe { &mut *raw })
50 } else {
51 GpuRef::Guard(self.gpu.lock().unwrap_or_else(|p| p.into_inner()))
52 }
53 })
54 }
55
56 #[inline(always)]
62 fn gpu_ref_shared(&self) -> impl std::ops::Deref<Target = cvkg_render_gpu::GpuRenderer> + '_ {
63 GPU_FRAME_PTR.with(|ptr| {
64 let raw = ptr.get();
65 if !raw.is_null() {
66 GpuRefShared::Ptr(unsafe { &*raw })
69 } else {
70 GpuRefShared::Guard(self.gpu.lock().unwrap_or_else(|p| p.into_inner()))
71 }
72 })
73 }
74
75 pub(crate) fn new(
77 window: Arc<Window>,
78 gpu: Arc<std::sync::Mutex<cvkg_render_gpu::GpuRenderer>>,
79 delta_time: f32,
80 elapsed_time: f32,
81 berserker_mode: RenderIntensityMode,
82 rage: f32,
83 ) -> Self {
84 Self {
85 gpu,
86 delta_time,
87 elapsed_time,
88 berserker_mode,
89 rage,
90 window,
91 }
92 }
93
94 pub fn run<V: cvkg_core::View + 'static>(
98 view: V,
99 prewarm_assets: Option<Vec<(String, Vec<u8>)>>,
100 ) {
101 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
102 .format_timestamp_millis()
103 .init();
104
105 let event_loop = EventLoop::<AppEvent>::with_user_event()
106 .with_any_thread(true)
107 .build()
108 .expect("failed to create winit event loop: platform initialization failed");
109 event_loop.set_control_flow(ControlFlow::Wait);
110
111 let mut app = App {
112 view,
113 window_manager: WindowManager::new(),
114 gpu: None,
115 asset_manager: std::sync::Arc::new(NativeAssetManager::new()),
116 proxy: event_loop.create_proxy(),
117 start_time: std::time::Instant::now(),
118 last_frame_time: std::time::Instant::now(),
119 berserker_mode: RenderIntensityMode::Normal,
120 rage: 0.0,
121 state_detector: crate::window::WindowStateDetector::new(),
122 frame_budget: cvkg_core::FrameBudgetTracker::default_120fps(),
123 modifiers: winit::keyboard::ModifiersState::default(),
124 audio_engine: None,
125 haptic_engine: Arc::new(VisualHapticEngine::new()),
126 pending_prewarm: prewarm_assets,
127 };
128
129 event_loop
130 .run_app(&mut app)
131 .expect("winit event loop terminated with error");
132 }
133
134 pub fn run_with_background<V: cvkg_core::View + 'static>(
138 view: V,
139 image_name: &str,
140 image_path: &str,
141 ) {
142 let image_data = std::fs::read(image_path)
143 .unwrap_or_else(|e| panic!("Failed to load background image '{}': {}", image_path, e));
144 let assets = vec![(image_name.to_string(), image_data)];
145 Self::run(view, Some(assets));
146 }
147}
148
149enum GpuRef<'a> {
151 Ptr(&'a mut cvkg_render_gpu::GpuRenderer),
152 Guard(std::sync::MutexGuard<'a, cvkg_render_gpu::GpuRenderer>),
153}
154
155impl<'a> std::ops::Deref for GpuRef<'a> {
156 type Target = cvkg_render_gpu::GpuRenderer;
157 fn deref(&self) -> &Self::Target {
158 match self {
159 GpuRef::Ptr(r) => r,
160 GpuRef::Guard(g) => g,
161 }
162 }
163}
164
165impl<'a> std::ops::DerefMut for GpuRef<'a> {
166 fn deref_mut(&mut self) -> &mut Self::Target {
167 match self {
168 GpuRef::Ptr(r) => r,
169 GpuRef::Guard(g) => &mut *g,
170 }
171 }
172}
173
174enum GpuRefShared<'a> {
176 Ptr(&'a cvkg_render_gpu::GpuRenderer),
177 Guard(std::sync::MutexGuard<'a, cvkg_render_gpu::GpuRenderer>),
178}
179
180impl<'a> std::ops::Deref for GpuRefShared<'a> {
181 type Target = cvkg_render_gpu::GpuRenderer;
182 fn deref(&self) -> &Self::Target {
183 match self {
184 GpuRefShared::Ptr(r) => r,
185 GpuRefShared::Guard(g) => g,
186 }
187 }
188}
189
190impl cvkg_core::ElapsedTime for NativeRenderer {
191 fn delta_time(&self) -> f32 {
192 self.delta_time
193 }
194
195 fn elapsed_time(&self) -> f32 {
196 self.elapsed_time
197 }
198}
199
200impl cvkg_core::RendererErrorHandler for NativeRenderer {}
201
202impl cvkg_core::Renderer for NativeRenderer {
203 fn fill_rect(&mut self, rect: Rect, color: [f32; 4]) {
204 self.gpu_ref().fill_rect(rect, color);
205 }
206 fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]) {
207 self.gpu_ref().fill_rounded_rect(rect, radius, color);
208 }
209 fn fill_ellipse(&mut self, rect: Rect, color: [f32; 4]) {
210 self.gpu_ref().fill_ellipse(rect, color);
211 }
212 fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32) {
213 self.gpu_ref().stroke_rect(rect, color, stroke_width);
214 }
215 fn stroke_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4], stroke_width: f32) {
216 self.gpu_ref()
217 .stroke_rounded_rect(rect, radius, color, stroke_width);
218 }
219 fn stroke_ellipse(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32) {
220 self.gpu_ref().stroke_ellipse(rect, color, stroke_width);
221 }
222 fn draw_line(
223 &mut self,
224 x1: f32,
225 y1: f32,
226 x2: f32,
227 y2: f32,
228 color: [f32; 4],
229 stroke_width: f32,
230 ) {
231 self.gpu_ref()
232 .draw_line(x1, y1, x2, y2, color, stroke_width);
233 }
234
235 fn fill_glass_rect(&mut self, rect: Rect, radius: f32, blur_radius: f32) {
236 self.gpu_ref().fill_glass_rect(rect, radius, blur_radius);
237 }
238
239 fn fill_glass_rect_with_intensity(
240 &mut self,
241 rect: Rect,
242 radius: f32,
243 blur_radius: f32,
244 glass_intensity: f32,
245 ) {
246 self.gpu_ref()
247 .fill_glass_rect_with_intensity(rect, radius, blur_radius, glass_intensity);
248 }
249
250 fn fill_glass_rect_with_pressure(
251 &mut self,
252 rect: Rect,
253 radius: f32,
254 blur_radius: f32,
255 pressure: f32,
256 ) {
257 self.gpu_ref()
258 .fill_glass_rect_with_intensity(rect, radius, blur_radius, pressure);
259 }
260
261 fn fill_squircle(&mut self, rect: Rect, n: f32, color: [f32; 4]) {
262 self.gpu_ref().fill_squircle(rect, n, color);
263 }
264
265 fn stroke_squircle(&mut self, rect: Rect, n: f32, color: [f32; 4], stroke_width: f32) {
266 self.gpu_ref().stroke_squircle(rect, n, color, stroke_width);
267 }
268
269 fn draw_focus_ring(
270 &mut self,
271 rect: Rect,
272 radius: f32,
273 offset: f32,
274 width: f32,
275 color: [f32; 4],
276 ) {
277 self.gpu_ref()
278 .draw_focus_ring(rect, radius, offset, width, color);
279 }
280
281 fn draw_linear_gradient(
282 &mut self,
283 rect: Rect,
284 start_color: [f32; 4],
285 end_color: [f32; 4],
286 angle: f32,
287 ) {
288 self.gpu_ref()
289 .draw_linear_gradient(rect, start_color, end_color, angle);
290 }
291 fn draw_radial_gradient(&mut self, rect: Rect, inner_color: [f32; 4], outer_color: [f32; 4]) {
292 self.gpu_ref()
293 .draw_radial_gradient(rect, inner_color, outer_color);
294 }
295 fn draw_texture(&mut self, texture_id: u32, rect: Rect) {
296 self.gpu_ref().draw_texture(texture_id, rect);
297 }
298 fn draw_image(&mut self, image_name: &str, rect: Rect) {
299 self.gpu_ref().draw_image(image_name, rect);
300 }
301 fn load_image(&mut self, name: &str, data: &[u8]) {
302 self.gpu_ref().load_image(name, data);
303 }
304 fn push_clip_rect(&mut self, rect: Rect) {
305 self.gpu_ref().push_clip_rect(rect);
306 }
307 fn pop_clip_rect(&mut self) {
308 self.gpu_ref().pop_clip_rect();
309 }
310 fn push_opacity(&mut self, opacity: f32) {
311 self.gpu_ref().push_opacity(opacity);
312 }
313 fn draw_3d_cube(&mut self, rect: Rect, color: [f32; 4], rotation: [f32; 3]) {
314 self.gpu_ref().draw_3d_cube(rect, color, rotation);
315 }
316 fn render_scene_node_3d(
317 &mut self,
318 position: [f32; 3],
319 rotation: [f32; 4],
320 scale: [f32; 3],
321 color: [f32; 4],
322 meshes: &[Mesh],
323 ) {
324 self.gpu_ref()
325 .render_scene_node_3d(position, rotation, scale, color, meshes);
326 }
327 fn pop_opacity(&mut self) {
328 self.gpu_ref().pop_opacity();
329 }
330 fn bifrost(&mut self, rect: Rect, blur: f32, saturation: f32, opacity: f32) {
331 self.gpu_ref().bifrost(rect, blur, saturation, opacity);
332 }
333 fn push_mjolnir_slice(&mut self, angle: f32, offset: f32) {
334 self.gpu_ref().push_mjolnir_slice(angle, offset);
335 }
336 fn pop_mjolnir_slice(&mut self) {
337 self.gpu_ref().pop_mjolnir_slice();
338 }
339 fn mjolnir_shatter(&mut self, rect: Rect, pieces: u32, force: f32, color: [f32; 4]) {
340 self.gpu_ref().mjolnir_shatter(rect, pieces, force, color);
341 }
342 fn mjolnir_fluid_shatter(&mut self, rect: Rect, pieces: u32, force: f32, color: [f32; 4]) {
343 self.gpu_ref()
344 .mjolnir_fluid_shatter(rect, pieces, force, color);
345 }
346 fn draw_mjolnir_bolt(&mut self, from: [f32; 2], to: [f32; 2], color: [f32; 4]) {
347 self.gpu_ref().draw_mjolnir_bolt(from, to, color);
348 }
349 fn gungnir(&mut self, rect: Rect, color: [f32; 4], radius: f32, intensity: f32) {
350 self.gpu_ref().gungnir(rect, color, radius, intensity);
351 }
352 fn mani_glow(&mut self, rect: Rect, color: [f32; 4], radius: f32) {
353 self.gpu_ref().mani_glow(rect, color, radius);
354 }
355 fn register_handler(
356 &mut self,
357 event_type: &str,
358 handler: Arc<dyn Fn(cvkg_core::Event) + Send + Sync>,
359 ) {
360 self.gpu_ref().register_handler(event_type, handler);
361 }
362 fn push_vnode(&mut self, rect: Rect, name: &'static str) {
363 self.gpu_ref().push_vnode(rect, name);
364 }
365 fn pop_vnode(&mut self) {
366 self.gpu_ref().pop_vnode();
367 }
368 fn set_z_index(&mut self, z: f32) {
369 self.gpu_ref().set_z_index(z);
370 }
371 fn get_z_index(&self) -> f32 {
372 self.gpu_ref_shared().get_z_index()
373 }
374 fn register_shared_element(&mut self, id: &str, rect: Rect) {
375 self.gpu_ref().register_shared_element(id, rect);
376 }
377 fn set_material(&mut self, material: DrawMaterial) {
378 self.gpu_ref().set_material(material);
379 }
380 fn current_material(&self) -> DrawMaterial {
381 self.gpu_ref_shared().current_material()
382 }
383 fn serialize_svg(&mut self, name: &str) -> Result<String, String> {
384 self.gpu_ref().serialize_svg(name)
385 }
386 fn apply_svg_filter(
387 &mut self,
388 name: &str,
389 filter_id: &str,
390 region: Rect,
391 ) -> Result<String, String> {
392 self.gpu_ref().apply_svg_filter(name, filter_id, region)
393 }
394 fn push_shadow(&mut self, radius: f32, color: [f32; 4], offset: [f32; 2]) {
395 self.gpu_ref().push_shadow(radius, color, offset);
396 }
397 fn pop_shadow(&mut self) {
398 self.gpu_ref().pop_shadow();
399 }
400 fn push_affine(&mut self, transform: [f32; 6]) {
401 self.gpu_ref().push_affine(transform);
402 }
403 fn enter_portal(&mut self, z_index: i32) {
404 log::warn!(
405 "Portal rendering (enter_portal) not yet implemented in GPU backend; z_index={}",
406 z_index
407 );
408 }
409 fn exit_portal(&mut self) {
410 log::warn!("Portal rendering (exit_portal) not yet implemented in GPU backend");
411 }
412 fn viewport_size(&self) -> Rect {
413 let size = self.window.inner_size();
414 let scale = self.window.scale_factor();
415 let logical = size.to_logical::<f32>(scale);
416 Rect::new(0.0, 0.0, logical.width, logical.height)
417 }
418 fn announce(&mut self, message: &str, priority: cvkg_core::AnnouncementPriority) {
419 log::info!("Accessibility announcement [{:?}]: {}", priority, message);
420 }
421 fn load_svg(&mut self, name: &str, svg_data: &[u8]) {
422 self.gpu_ref().load_svg(name, svg_data);
423 }
424 fn draw_svg(&mut self, name: &str, rect: Rect) {
425 self.gpu_ref().draw_svg(name, rect, None, 0);
426 }
427 fn draw_svg_with_offset(&mut self, name: &str, rect: Rect, animation_time_offset: f32) {
428 self.gpu_ref()
429 .draw_svg_with_offset(name, rect, None, 0, animation_time_offset);
430 }
431 fn get_telemetry(&self) -> TelemetryData {
432 self.gpu_ref_shared().telemetry.clone()
433 }
434 fn prewarm_vram(&mut self, assets: Vec<(String, Vec<u8>)>) {
435 self.gpu_ref().prewarm_vram(assets);
436 }
437
438 fn text_scale_factor(&self) -> f32 {
439 self.gpu_ref_shared().text_scale_factor()
440 }
441
442 fn is_over_budget(&self) -> bool {
443 self.gpu_ref_shared().is_over_budget()
444 }
445
446 fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]) {
447 self.gpu_ref().draw_text(text, x, y, size, color);
448 }
449
450 fn measure_text(&mut self, text: &str, size: f32) -> (f32, f32) {
451 self.gpu_ref().measure_text(text, size)
452 }
453
454 fn shape_rich_text(
455 &mut self,
456 spans: &[runic_text::TextSpan],
457 max_width: Option<f32>,
458 align: runic_text::TextAlign,
459 overflow: runic_text::TextOverflow,
460 ) -> Option<runic_text::ShapedText> {
461 self.gpu_ref()
462 .shape_rich_text(spans, max_width, align, overflow)
463 }
464
465 fn draw_shaped_text(&mut self, shaped: &runic_text::ShapedText, x: f32, y: f32) {
466 self.gpu_ref().draw_shaped_text(shaped, x, y);
467 }
468
469 fn fill_glass_rect_with_tint(
470 &mut self,
471 rect: Rect,
472 radius: f32,
473 blur_radius: f32,
474 tint_color: [f32; 4],
475 glass_intensity: f32,
476 ) {
477 self.gpu_ref().fill_glass_rect_with_tint(
478 rect,
479 radius,
480 blur_radius,
481 tint_color,
482 glass_intensity,
483 );
484 }
485
486 fn set_theme(&mut self, theme: ColorTheme) {
487 self.gpu_ref().set_theme(theme);
488 }
489
490 fn trigger_shatter_event(&mut self, origin: [f32; 2], force: f32) {
491 self.gpu_ref().trigger_shatter_event(origin, force);
492 }
493
494 fn set_fireball_pos(&mut self, pos: [f32; 2]) {
495 self.gpu_ref().set_fireball_pos(pos);
496 }
497
498 fn set_scene(&mut self, scene: &str) {
499 self.gpu_ref().set_scene(scene);
500 }
501
502 fn set_scene_preset(&mut self, preset: u32) {
503 self.gpu_ref().set_scene_preset(preset);
504 }
505
506 fn set_default_background_color(&mut self, color: [f32; 4]) {
507 self.gpu_ref().set_default_background_color(color);
508 }
509 fn push_transform(&mut self, translation: [f32; 2], scale: [f32; 2], rotation: f32) {
510 self.gpu_ref().push_transform(translation, scale, rotation);
511 }
512 fn pop_transform(&mut self) {
513 self.gpu_ref().pop_transform();
514 }
515
516 fn set_berserker_mode(&mut self, state: RenderIntensityMode) {
517 self.berserker_mode = state;
518
519 if state == RenderIntensityMode::GodMode {
520 log::info!("ENTERING GOD MODE: Activating Berserker Determinism (High Priority)");
521 #[cfg(target_os = "linux")]
522 unsafe {
523 let _ = libc::setpriority(libc::PRIO_PROCESS, 0, -10);
524 }
525 } else {
526 #[cfg(target_os = "linux")]
527 unsafe {
528 let _ = libc::setpriority(libc::PRIO_PROCESS, 0, 0);
529 }
530 }
531
532 self.gpu_ref().set_berserker_mode(state);
533 }
534
535 fn set_rage(&mut self, rage: f32) {
536 self.rage = rage;
537 self.gpu_ref().set_rage(rage);
538 }
539
540 fn memoize(&mut self, id: u64, data_hash: u64, render_fn: &dyn Fn(&mut dyn Renderer)) {
541 self.gpu_ref().memoize(id, data_hash, render_fn);
542 }
543
544 fn snapshot_render_state(&self) -> RenderStateSnapshot {
545 self.gpu_ref_shared().snapshot_render_state()
546 }
547
548 fn restore_render_state(&mut self, snap: RenderStateSnapshot) {
549 self.gpu_ref().restore_render_state(snap);
550 }
551 fn request_redraw(&mut self) {
552 self.window.request_redraw();
553 }
554
555 fn capture_png(&mut self) -> Vec<u8> {
556 log::info!("CAPTURING_FRAME: Initiating GPU readback...");
557 let gpu = self.gpu.lock().unwrap_or_else(|p| p.into_inner());
558 pollster::block_on(gpu.capture_frame()).unwrap_or_else(|e| {
559 log::error!("GPU frame capture failed: {}", e);
560 Vec::new()
561 })
562 }
563
564 fn print(&mut self) {
565 log::info!("PRINT_BRIDGE: Spooling mission status to native printer...");
566 println!("[BRIDGE] PRINTER_READY // SPOOLING_DATA...");
567 }
568}