variable scale menu
[carveJwlIkooP6JGAAIwe30JlM.git] / render.h
1 /*
2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #include "common.h"
6 #include "model.h"
7 #include "camera.h"
8 #include "world.h"
9
10 #include "shaders/blit.h"
11 #include "shaders/blitblur.h"
12 #include "shaders/blitcolour.h"
13
14 #if 0
15 #include "shaders/standard.h"
16 #include "shaders/vblend.h"
17 #endif
18
19 VG_STATIC void render_water_texture( world_instance *world, camera *cam,
20 int layer_depth );
21 VG_STATIC void render_water_surface( world_instance *world, camera *cam );
22 VG_STATIC void render_world( world_instance *world, camera *cam,
23 int layer_depth );
24 VG_STATIC void render_world_depth( world_instance *world, camera *cam );
25
26 #ifndef RENDER_H
27 #define RENDER_H
28
29 typedef struct framebuffer framebuffer;
30
31 /*
32 * All standard buffers used in rendering
33 */
34 VG_STATIC struct pipeline
35 {
36 glmesh fsquad;
37
38 framebuffer *fb_main,
39 *fb_water_reflection,
40 *fb_water_beneath;
41 int ready;
42
43 float view_render_scale,
44 water_render_scale;
45 }
46 gpipeline = { .view_render_scale = 1.0f };
47
48 struct framebuffer
49 {
50 const char *display_name;
51 int resolution_div, /* definition */
52 fixed_w,
53 fixed_h,
54
55 render_w, /* runtime */
56 render_h;
57
58 struct framebuffer_attachment
59 {
60 const char *display_name;
61
62 enum framebuffer_attachment_type
63 {
64 k_framebuffer_attachment_type_none,
65 k_framebuffer_attachment_type_colour,
66 k_framebuffer_attachment_type_renderbuffer
67 }
68 purpose;
69
70 enum framebuffer_quality_profile
71 {
72 k_framebuffer_quality_all,
73 k_framebuffer_quality_high_only
74 }
75 quality;
76
77 GLenum internalformat,
78 format,
79 type,
80 attachment;
81
82 GLuint id;
83
84 /* Runtime */
85 int debug_view;
86 }
87 attachments[5];
88 GLuint fb;
89 framebuffer **link;
90 }
91 framebuffers[] =
92 {
93 {
94 /*
95 * The primary draw target
96 */
97 "main",
98 .link = &gpipeline.fb_main,
99 .resolution_div = 1,
100 .attachments =
101 {
102 {
103 "colour", k_framebuffer_attachment_type_colour,
104
105 .internalformat = GL_RGB,
106 .format = GL_RGB,
107 .type = GL_UNSIGNED_BYTE,
108 .attachment = GL_COLOR_ATTACHMENT0
109 },
110 {
111 "motion", k_framebuffer_attachment_type_colour,
112
113 .quality = k_framebuffer_quality_high_only,
114 .internalformat = GL_RG16F,
115 .format = GL_RG,
116 .type = GL_FLOAT,
117 .attachment = GL_COLOR_ATTACHMENT1
118 },
119 {
120 "depth_stencil", k_framebuffer_attachment_type_renderbuffer,
121
122 .internalformat = GL_DEPTH24_STENCIL8,
123 .attachment = GL_DEPTH_STENCIL_ATTACHMENT
124 }
125 }
126 },
127 {
128 /*
129 * Second rendered view from the perspective of the water reflection
130 */
131 "water_reflection",
132 .link = &gpipeline.fb_water_reflection,
133 .resolution_div = 2,
134 .attachments =
135 {
136 {
137 "colour", k_framebuffer_attachment_type_colour,
138 .internalformat = GL_RGB,
139 .format = GL_RGB,
140 .type = GL_UNSIGNED_BYTE,
141 .attachment = GL_COLOR_ATTACHMENT0
142 },
143 {
144 "depth_stencil", k_framebuffer_attachment_type_renderbuffer,
145
146 .internalformat = GL_DEPTH24_STENCIL8,
147 .attachment = GL_DEPTH_STENCIL_ATTACHMENT
148 }
149 }
150 },
151 {
152 /*
153 * Thid rendered view from the perspective of the camera, but just
154 * captures stuff thats under the water
155 */
156 "water_beneath",
157 .link = &gpipeline.fb_water_beneath,
158 .resolution_div = 2,
159 .attachments =
160 {
161 {
162 "colour", k_framebuffer_attachment_type_colour,
163 .internalformat = GL_RED,
164 .format = GL_RED,
165 .type = GL_UNSIGNED_BYTE,
166 .attachment = GL_COLOR_ATTACHMENT0
167 },
168 {
169 "depth_stencil", k_framebuffer_attachment_type_renderbuffer,
170
171 .internalformat = GL_DEPTH24_STENCIL8,
172 .attachment = GL_DEPTH_STENCIL_ATTACHMENT
173 }
174 }
175 }
176 };
177
178 /*
179 * Get the current (automatically scaled or fixed) resolution of framebuffer
180 */
181 VG_STATIC void render_fb_get_current_res( struct framebuffer *fb,
182 int *x, int *y )
183 {
184 if( fb->resolution_div ){
185 *x = vg.window_x / fb->resolution_div;
186 *y = vg.window_y / fb->resolution_div;
187 }
188 else{
189 *x = fb->fixed_w;
190 *y = fb->fixed_h;
191 }
192 }
193
194 VG_STATIC void render_fb_inverse_ratio( framebuffer *fb, v2f inverse )
195 {
196 int x, y;
197 render_fb_get_current_res( fb, &x, &y );
198
199 v2f render = { fb->render_w, fb->render_h },
200 original = { x, y };
201
202 v2_div( render, original, inverse );
203 }
204
205 /*
206 * Bind framebuffer for drawing to
207 */
208 VG_STATIC void render_fb_bind( framebuffer *fb, int use_scaling )
209 {
210 int x, y;
211 render_fb_get_current_res( fb, &x, &y );
212
213 if( use_scaling ){
214 x = gpipeline.view_render_scale*(float)x;
215 y = gpipeline.view_render_scale*(float)y;
216
217 x = VG_MAX( 16, x );
218 y = VG_MAX( 16, y );
219
220 fb->render_w = x;
221 fb->render_h = y;
222 }
223
224 glBindFramebuffer( GL_FRAMEBUFFER, fb->fb );
225 glViewport( 0, 0, x, y );
226 }
227
228 /*
229 * Bind framebuffer attachment's texture
230 */
231 VG_STATIC void render_fb_bind_texture( framebuffer *fb,
232 int attachment, int slot )
233 {
234 struct framebuffer_attachment *at = &fb->attachments[attachment];
235
236 if( at->purpose != k_framebuffer_attachment_type_colour )
237 {
238 vg_fatal_exit_loop( "illegal operation: bind non-colour framebuffer"
239 " attachment to texture slot" );
240 }
241
242 glActiveTexture( GL_TEXTURE0 + slot );
243 glBindTexture( GL_TEXTURE_2D, fb->attachments[attachment].id );
244 }
245
246
247 /*
248 * Shaders
249 */
250
251 #define FB_FORMAT_STR( E ) { E, #E },
252
253 /*
254 * Convert OpenGL attachment ID enum to string
255 */
256 VG_STATIC const char *render_fb_attachment_str( GLenum e )
257 {
258 struct { GLenum e; const char *str; }
259 formats[] =
260 {
261 FB_FORMAT_STR(GL_COLOR_ATTACHMENT0)
262 FB_FORMAT_STR(GL_COLOR_ATTACHMENT1)
263 FB_FORMAT_STR(GL_COLOR_ATTACHMENT2)
264 FB_FORMAT_STR(GL_COLOR_ATTACHMENT3)
265 FB_FORMAT_STR(GL_COLOR_ATTACHMENT4)
266 FB_FORMAT_STR(GL_DEPTH_STENCIL_ATTACHMENT)
267 };
268
269 for( int i=0; i<vg_list_size(formats); i++ )
270 if( formats[i].e == e )
271 return formats[i].str;
272
273 return "UNDEFINED";
274 }
275
276 /*
277 * Convert OpenGL texture format enums from TexImage2D table 1,2 &
278 * RenderBufferStorage Table 1, into strings
279 */
280 VG_STATIC const char *render_fb_format_str( GLenum format )
281 {
282 struct { GLenum e; const char *str; }
283 formats[] =
284 {
285 /* Table 1 */
286 FB_FORMAT_STR(GL_DEPTH_COMPONENT)
287 FB_FORMAT_STR(GL_DEPTH_STENCIL)
288 FB_FORMAT_STR(GL_RED)
289 FB_FORMAT_STR(GL_RG)
290 FB_FORMAT_STR(GL_RGB)
291 FB_FORMAT_STR(GL_RGBA)
292
293 /* Render buffer formats */
294 FB_FORMAT_STR(GL_DEPTH_COMPONENT16)
295 FB_FORMAT_STR(GL_DEPTH_COMPONENT24)
296 FB_FORMAT_STR(GL_DEPTH_COMPONENT32F)
297 FB_FORMAT_STR(GL_DEPTH24_STENCIL8)
298 FB_FORMAT_STR(GL_DEPTH32F_STENCIL8)
299 FB_FORMAT_STR(GL_STENCIL_INDEX8)
300
301 /* Table 2 */
302 FB_FORMAT_STR(GL_R8)
303 FB_FORMAT_STR(GL_R8_SNORM)
304 FB_FORMAT_STR(GL_R16)
305 FB_FORMAT_STR(GL_R16_SNORM)
306 FB_FORMAT_STR(GL_RG8)
307 FB_FORMAT_STR(GL_RG8_SNORM)
308 FB_FORMAT_STR(GL_RG16)
309 FB_FORMAT_STR(GL_RG16_SNORM)
310 FB_FORMAT_STR(GL_R3_G3_B2)
311 FB_FORMAT_STR(GL_RGB4)
312 FB_FORMAT_STR(GL_RGB5)
313 FB_FORMAT_STR(GL_RGB8)
314 FB_FORMAT_STR(GL_RGB8_SNORM)
315 FB_FORMAT_STR(GL_RGB10)
316 FB_FORMAT_STR(GL_RGB12)
317 FB_FORMAT_STR(GL_RGB16_SNORM)
318 FB_FORMAT_STR(GL_RGBA2)
319 FB_FORMAT_STR(GL_RGBA4)
320 FB_FORMAT_STR(GL_RGB5_A1)
321 FB_FORMAT_STR(GL_RGBA8)
322 FB_FORMAT_STR(GL_RGBA8_SNORM)
323 FB_FORMAT_STR(GL_RGB10_A2)
324 FB_FORMAT_STR(GL_RGB10_A2UI)
325 FB_FORMAT_STR(GL_RGBA12)
326 FB_FORMAT_STR(GL_RGBA16)
327 FB_FORMAT_STR(GL_SRGB8)
328 FB_FORMAT_STR(GL_SRGB8_ALPHA8)
329 FB_FORMAT_STR(GL_R16F)
330 FB_FORMAT_STR(GL_RG16F)
331 FB_FORMAT_STR(GL_RGB16F)
332 FB_FORMAT_STR(GL_RGBA16F)
333 FB_FORMAT_STR(GL_R32F)
334 FB_FORMAT_STR(GL_RG32F)
335 FB_FORMAT_STR(GL_RGB32F)
336 FB_FORMAT_STR(GL_RGBA32F)
337 FB_FORMAT_STR(GL_R11F_G11F_B10F)
338 FB_FORMAT_STR(GL_RGB9_E5)
339 FB_FORMAT_STR(GL_R8I)
340 FB_FORMAT_STR(GL_R8UI)
341 FB_FORMAT_STR(GL_R16I)
342 FB_FORMAT_STR(GL_R16UI)
343 FB_FORMAT_STR(GL_R32I)
344 FB_FORMAT_STR(GL_R32UI)
345 FB_FORMAT_STR(GL_RG8I)
346 FB_FORMAT_STR(GL_RG8UI)
347 FB_FORMAT_STR(GL_RG16I)
348 FB_FORMAT_STR(GL_RG16UI)
349 FB_FORMAT_STR(GL_RG32I)
350 FB_FORMAT_STR(GL_RG32UI)
351 FB_FORMAT_STR(GL_RGB8I)
352 FB_FORMAT_STR(GL_RGB8UI)
353 FB_FORMAT_STR(GL_RGB16I)
354 FB_FORMAT_STR(GL_RGB16UI)
355 FB_FORMAT_STR(GL_RGB32I)
356 FB_FORMAT_STR(GL_RGB32UI)
357 FB_FORMAT_STR(GL_RGBA8I)
358 FB_FORMAT_STR(GL_RGBA8UI)
359 FB_FORMAT_STR(GL_RGBA16I)
360 FB_FORMAT_STR(GL_RGBA16UI)
361 FB_FORMAT_STR(GL_RGBA32I)
362 FB_FORMAT_STR(GL_RGBA32UI)
363 };
364
365 for( int i=0; i<vg_list_size(formats); i++ )
366 if( formats[i].e == format )
367 return formats[i].str;
368
369 return "UNDEFINED";
370 }
371
372 /*
373 * Bind and allocate texture for framebuffer attachment
374 */
375 VG_STATIC void render_fb_allocate_texture( struct framebuffer *fb,
376 struct framebuffer_attachment *a )
377 {
378 int rx, ry;
379 render_fb_get_current_res( fb, &rx, &ry );
380
381 if( a->purpose == k_framebuffer_attachment_type_renderbuffer )
382 {
383 glBindRenderbuffer( GL_RENDERBUFFER, a->id );
384 glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, rx, ry );
385 }
386 else if( a->purpose == k_framebuffer_attachment_type_colour )
387 {
388 glBindTexture( GL_TEXTURE_2D, a->id );
389 glTexImage2D( GL_TEXTURE_2D, 0, a->internalformat, rx, ry,
390 0, a->format, a->type, NULL );
391 }
392 }
393
394 /*
395 * Full allocation of a framebuffer
396 */
397 VG_STATIC void render_fb_allocate( struct framebuffer *fb )
398 {
399 glGenFramebuffers( 1, &fb->fb );
400 glBindFramebuffer( GL_FRAMEBUFFER, fb->fb );
401
402 int rx, ry;
403 render_fb_get_current_res( fb, &rx, &ry );
404
405 vg_info( "allocate_framebuffer( %s, %dx%d )\n", fb->display_name, rx, ry );
406 vg_info( "{\n" );
407
408 GLenum colour_attachments[4];
409 u32 colour_count = 0;
410
411 for( int j=0; j<vg_list_size(fb->attachments); j++ )
412 {
413 struct framebuffer_attachment *attachment = &fb->attachments[j];
414
415 if( attachment->purpose == k_framebuffer_attachment_type_none )
416 continue;
417
418 vg_info( " %s: %s\n",
419 render_fb_attachment_str( attachment->attachment ),
420 render_fb_format_str( attachment->internalformat ) );
421
422 if( attachment->purpose == k_framebuffer_attachment_type_renderbuffer )
423 {
424 glGenRenderbuffers( 1, &attachment->id );
425 render_fb_allocate_texture( fb, attachment );
426 glFramebufferRenderbuffer( GL_FRAMEBUFFER,
427 GL_DEPTH_STENCIL_ATTACHMENT,
428 GL_RENDERBUFFER, attachment->id );
429 }
430 else if( attachment->purpose == k_framebuffer_attachment_type_colour )
431 {
432 glGenTextures( 1, &attachment->id );
433 render_fb_allocate_texture( fb, attachment );
434 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
435 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
436 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
437 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
438
439 glFramebufferTexture2D( GL_FRAMEBUFFER, attachment->attachment,
440 GL_TEXTURE_2D, attachment->id, 0 );
441
442 colour_attachments[ colour_count ++ ] = attachment->attachment;
443 }
444 }
445
446 glDrawBuffers( colour_count, colour_attachments );
447
448 /*
449 * Check result
450 */
451 GLenum result = glCheckFramebufferStatus( GL_FRAMEBUFFER );
452
453 if( result == GL_FRAMEBUFFER_COMPLETE )
454 {
455 /*
456 * Attatch to gpipeline
457 */
458 if( fb->link )
459 *fb->link = fb;
460
461 vg_success( " status: complete\n" );
462 vg_info( "}\n" );
463 }
464 else
465 {
466 if( result == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT )
467 vg_error( " status: Incomplete attachment" );
468 else if( result == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT )
469 vg_error( " status: Missing attachment" );
470 else if( result == GL_FRAMEBUFFER_UNSUPPORTED )
471 vg_error( " status: Unsupported framebuffer format" );
472 else
473 vg_error( " status: Generic Error" );
474
475 vg_info( "}\n" );
476 vg_fatal_exit_loop( "Incomplete framebuffer (see logs)" );
477 }
478 }
479
480 /*
481 * Resize/Update all framebuffers(we know about)
482 */
483 VG_STATIC void render_fb_resize(void)
484 {
485 if( !gpipeline.ready )
486 return;
487
488 for( int i=0; i<vg_list_size(framebuffers); i++ )
489 {
490 struct framebuffer *fb = &framebuffers[i];
491 for( int j=0; j<vg_list_size(fb->attachments); j++ )
492 {
493 struct framebuffer_attachment *attachment = &fb->attachments[j];
494 render_fb_allocate_texture( fb, attachment );
495 }
496 }
497 }
498
499 VG_STATIC int render_framebuffer_control( int argc, char const *argv[] );
500 VG_STATIC void render_framebuffer_poll( int argc, char const *argv[] );
501 VG_STATIC void render_init_fs_quad(void)
502 {
503 vg_info( "[render] Allocate quad\n" );
504
505 float quad[] =
506 {
507 0.00f,0.00f, 1.00f,1.00f, 0.00f,1.00f,
508 0.00f,0.00f, 1.00f,0.00f, 1.00f,1.00f,
509
510 0.20f,0.00f, 0.80f,1.00f, 0.20f,1.00f,
511 0.20f,0.00f, 0.80f,0.00f, 0.80f,1.00f,
512
513 /* 9x9 debug grid */
514 /* row0 */
515 0.00f,0.00f, 0.30f,0.30f, 0.00f,0.30f,
516 0.00f,0.00f, 0.30f,0.00f, 0.30f,0.30f,
517 0.30f,0.00f, 0.60f,0.30f, 0.30f,0.30f,
518 0.30f,0.00f, 0.60f,0.00f, 0.60f,0.30f,
519 0.60f,0.00f, 0.90f,0.30f, 0.60f,0.30f,
520 0.60f,0.00f, 0.90f,0.00f, 0.90f,0.30f,
521 /* row1 */
522 0.00f,0.30f, 0.30f,0.60f, 0.00f,0.60f,
523 0.00f,0.30f, 0.30f,0.30f, 0.30f,0.60f,
524 0.30f,0.30f, 0.60f,0.60f, 0.30f,0.60f,
525 0.30f,0.30f, 0.60f,0.30f, 0.60f,0.60f,
526 0.60f,0.30f, 0.90f,0.60f, 0.60f,0.60f,
527 0.60f,0.30f, 0.90f,0.30f, 0.90f,0.60f,
528 /* row2 */
529 0.00f,0.60f, 0.30f,0.90f, 0.00f,0.90f,
530 0.00f,0.60f, 0.30f,0.60f, 0.30f,0.90f,
531 0.30f,0.60f, 0.60f,0.90f, 0.30f,0.90f,
532 0.30f,0.60f, 0.60f,0.60f, 0.60f,0.90f,
533 0.60f,0.60f, 0.90f,0.90f, 0.60f,0.90f,
534 0.60f,0.60f, 0.90f,0.60f, 0.90f,0.90f,
535 };
536
537 vg_function_push( (struct vg_cmd)
538 {
539 .name = "fb",
540 .function = render_framebuffer_control,
541 .poll_suggest = render_framebuffer_poll
542 });
543
544 glGenVertexArrays( 1, &gpipeline.fsquad.vao );
545 glGenBuffers( 1, &gpipeline.fsquad.vbo );
546 glBindVertexArray( gpipeline.fsquad.vao );
547 glBindBuffer( GL_ARRAY_BUFFER, gpipeline.fsquad.vbo );
548 glBufferData( GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW );
549 glBindVertexArray( gpipeline.fsquad.vao );
550 glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE,
551 sizeof(float)*2, (void*)0 );
552 glEnableVertexAttribArray( 0 );
553
554 VG_CHECK_GL_ERR();
555 }
556
557 VG_STATIC void render_init(void)
558 {
559 shader_blit_register();
560 shader_blitblur_register();
561 shader_blitcolour_register();
562
563 vg_acquire_thread_sync();
564 {
565 /*
566 * Complete Framebuffers
567 */
568 for( int i=0; i<vg_list_size(framebuffers); i++ )
569 {
570 struct framebuffer *fb = &framebuffers[i];
571 render_fb_allocate( fb );
572 }
573
574 render_init_fs_quad();
575
576 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
577 gpipeline.ready = 1;
578 }
579
580 vg_release_thread_sync();
581 }
582
583 /*
584 * Utility
585 */
586 VG_STATIC void render_fsquad(void)
587 {
588 glBindVertexArray( gpipeline.fsquad.vao );
589 glDrawArrays( GL_TRIANGLES, 0, 6 );
590 }
591
592 VG_STATIC void render_fsquad1(void)
593 {
594 glBindVertexArray( gpipeline.fsquad.vao );
595 glDrawArrays( GL_TRIANGLES, 6, 6 );
596 }
597
598 /*
599 * Call this inside the UI function
600 */
601 VG_STATIC void render_view_framebuffer_ui(void)
602 {
603 int viewing_count = 0;
604
605 glBindVertexArray( gpipeline.fsquad.vao );
606 shader_blit_use();
607 shader_blit_uTexMain( 0 );
608
609 v2f identity = { 1.0f, 1.0f };
610 shader_blit_uInverseRatio( identity );
611
612 for( int i=0; i<vg_list_size(framebuffers); i++ ){
613 struct framebuffer *fb = &framebuffers[i];
614
615 for( int j=0; j<vg_list_size(fb->attachments); j++ ){
616 struct framebuffer_attachment *at = &fb->attachments[j];
617
618 if( !at->debug_view )
619 continue;
620
621 v2f corner,
622 window = { vg.window_x, vg.window_y };
623
624 corner[0] = viewing_count % 3;
625 corner[1] = 1 + (viewing_count / 3);
626 v2_mul( corner, window, corner );
627 v2_muls( corner, 0.3f, corner );
628 corner[1] = vg.window_y - corner[1];
629
630 ui_text( (ui_rect){ corner[0], corner[1], 0.0f, 0.0f },
631 fb->display_name, 2, k_text_align_left );
632 ui_text( (ui_rect){ corner[0], corner[1] + 32, 0.0f, 0.0f, },
633 at->display_name, 1, k_text_align_left );
634
635 if( at->purpose == k_framebuffer_attachment_type_renderbuffer ){
636 v2f center;
637 v2_muladds( corner, window, 0.15f, center );
638
639 ui_text( (ui_rect){ center[0], center[1], 0.0f, 0.0f },
640 "<hardware texture>", 1, k_text_align_center );
641 }
642 else{
643 render_fb_bind_texture( fb, j, 0 );
644
645 int start = (viewing_count+2) * 6,
646 count = 6;
647 glDrawArrays( GL_TRIANGLES, start, count );
648 }
649
650 viewing_count ++;
651 }
652 }
653 }
654
655 VG_STATIC void render_framebuffer_show( struct framebuffer *fb,
656 struct framebuffer_attachment *at,
657 int operation )
658 {
659 at->debug_view = operation;
660 vg_info( "%s %s:%s\n", (operation?"shown": "hidden"),
661 fb->display_name, at->display_name );
662 }
663
664 /*
665 * arg0: command "show"/"hide"
666 * arg1: framebuffer name <name>/"all"
667 * arg2: subname <name>/none
668 */
669 VG_STATIC int render_framebuffer_control( int argc, char const *argv[] )
670 {
671 if( argc < 2 )
672 {
673 vg_error( "Usage: fb \"show/hide\" <name>/\"all\" <name>/none\n" );
674 return 0;
675 }
676
677 int modify_all = 0,
678 operation = 0;
679
680 if( !strcmp( argv[0], "show" ) )
681 operation = 1;
682 else if( !strcmp( argv[0], "hide" ) )
683 operation = 0;
684 else
685 {
686 vg_error( "Unknown framebuffer operation: '%s'\n", argv[0] );
687 return 0;
688 }
689
690 if( !strcmp( argv[1], "all" ) )
691 modify_all = 1;
692
693 for( int i=0; i<vg_list_size(framebuffers); i++ )
694 {
695 struct framebuffer *fb = &framebuffers[i];
696
697 for( int j=0; j<vg_list_size(fb->attachments); j++ )
698 {
699 struct framebuffer_attachment *at = &fb->attachments[j];
700
701 if( at->purpose == k_framebuffer_attachment_type_none )
702 continue;
703
704 if( modify_all )
705 {
706 render_framebuffer_show( fb, at, operation );
707 }
708 else
709 {
710 if( !strcmp( fb->display_name, argv[1] ) )
711 {
712 if( argc == 2 )
713 render_framebuffer_show( fb, at, operation );
714 else if( !strcmp( at->display_name, argv[2] ) )
715 render_framebuffer_show( fb, at, operation );
716 }
717 }
718 }
719 }
720
721 return 0;
722 }
723
724 VG_STATIC void render_framebuffer_poll( int argc, char const *argv[] )
725 {
726 const char *term = argv[argc-1];
727
728 if( argc == 1 )
729 {
730 console_suggest_score_text( "show", term, 0 );
731 console_suggest_score_text( "hide", term, 0 );
732 }
733 else if( argc == 2 )
734 {
735 console_suggest_score_text( "all", term, 0 );
736
737 for( int i=0; i<vg_list_size(framebuffers); i++ )
738 {
739 struct framebuffer *fb = &framebuffers[i];
740 console_suggest_score_text( fb->display_name, term, 0 );
741 }
742 }
743 else if( argc == 3 )
744 {
745 int modify_all = 0;
746
747 if( !strcmp( argv[1], "all" ) )
748 modify_all = 1;
749
750 for( int i=0; i<vg_list_size(framebuffers); i++ )
751 {
752 struct framebuffer *fb = &framebuffers[i];
753
754 for( int j=0; j<vg_list_size(fb->attachments); j++ )
755 {
756 struct framebuffer_attachment *at = &fb->attachments[j];
757
758 if( at->purpose == k_framebuffer_attachment_type_none )
759 continue;
760
761 if( modify_all )
762 {
763 console_suggest_score_text( at->display_name, term, 0 );
764 }
765 else if( !strcmp( fb->display_name, argv[1] ) )
766 {
767 console_suggest_score_text( at->display_name, term, 0 );
768 }
769 }
770 }
771 }
772 }
773
774 #endif /* RENDER_H */