ry_gfx/render_queue.rs
1//! # RyDit Render Queue - Command Queue + Double Buffering + Platform Sync
2//!
3//! **TRES CAPAS CRÍTICAS PARA RENDIMIENTO Y COMPATIBILIDAD**
4//!
5//! 1. **Command Queue (8192+ draw calls)** - Acumular comandos de dibujado
6//! 2. **Double Buffering** - Separar lógica de renderizado
7//! 3. **Platform Sync (XFlush/XSync)** - Sincronización con X11
8//!
9//! ## Arquitectura
10//!
11//! ```text
12//! ┌─────────────────────────────────────────────────────────┐
13//! │ RYDIT RENDER QUEUE │
14//! ├─────────────────────────────────────────────────────────┤
15//! │ │
16//! │ ┌─────────────────────────────────────────────────┐ │
17//! │ │ 1. COMMAND QUEUE (8192+ draw calls) │ │
18//! │ │ - Acumular: circle, rect, line, text, etc. │ │
19//! │ │ - Ejecutar: batch processing │ │
20//! │ │ - Buffer circular: head, tail, capacity │ │
21//! │ └─────────────────────────────────────────────────┘ │
22//! │ │ │
23//! │ ┌──────────────────────▼──────────────────────────┐ │
24//! │ │ 2. DOUBLE BUFFERING │ │
25//! │ │ - Front buffer: lógica (executor) │ │
26//! │ │ - Back buffer: render (draw calls) │ │
27//! │ │ - Swap: intercambiar buffers por frame │ │
28//! │ └─────────────────────────────────────────────────┘ │
29//! │ │ │
30//! │ ┌──────────────────────▼──────────────────────────┐ │
31//! │ │ 3. PLATFORM SYNC (X11) │ │
32//! │ │ - XFlush: forzar flush de comandos │ │
33//! │ │ - XSync: sincronizar con servidor X11 │ │
34//! │ │ - glFlush: OpenGL buffer swap │ │
35//! │ └─────────────────────────────────────────────────┘ │
36//! │ │
37//! └─────────────────────────────────────────────────────────┘
38//! ```
39//!
40//! ## Uso
41//!
42//! ```rust,no_run
43//! use ry_gfx::render_queue::{RenderQueue, DrawCommand};
44//!
45//! let mut queue = RenderQueue::with_capacity(8192);
46//!
47//! // Acumular comandos (front buffer - lógica)
48//! queue.push(DrawCommand::Circle { x: 400, y: 300, radius: 50, color: "rojo" });
49//! queue.push(DrawCommand::Rect { x: 100, y: 100, w: 100, h: 100, color: "verde" });
50//!
51//! // Ejecutar todos los comandos (back buffer - render)
52//! queue.execute(&mut gfx);
53//!
54//! // Platform sync (X11)
55//! queue.platform_sync();
56//! ```
57
58use crate::Assets;
59use crate::{ColorRydit, RyditGfx};
60use std::collections::VecDeque;
61
62// ✅ v0.10.8: PlatformSync ahora viene de v-shield
63pub use v_shield::platform_sync::{PlatformSync, PlatformSyncMode};
64
65// ============================================================================
66// DRAW COMMANDS - Tipos de comandos de dibujado
67// ============================================================================
68
69/// Comandos de dibujado para la queue
70#[derive(Debug, Clone)]
71pub enum DrawCommand {
72 /// draw.circle(x, y, radius, color)
73 Circle {
74 x: i32,
75 y: i32,
76 radius: i32,
77 color: ColorRydit,
78 },
79 /// draw.rect(x, y, w, h, color)
80 Rect {
81 x: i32,
82 y: i32,
83 w: i32,
84 h: i32,
85 color: ColorRydit,
86 },
87 /// draw.line(x1, y1, x2, y2, color)
88 Line {
89 x1: i32,
90 y1: i32,
91 x2: i32,
92 y2: i32,
93 color: ColorRydit,
94 },
95 /// draw.text(text, x, y, size, color)
96 Text {
97 text: String,
98 x: i32,
99 y: i32,
100 size: i32,
101 color: ColorRydit,
102 },
103 /// draw.triangle(v1, v2, v3, color)
104 Triangle {
105 v1: (i32, i32),
106 v2: (i32, i32),
107 v3: (i32, i32),
108 color: ColorRydit,
109 },
110 /// draw.texture(id, x, y, scale, rotation, color)
111 Texture {
112 id: String,
113 x: f32,
114 y: f32,
115 scale: f32,
116 rotation: f32,
117 color: ColorRydit,
118 },
119 /// Limpiar pantalla
120 Clear { color: ColorRydit },
121}
122
123// ============================================================================
124// RENDER QUEUE - Command Queue (8192+ draw calls)
125// ============================================================================
126
127/// Command Queue para draw calls
128///
129/// **Capacidad**: 8192+ comandos por frame
130/// **Estrategia**: Buffer circular con head/tail
131pub struct RenderQueue {
132 /// Cola de comandos (buffer circular)
133 commands: VecDeque<DrawCommand>,
134 /// Capacidad máxima
135 capacity: usize,
136 /// Comandos acumulados en este frame
137 frame_count: usize,
138 /// Total de comandos ejecutados
139 total_executed: usize,
140 /// Estadísticas: máximo de comandos en un frame
141 max_frame_count: usize,
142}
143
144impl RenderQueue {
145 /// Crear queue con capacidad por defecto (8192)
146 pub fn new() -> Self {
147 Self::with_capacity(8192)
148 }
149
150 /// Crear queue con capacidad custom
151 pub fn with_capacity(capacity: usize) -> Self {
152 println!(
153 "[RENDER QUEUE] Command Queue creada: capacidad={} (8192+ draw calls)",
154 capacity
155 );
156 Self {
157 commands: VecDeque::with_capacity(capacity),
158 capacity,
159 frame_count: 0,
160 total_executed: 0,
161 max_frame_count: 0,
162 }
163 }
164
165 /// Push de comando a la queue
166 pub fn push(&mut self, command: DrawCommand) {
167 // Verificar overflow
168 if self.commands.len() >= self.capacity {
169 // Buffer lleno: remover el comando más antiguo (política FIFO)
170 self.commands.pop_front();
171 #[cfg(debug_assertions)]
172 eprintln!(
173 "[RENDER QUEUE] WARNING: Buffer lleno ({}), removiendo comando antiguo",
174 self.capacity
175 );
176 }
177
178 self.commands.push_back(command);
179 self.frame_count += 1;
180 }
181
182 /// Ejecutar todos los comandos acumulados
183 pub fn execute(&mut self, gfx: &mut RyditGfx, assets: &Assets) {
184 if self.commands.is_empty() {
185 return;
186 }
187
188 // Actualizar estadísticas
189 if self.frame_count > self.max_frame_count {
190 self.max_frame_count = self.frame_count;
191 }
192 self.total_executed += self.frame_count;
193
194 #[cfg(debug_assertions)]
195 eprintln!(
196 "[RENDER QUEUE] Ejecutando {} comandos (total: {}, max frame: {})",
197 self.frame_count, self.total_executed, self.max_frame_count
198 );
199
200 // Ejecutar todos los comandos EN UN SOLO begin_draw
201 {
202 let mut d = gfx.begin_draw();
203
204 for command in self.commands.drain(..) {
205 match command {
206 DrawCommand::Circle {
207 x,
208 y,
209 radius,
210 color,
211 } => {
212 d.draw_circle(x, y, radius, color);
213 }
214 DrawCommand::Rect { x, y, w, h, color } => {
215 d.draw_rectangle(x, y, w, h, color);
216 }
217 DrawCommand::Line {
218 x1,
219 y1,
220 x2,
221 y2,
222 color,
223 } => {
224 d.draw_line(x1, y1, x2, y2, color);
225 }
226 DrawCommand::Text {
227 text,
228 x,
229 y,
230 size,
231 color,
232 } => {
233 d.draw_text(&text, x, y, size, color);
234 }
235 DrawCommand::Triangle { v1, v2, v3, color } => {
236 d.draw_triangle(v1, v2, v3, color);
237 }
238 DrawCommand::Texture {
239 id,
240 x,
241 y,
242 scale,
243 rotation,
244 color,
245 } => {
246 // Dibujar textura con escala y rotación
247 assets.draw_texture_ex_by_id(
248 &mut d.draw,
249 &id,
250 x,
251 y,
252 scale,
253 rotation,
254 color,
255 );
256 }
257 DrawCommand::Clear { color } => {
258 d.clear(color);
259 }
260 }
261 }
262
263 // Drop EXPLÍCITO para forzar buffer swap (fix Zink/Vulkan)
264 drop(d);
265 }
266
267 // Reset contador de frame
268 self.frame_count = 0;
269 }
270
271 /// ✅ v0.10.4: Ejecutar comandos con DrawHandle existente (para integrar con partículas)
272 pub fn execute_with_handle(&mut self, d: &mut crate::DrawHandle, assets: &Assets) {
273 if self.commands.is_empty() {
274 return;
275 }
276
277 // Actualizar estadísticas
278 if self.frame_count > self.max_frame_count {
279 self.max_frame_count = self.frame_count;
280 }
281 self.total_executed += self.frame_count;
282
283 for command in self.commands.drain(..) {
284 match command {
285 DrawCommand::Circle {
286 x,
287 y,
288 radius,
289 color,
290 } => {
291 d.draw_circle(x, y, radius, color);
292 }
293 DrawCommand::Rect { x, y, w, h, color } => {
294 d.draw_rectangle(x, y, w, h, color);
295 }
296 DrawCommand::Line {
297 x1,
298 y1,
299 x2,
300 y2,
301 color,
302 } => {
303 d.draw_line(x1, y1, x2, y2, color);
304 }
305 DrawCommand::Text {
306 text,
307 x,
308 y,
309 size,
310 color,
311 } => {
312 d.draw_text(&text, x, y, size, color);
313 }
314 DrawCommand::Triangle { v1, v2, v3, color } => {
315 d.draw_triangle(v1, v2, v3, color);
316 }
317 DrawCommand::Texture {
318 id,
319 x,
320 y,
321 scale,
322 rotation,
323 color,
324 } => {
325 // d es DrawHandle, usar d.draw para obtener RaylibDrawHandle
326 assets.draw_texture_ex_by_id(&mut d.draw, &id, x, y, scale, rotation, color);
327 }
328 DrawCommand::Clear { color } => {
329 d.clear(color);
330 }
331 }
332 }
333
334 // Reset contador de frame
335 self.frame_count = 0;
336 }
337
338 /// Limpiar queue sin ejecutar
339 pub fn clear(&mut self) {
340 self.commands.clear();
341 self.frame_count = 0;
342 }
343
344 /// Obtener cantidad de comandos pendientes
345 pub fn len(&self) -> usize {
346 self.commands.len()
347 }
348
349 /// Verificar si está vacía
350 pub fn is_empty(&self) -> bool {
351 self.commands.is_empty()
352 }
353
354 /// Obtener capacidad máxima
355 pub fn capacity(&self) -> usize {
356 self.capacity
357 }
358
359 /// Obtener estadísticas
360 pub fn stats(&self) -> RenderQueueStats {
361 RenderQueueStats {
362 pending: self.commands.len(),
363 frame_count: self.frame_count,
364 total_executed: self.total_executed,
365 max_frame_count: self.max_frame_count,
366 capacity: self.capacity,
367 }
368 }
369}
370
371impl Default for RenderQueue {
372 fn default() -> Self {
373 Self::new()
374 }
375}
376
377/// Estadísticas de la render queue
378#[derive(Debug, Clone, Default)]
379pub struct RenderQueueStats {
380 pub pending: usize,
381 pub frame_count: usize,
382 pub total_executed: usize,
383 pub max_frame_count: usize,
384 pub capacity: usize,
385}
386
387impl std::fmt::Display for RenderQueueStats {
388 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 write!(
390 f,
391 "RenderQueue {{ pending: {}, frame: {}, total: {}, max: {}, capacity: {} }}",
392 self.pending,
393 self.frame_count,
394 self.total_executed,
395 self.max_frame_count,
396 self.capacity
397 )
398 }
399}
400
401// ============================================================================
402// DOUBLE BUFFERING - Separar lógica de renderizado
403// ============================================================================
404
405/// Double Buffer para draw commands
406///
407/// **Front Buffer**: Lógica acumula comandos (executor)
408/// **Back Buffer**: Render ejecuta comandos (gfx)
409/// **Swap**: Intercambiar buffers al final del frame
410pub struct DoubleBuffer {
411 /// Front buffer (acumulación - lógica)
412 front: RenderQueue,
413 /// Back buffer (ejecución - render)
414 back: RenderQueue,
415 /// Frame actual
416 frame: u64,
417}
418
419impl DoubleBuffer {
420 /// Crear double buffer con capacidad
421 pub fn new(capacity: usize) -> Self {
422 println!("[DOUBLE BUFFER] Creado con capacidad={}", capacity);
423 Self {
424 front: RenderQueue::with_capacity(capacity),
425 back: RenderQueue::with_capacity(capacity),
426 frame: 0,
427 }
428 }
429
430 /// Push al front buffer (lógica)
431 pub fn push(&mut self, command: DrawCommand) {
432 self.front.push(command);
433 }
434
435 /// Swap de buffers: front → back
436 /// Retorna el back buffer anterior para ejecutar
437 pub fn swap(&mut self) {
438 self.frame += 1;
439
440 // Intercambiar buffers
441 std::mem::swap(&mut self.front, &mut self.back);
442
443 #[cfg(debug_assertions)]
444 eprintln!(
445 "[DOUBLE BUFFER] Frame {} - Swap completado (back: {} comandos)",
446 self.frame,
447 self.back.len()
448 );
449 }
450
451 /// Ejecutar back buffer
452 pub fn execute(&mut self, gfx: &mut RyditGfx, assets: &Assets) {
453 self.back.execute(gfx, assets);
454 self.back.clear();
455 }
456
457 /// Swap + execute (operación combinada)
458 pub fn swap_and_execute(&mut self, gfx: &mut RyditGfx, assets: &Assets) {
459 self.swap();
460 self.execute(gfx, assets);
461 }
462
463 /// Obtener frame actual
464 pub fn frame(&self) -> u64 {
465 self.frame
466 }
467
468 /// Obtener estadísticas de ambos buffers
469 pub fn stats(&self) -> (RenderQueueStats, RenderQueueStats) {
470 (self.front.stats(), self.back.stats())
471 }
472}
473
474// ✅ v0.10.8: PlatformSync migrado a v-shield (re-export arriba)
475
476// ============================================================================
477// INTEGRACIÓN CON RyditGfx
478// ============================================================================
479
480/// Extensión para RyditGfx con render queue
481pub trait RyditGfxExt {
482 /// Ejecutar comandos desde queue
483 fn execute_queue(&mut self, queue: &mut RenderQueue, assets: &Assets);
484
485 /// Platform sync (X11)
486 fn platform_sync(&mut self);
487}
488
489impl RyditGfxExt for RyditGfx {
490 fn execute_queue(&mut self, queue: &mut RenderQueue, assets: &Assets) {
491 queue.execute(self, assets);
492 }
493
494 fn platform_sync(&mut self) {
495 // Platform sync se maneja externamente
496 // El buffer swap ya ocurre en Drop de DrawHandle
497 }
498}