1 // Copyright (c) 2015 Sergio Gonzalez. All rights reserved.
2 // License: https://github.com/serge-rgb/milton#license
3
4 #include "shaders.gen.h"
5
6 #include "color.h"
7 #include "gl_helpers.h"
8 #include "gui.h"
9 #include "milton.h"
10 #include "vector.h"
11
12 #define MAX_DEPTH_VALUE (1<<20) // Strokes have MAX_DEPTH_VALUE different z values. 1/i for each i in [1, MAX_DEPTH_VALUE)
13 // Also defined in stroke_raster.v.glsl
14 //
15 // NOTE: Using this technique means that the algorithm is not correct.
16 // There is a low probability that one stroke will cover another
17 // stroke with the same z value.
18
19
20 #define RENDER_CHUNK_SIZE_LOG2 28
21
22
23 enum ImmediateFlag
24 {
25 ImmediateFlag_RECT = (1<<0),
26 };
27
28 // Draw data for single stroke
29 enum RenderElementFlags
30 {
31 RenderElementFlags_NONE = 0,
32 RenderElementFlags_LAYER = 1<<0,
33 RenderElementFlags_PRESSURE_TO_OPACITY = 1<<1,
34 RenderElementFlags_DISTANCE_TO_OPACITY = 1<<2,
35 RenderElementFlags_ERASER = 1<<3,
36 };
37
38 struct RenderElement
39 {
40 GLuint vbo_stroke;
41 GLuint vbo_pointa;
42 GLuint vbo_pointb;
43 GLuint indices;
44 #if STROKE_DEBUG_VIZ
45 GLuint vbo_debug;
46 #endif
47
48 i64 count;
49
50 union {
51 struct { // For when element is a stroke.
52 v4f color;
53 i32 radius;
54 f32 min_opacity;
55 f32 hardness;
56 };
57 struct { // For when element is layer.
58 f32 layer_alpha;
59 LayerEffect* effects;
60 };
61 };
62
63 int flags; // RenderElementFlags enum;
64 };
65
66 struct RenderBackend
67 {
68 f32 viewport_limits[2]; // OpenGL limits to the framebuffer size.
69
70 v2i render_center;
71
72 // OpenGL programs.
73
74 GLuint stroke_program;
75 GLuint stroke_eraser_program;
76
77 GLuint stroke_clear_program;
78 GLuint stroke_info_program;
79
80 // TODO: If we add more variations we probably will want to automate this...
81 GLuint stroke_fill_program_pressure;
82 GLuint stroke_fill_program_distance;
83 GLuint stroke_fill_program_pressure_distance;
84
85 GLuint quad_program;
86 GLuint picker_program;
87 GLuint layer_blend_program;
88 GLuint outline_program;
89 GLuint exporter_program;
90 GLuint texture_fill_program;
91 GLuint postproc_program;
92 GLuint blur_program;
93 #if MILTON_DEBUG
94 GLuint simple_program;
95 #endif
96
97 // VBO for the screen-covering quad.
98 GLuint vbo_screen_quad;
99
100 // Handles for color picker.
101 GLuint vbo_picker;
102 GLuint vbo_picker_norm;
103
104 // Handles for brush outline.
105 GLuint vbo_outline;
106 GLuint vbo_outline_sizes;
107
108 // Handles for exporter rectangle.
109 GLuint vbo_exporter;
110 GLuint exporter_indices;
111 int exporter_indices_count;
112
113 u32 imm_flags; // ImmediateFlag
114
115 // Objects used in rendering.
116 GLuint canvas_texture;
117 GLuint eraser_texture;
118 GLuint helper_texture; // Used for various effects..
119 GLuint stencil_texture;
120 GLuint stroke_info_texture;
121
122 GLuint fbo;
123
124 i32 flags; // RenderBackendFlags enum
125
126 DArray<RenderElement> clip_array;
127
128 // Screen size.
129 i32 width;
130 i32 height;
131
132 v3f background_color;
133 i32 scale; // zoom
134
135 // See MAX_DEPTH_VALUE
136 i32 stroke_z;
137
138 // TODO: Re-enable these?
139 // Cached values for stroke rendering uniforms.
140 // v4f current_color;
141 // float current_radius;
142
143 #if MILTON_ENABLE_PROFILING
144 u64 clipped_count;
145 #endif
146 };
147
148 enum GLVendor
149 {
150 GLVendor_NVIDIA,
151 GLVendor_INTEL,
152 GLVendor_AMD,
153 GLVendor_UNKNOWN,
154 };
155
156 char DEBUG_g_buffers[100000];
157
158 static void
DEBUG_gl_mark_buffer(GLuint buffer)159 DEBUG_gl_mark_buffer(GLuint buffer)
160 {
161 mlt_assert(buffer < 100000);
162 DEBUG_g_buffers[buffer] = 1;
163 }
164
165 static void
DEBUG_gl_unmark_buffer(GLuint buffer)166 DEBUG_gl_unmark_buffer(GLuint buffer)
167 {
168 mlt_assert(buffer < 100000);
169 DEBUG_g_buffers[buffer] = 0;
170 }
171
172 static void
DEBUG_gl_validate_buffer(GLuint buffer)173 DEBUG_gl_validate_buffer(GLuint buffer)
174 {
175 mlt_assert(buffer < 100000);
176 mlt_assert(DEBUG_g_buffers[buffer]);
177 }
178
179 static void
print_framebuffer_status()180 print_framebuffer_status()
181 {
182 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
183 char* msg = NULL;
184 switch ( status ) {
185 case GL_FRAMEBUFFER_COMPLETE: {
186 // OK!
187 break;
188 }
189 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: {
190 msg = "Incomplete Attachment";
191 break;
192 }
193 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: {
194 msg = "Missing Attachment";
195 break;
196 }
197 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: {
198 msg = "Incomplete Draw Buffer";
199 break;
200 }
201 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: {
202 msg = "Incomplete Read Buffer";
203 break;
204 }
205 case GL_FRAMEBUFFER_UNSUPPORTED: {
206 msg = "Unsupported Framebuffer";
207 break;
208 }
209 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: {
210 msg = "Incomplete Multisample";
211 break;
212 }
213 default: {
214 msg = "Unknown";
215 break;
216 }
217 }
218
219 if ( status != GL_FRAMEBUFFER_COMPLETE ) {
220 char warning[1024];
221 snprintf(warning, 1024, "Framebuffer Error: %s", msg);
222 milton_log("Warning %s\n", warning);
223 }
224 }
225
226 static RenderElement*
get_render_element(RenderHandle handle)227 get_render_element(RenderHandle handle)
228 {
229 RenderElement* e = reinterpret_cast<RenderElement*>(handle);
230 return e;
231 }
232
233 RenderBackend*
gpu_allocate_render_backend(Arena * arena)234 gpu_allocate_render_backend(Arena* arena)
235 {
236 RenderBackend* p = arena_alloc_elem(arena, RenderBackend);
237 return p;
238 }
239
240 // Send Color Picker data to OpenGL.
241 void
gpu_update_picker(RenderBackend * r,ColorPicker * picker)242 gpu_update_picker(RenderBackend* r, ColorPicker* picker)
243 {
244 gl::use_program(r->picker_program);
245 // Transform to [-1,1]
246 v2f a = picker->data.a;
247 v2f b = picker->data.b;
248 v2f c = picker->data.c;
249 Rect bounds = picker_get_bounds(picker);
250 int w = bounds.right-bounds.left;
251 int h = bounds.bottom-bounds.top;
252 // The center of the picker has an offset of (25,35)
253 // and the bounds radius is 100 px
254 auto transform = [&](v2f p) { return v2f{2*p.x/w-1 - .25f, 2*p.y/h-1 -0.35f}; };
255 a = transform(a);
256 b = transform(b);
257 c = transform(c);
258 gl::set_uniform_vec2(r->picker_program, "u_pointa", 1, a.d);
259 gl::set_uniform_vec2(r->picker_program, "u_pointb", 1, b.d);
260 gl::set_uniform_vec2(r->picker_program, "u_pointc", 1, c.d);
261 gl::set_uniform_f(r->picker_program, "u_angle", picker->data.hsv.h);
262
263 v3f hsv = picker->data.hsv;
264 gl::set_uniform_vec3(r->picker_program, "u_color", 1, hsv_to_rgb(hsv).d);
265
266 // Point within triangle
267 {
268 v2f point = lerp(picker->data.b, lerp(picker->data.a, picker->data.c, hsv.s), hsv.v);
269 // Move to [-1,1]^2
270 point = transform(point);
271 gl::set_uniform_vec2(r->picker_program, "u_triangle_point", 1, point.d);
272 }
273 v4f colors[5] = {};
274 ColorButton* button = picker->color_buttons;
275 colors[0] = button->rgba; button = button->next;
276 colors[1] = button->rgba; button = button->next;
277 colors[2] = button->rgba; button = button->next;
278 colors[3] = button->rgba; button = button->next;
279 colors[4] = button->rgba; button = button->next;
280 gl::set_uniform_vec4(r->picker_program, "u_colors", 5, (float*)colors);
281
282 // Update VBO for picker
283 {
284 Rect rect = get_bounds_for_picker_and_colors(picker);
285 // convert to clip space
286 v2i screen_size = {r->width, r->height};
287 float top = (float)rect.top / screen_size.h;
288 float bottom = (float)rect.bottom / screen_size.h;
289 float left = (float)rect.left / screen_size.w;
290 float right = (float)rect.right / screen_size.w;
291 top = (top*2.0f - 1.0f) * -1;
292 bottom = (bottom*2.0f - 1.0f) *-1;
293 left = left*2.0f - 1.0f;
294 right = right*2.0f - 1.0f;
295 // a------d
296 // | \ |
297 // | \ |
298 // b______c
299 GLfloat data[] =
300 {
301 left, top,
302 left, bottom,
303 right, bottom,
304 right, top,
305 };
306 float ratio = (float)(rect.bottom-rect.top) / (float)(rect.right-rect.left);
307 ratio = (ratio*2)-1;
308 // normalized positions.
309 GLfloat norm[] =
310 {
311 -1, -1,
312 -1, ratio,
313 1, ratio,
314 1, -1,
315 };
316
317 // Create buffers and upload
318 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_picker);
319 DEBUG_gl_mark_buffer(r->vbo_picker);
320 glBufferData(GL_ARRAY_BUFFER, array_count(data)*sizeof(*data), data, GL_STATIC_DRAW);
321
322 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_picker_norm);
323 DEBUG_gl_mark_buffer(r->vbo_picker_norm);
324 glBufferData(GL_ARRAY_BUFFER, array_count(norm)*sizeof(*norm), norm, GL_STATIC_DRAW);
325 }
326 }
327
328 void
gpu_update_brush_outline(RenderBackend * r,i32 cx,i32 cy,i32 radius,BrushOutlineEnum outline_enum,v4f color)329 gpu_update_brush_outline(RenderBackend* r, i32 cx, i32 cy, i32 radius,
330 BrushOutlineEnum outline_enum, v4f color)
331 {
332 if ( r->vbo_outline == 0 ) {
333 mlt_assert(r->vbo_outline_sizes == 0);
334 glGenBuffers(1, &r->vbo_outline);
335 glGenBuffers(1, &r->vbo_outline_sizes);
336 }
337 mlt_assert(r->vbo_outline_sizes != 0);
338
339 float radius_plus_girth = radius + 4.0f; // Girth defined in outline.f.glsl
340
341 auto w = r->width;
342 auto h = r->height;
343
344 // Normalized to [-1,1]
345 GLfloat data[] = {
346 2*((cx-radius_plus_girth) / w)-1, -2*((cy-radius_plus_girth) / h)+1,
347 2*((cx-radius_plus_girth) / w)-1, -2*((cy+radius_plus_girth) / h)+1,
348 2*((cx+radius_plus_girth) / w)-1, -2*((cy+radius_plus_girth) / h)+1,
349 2*((cx+radius_plus_girth) / w)-1, -2*((cy-radius_plus_girth) / h)+1,
350 };
351
352 GLfloat sizes[] = {
353 -radius_plus_girth, -radius_plus_girth,
354 -radius_plus_girth, radius_plus_girth,
355 radius_plus_girth, radius_plus_girth,
356 radius_plus_girth, -radius_plus_girth,
357 };
358
359 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_outline_sizes);
360 DEBUG_gl_mark_buffer(r->vbo_outline_sizes);
361 glBufferData(GL_ARRAY_BUFFER, array_count(sizes)*sizeof(*sizes), sizes, GL_DYNAMIC_DRAW);
362
363 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_outline);
364 DEBUG_gl_mark_buffer(r->vbo_outline);
365 glBufferData(GL_ARRAY_BUFFER, array_count(data)*sizeof(*data), data, GL_DYNAMIC_DRAW);
366
367 gl::set_uniform_i(r->outline_program, "u_radius", radius);
368 if ( outline_enum == BrushOutline_FILL ) {
369 gl::set_uniform_i(r->outline_program, "u_fill", true);
370 gl::set_uniform_vec4(r->outline_program, "u_color", 1, color.d);
371 }
372 else if ( outline_enum == BrushOutline_NO_FILL ) {
373 gl::set_uniform_i(r->outline_program, "u_fill", false);
374 }
375 }
376
GL_DEBUG_CALLBACK(milton_gl_debug_callback)377 GL_DEBUG_CALLBACK(milton_gl_debug_callback)
378 {
379 switch ( type ) {
380 case GL_DEBUG_TYPE_ERROR:
381 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
382 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
383 case GL_DEBUG_SOURCE_APPLICATION: {
384 milton_log("[OpenGl Debug message (severity: %d)]: %s\n", severity, message);
385 } break;
386 }
387 }
388
389
390 b32
gpu_init(RenderBackend * r,CanvasView * view,ColorPicker * picker)391 gpu_init(RenderBackend* r, CanvasView* view, ColorPicker* picker)
392 {
393 #if MILTON_DEBUG
394 glEnable(GL_DEBUG_OUTPUT);
395 if (glDebugMessageCallback) { glDebugMessageCallback(milton_gl_debug_callback, NULL); }
396 #endif
397
398 r->stroke_z = MAX_DEPTH_VALUE - 20;
399
400 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
401 glEnable(GL_MULTISAMPLE);
402 // TODO: remove sample shading
403 if ( gl::check_flags(GLHelperFlags_SAMPLE_SHADING) ) {
404 glEnable(GL_SAMPLE_SHADING_ARB);
405 // glMinSampleShadingARB(1.0f);
406 }
407 }
408
409 {
410 GLfloat viewport_dims[2] = {};
411 glGetFloatv(GL_MAX_VIEWPORT_DIMS, viewport_dims);
412 milton_log("Maximum viewport dimensions, %fx%f\n", viewport_dims[0], viewport_dims[1]);
413 r->viewport_limits[0] = viewport_dims[0];
414 r->viewport_limits[1] = viewport_dims[1];
415 }
416
417 // r->current_color = {-1,-1,-1,-1};
418 // r->current_radius = -1;
419
420 glEnable(GL_SCISSOR_TEST);
421 glActiveTexture(GL_TEXTURE0);
422 bool result = true;
423
424 // Create a single VAO and bind it.
425 #if USE_GL_3_2
426 GLuint proxy_vao = 0;
427 glGenVertexArrays(1, &proxy_vao);
428 glBindVertexArray(proxy_vao);
429 #endif
430
431 GLVendor vendor = GLVendor_UNKNOWN;
432 {
433 char *vendor_string = (char *)glGetString(GL_VENDOR);
434 if ( vendor_string ) {
435 if ( strcmp("NVIDIA Corporation", vendor_string) == 0 ) {
436 vendor = GLVendor_NVIDIA;
437 }
438 else if ( strcmp("AMD", vendor_string) == 0 ) {
439 vendor = GLVendor_AMD;
440 }
441 else if ( strcmp("Intel", vendor_string) == 0 ) {
442 vendor = GLVendor_INTEL;
443 }
444 }
445 milton_log("Vendor string: \"%s\"\n", vendor_string);
446 }
447 // Quad that fills the screen.
448 {
449 // a------d
450 // | \ |
451 // | \ |
452 // b______c
453 // Triangle fan:
454 GLfloat quad_data[] = {
455 -1 , -1 , // a
456 -1 , 1 , // b
457 1 , 1 , // c
458 1 , -1 , // d
459 };
460
461 // Create buffers and upload
462 GLuint vbo = 0;
463 glGenBuffers(1, &vbo);
464 glBindBuffer(GL_ARRAY_BUFFER, vbo);
465 DEBUG_gl_mark_buffer(vbo);
466 glBufferData(GL_ARRAY_BUFFER, array_count(quad_data)*sizeof(*quad_data), quad_data, GL_STATIC_DRAW);
467
468 float u = 1.0f;
469 GLfloat uv_data[] = {
470 0,0,
471 0,u,
472 u,u,
473 u,0,
474 };
475 GLuint vbo_uv = 0;
476 glGenBuffers(1, &vbo_uv);
477 glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
478 DEBUG_gl_mark_buffer(vbo_uv);
479 glBufferData(GL_ARRAY_BUFFER, array_count(uv_data)*sizeof(*uv_data), uv_data, GL_STATIC_DRAW);
480
481 r->vbo_screen_quad = vbo;
482
483 GLuint objs[2] = {};
484 objs[0] = gl::compile_shader(g_quad_v, GL_VERTEX_SHADER);
485 objs[1] = gl::compile_shader(g_quad_f, GL_FRAGMENT_SHADER);
486 r->quad_program = glCreateProgram();
487 gl::link_program(r->quad_program, objs, array_count(objs));
488 }
489
490 GLuint stroke_vs = gl::compile_shader(g_stroke_raster_v, GL_VERTEX_SHADER);
491
492 { // Stroke raster program
493 GLuint objs[2];
494
495 char* config_string = "";
496 if ( gl::check_flags(GLHelperFlags_SAMPLE_SHADING) ) {
497 if ( vendor == GLVendor_NVIDIA ) {
498 config_string =
499 "#define HAS_SAMPLE_SHADING 1 \n"
500 "#define VENDOR_NVIDIA 1 \n";
501 }
502 else if ( vendor == GLVendor_INTEL ) {
503 config_string =
504 "#define HAS_SAMPLE_SHADING 1 \n"
505 "#define VENDOR_INTEL 1 \n";
506 }
507 else {
508 config_string =
509 "#define HAS_SAMPLE_SHADING 1 \n";
510 }
511 }
512
513 objs[0] = stroke_vs;
514 objs[1] = gl::compile_shader(g_stroke_raster_f, GL_FRAGMENT_SHADER, config_string);
515
516 r->stroke_program = glCreateProgram();
517
518 gl::link_program(r->stroke_program, objs, array_count(objs));
519 }
520 // Stroke eraser
521 {
522 GLuint objs[2];
523 objs[0] = stroke_vs;
524 objs[1] = gl::compile_shader(g_stroke_eraser_f, GL_FRAGMENT_SHADER);
525
526 r->stroke_eraser_program = glCreateProgram();
527 gl::link_program(r->stroke_eraser_program, objs, array_count(objs));
528
529 gl::set_uniform_i(r->stroke_eraser_program, "u_canvas", 0);
530 }
531 // Stroke info program
532 {
533 GLuint objs[2];
534 objs[0] = stroke_vs;
535 objs[1] = gl::compile_shader(g_stroke_info_f, GL_FRAGMENT_SHADER);
536
537 r->stroke_info_program = glCreateProgram();
538 gl::link_program(r->stroke_info_program, objs, array_count(objs));
539
540 }
541 // Stroke fill program
542 {
543 GLuint objs[2];
544 objs[0] = stroke_vs;
545
546 r->stroke_fill_program_distance = glCreateProgram();
547 r->stroke_fill_program_pressure = glCreateProgram();
548 r->stroke_fill_program_pressure_distance = glCreateProgram();
549
550 objs[1] = gl::compile_shader(g_stroke_fill_f, GL_FRAGMENT_SHADER);
551 gl::link_program(r->stroke_fill_program_pressure, objs, array_count(objs));
552
553 objs[1] = gl::compile_shader(g_stroke_fill_f, GL_FRAGMENT_SHADER, "", "#define DISTANCE_TO_OPACITY 1\n#define PRESSURE_TO_OPACITY 0\n");
554 gl::link_program(r->stroke_fill_program_distance, objs, array_count(objs));
555
556 objs[1] = gl::compile_shader(g_stroke_fill_f, GL_FRAGMENT_SHADER, "", "#define DISTANCE_TO_OPACITY 1\n");
557 gl::link_program(r->stroke_fill_program_pressure_distance, objs, array_count(objs));
558
559 GLuint ps[] = {
560 r->stroke_fill_program_pressure,
561 r->stroke_fill_program_pressure_distance,
562 r->stroke_fill_program_distance,
563 };
564 for (auto p : ps) {
565 gl::set_uniform_i(p, "u_info", 0);
566 }
567 }
568 // Stroke clear program
569 {
570 GLuint objs[2];
571 objs[0] = stroke_vs;
572 objs[1] = gl::compile_shader(g_stroke_clear_f, GL_FRAGMENT_SHADER);
573
574 r->stroke_clear_program = glCreateProgram();
575 gl::link_program(r->stroke_clear_program, objs, array_count(objs));
576 }
577 { // Color picker program
578 r->picker_program = glCreateProgram();
579 GLuint objs[2] = {};
580
581 // g_picker_* generated by shadergen.cc
582 objs[0] = gl::compile_shader(g_picker_v, GL_VERTEX_SHADER);
583 objs[1] = gl::compile_shader(g_picker_f, GL_FRAGMENT_SHADER);
584 gl::link_program(r->picker_program, objs, array_count(objs));
585 gl::set_uniform_i(r->picker_program, "u_canvas", 0);
586 }
587 { // Layer blend program
588 r->layer_blend_program = glCreateProgram();
589 GLuint objs[2] = {};
590
591 objs[0] = gl::compile_shader(g_layer_blend_v, GL_VERTEX_SHADER);
592 objs[1] = gl::compile_shader(g_layer_blend_f, GL_FRAGMENT_SHADER);
593 gl::link_program(r->layer_blend_program, objs, array_count(objs));
594 gl::set_uniform_i(r->layer_blend_program, "u_canvas", 0);
595 }
596 { // Brush outline program
597 r->outline_program = glCreateProgram();
598 GLuint objs[2] = {};
599 objs[0] = gl::compile_shader(g_outline_v, GL_VERTEX_SHADER);
600 objs[1] = gl::compile_shader(g_outline_f, GL_FRAGMENT_SHADER);
601
602 gl::link_program(r->outline_program, objs, array_count(objs));
603 }
604 { // Exporter program
605 r->exporter_program = glCreateProgram();
606
607 GLuint objs[2] = {};
608 objs[0] = gl::compile_shader(g_simple_v, GL_VERTEX_SHADER);
609 objs[1] = gl::compile_shader(g_exporter_rect_f, GL_FRAGMENT_SHADER);
610
611 gl::link_program(r->exporter_program, objs, array_count(objs));
612 gl::set_uniform_i(r->exporter_program, "u_canvas", 0);
613 }
614 {
615 r->texture_fill_program = glCreateProgram();
616 GLuint objs[2] = {};
617 objs[0] = gl::compile_shader(g_simple_v, GL_VERTEX_SHADER);
618 objs[1] = gl::compile_shader(g_texture_fill_f, GL_FRAGMENT_SHADER);
619
620 gl::link_program(r->texture_fill_program, objs, array_count(objs));
621 gl::set_uniform_i(r->texture_fill_program, "u_canvas", 0);
622 }
623 {
624 r->postproc_program = glCreateProgram();
625 GLuint objs[2] = {};
626 objs[0] = gl::compile_shader(g_simple_v, GL_VERTEX_SHADER);
627 objs[1] = gl::compile_shader(g_postproc_f, GL_FRAGMENT_SHADER);
628
629 gl::link_program(r->postproc_program, objs, array_count(objs));
630 gl::set_uniform_i(r->postproc_program, "u_canvas", 0);
631 }
632 {
633 r->blur_program = glCreateProgram();
634 GLuint objs[2] = {};
635 objs[0] = gl::compile_shader(g_simple_v, GL_VERTEX_SHADER);
636 objs[1] = gl::compile_shader(g_blur_f, GL_FRAGMENT_SHADER);
637 gl::link_program(r->blur_program, objs, array_count(objs));
638 gl::set_uniform_i(r->blur_program, "u_canvas", 0);
639 }
640 #if MILTON_DEBUG
641 { // Simple program
642 r->simple_program = glCreateProgram();
643
644 GLuint objs[2] = {};
645 objs[0] = gl::compile_shader(g_simple_v, GL_VERTEX_SHADER);
646 objs[1] = gl::compile_shader(g_simple_f, GL_FRAGMENT_SHADER);
647
648 gl::link_program(r->simple_program, objs, array_count(objs));
649 }
650 #endif
651
652 // Framebuffer object for canvas. Layer buffer
653 {
654 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
655 r->canvas_texture = gl::new_color_texture_multisample(view->screen_size.w, view->screen_size.h);
656 } else {
657 r->canvas_texture = gl::new_color_texture(view->screen_size.w, view->screen_size.h);
658 }
659
660 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
661 r->eraser_texture = gl::new_color_texture_multisample(view->screen_size.w, view->screen_size.h);
662 } else {
663 r->eraser_texture = gl::new_color_texture(view->screen_size.w, view->screen_size.h);
664 }
665
666 glGenTextures(1, &r->helper_texture);
667
668 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
669 r->helper_texture = gl::new_color_texture_multisample(view->screen_size.w, view->screen_size.h);
670 } else {
671 r->helper_texture = gl::new_color_texture(view->screen_size.w, view->screen_size.h);
672 }
673
674 // Stroke info buffer
675 {
676 r->stroke_info_texture = gl::new_color_texture(view->screen_size.w, view->screen_size.h);
677 print_framebuffer_status();
678 }
679
680
681 glGenTextures(1, &r->stencil_texture);
682
683 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
684 r->stencil_texture = gl::new_depth_stencil_texture_multisample(view->screen_size.w, view->screen_size.h);
685 }
686 else {
687 r->stencil_texture = gl::new_depth_stencil_texture(view->screen_size.w, view->screen_size.h);
688 }
689
690 // Create framebuffer object.
691 GLenum texture_target;
692 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
693 texture_target = GL_TEXTURE_2D_MULTISAMPLE;
694 }
695 else{
696 texture_target = GL_TEXTURE_2D;
697 }
698 r->fbo = gl::new_fbo(r->canvas_texture, r->stencil_texture, texture_target);
699 glBindFramebufferEXT(GL_FRAMEBUFFER, r->fbo);
700 print_framebuffer_status();
701 glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
702 }
703 // VBO for picker
704 glGenBuffers(1, &r->vbo_picker);
705 glGenBuffers(1, &r->vbo_picker_norm);
706
707 // Call gpu_update_picker() to initialize the color picker
708 gpu_update_picker(r, picker);
709 return result;
710 }
711
712 void
gpu_resize(RenderBackend * r,CanvasView * view)713 gpu_resize(RenderBackend* r, CanvasView* view)
714 {
715 r->width = view->screen_size.w;
716 r->height = view->screen_size.h;
717
718 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
719 gl::resize_color_texture_multisample(r->eraser_texture, r->width, r->height);
720 gl::resize_color_texture_multisample(r->canvas_texture, r->width, r->height);
721 gl::resize_color_texture_multisample(r->helper_texture, r->width, r->height);
722 gl::resize_depth_stencil_texture_multisample(r->stencil_texture, r->width, r->height);
723 }
724 else {
725 gl::resize_color_texture(r->eraser_texture, r->width, r->height);
726 gl::resize_color_texture(r->canvas_texture, r->width, r->height);
727 gl::resize_color_texture(r->helper_texture, r->width, r->height);
728 gl::resize_color_texture(r->stroke_info_texture, r->width, r->height);
729 gl::resize_depth_stencil_texture(r->stencil_texture, r->width, r->height);
730 }
731 }
732
733 void
gpu_reset_render_flags(RenderBackend * r,int flags)734 gpu_reset_render_flags(RenderBackend* r, int flags)
735 {
736 r->flags = flags;
737 }
738
739 void
gpu_update_scale(RenderBackend * r,i32 scale)740 gpu_update_scale(RenderBackend* r, i32 scale)
741 {
742 r->scale = scale;
743 GLuint ps[] = {
744 r->stroke_program,
745 r->stroke_eraser_program,
746 r->stroke_info_program,
747 r->stroke_fill_program_pressure,
748 r->stroke_fill_program_pressure_distance,
749 r->stroke_fill_program_distance,
750 r->stroke_clear_program,
751 };
752 for (sz i = 0; i < array_count(ps); ++i) {
753 gl::set_uniform_i(ps[i], "u_scale", scale);
754 }
755 }
756
757 void
gpu_update_export_rect(RenderBackend * r,Exporter * exporter)758 gpu_update_export_rect(RenderBackend* r, Exporter* exporter)
759 {
760 if ( r->vbo_exporter == 0 ) {
761 glGenBuffers(1, &r->vbo_exporter);
762 mlt_assert(r->exporter_indices == 0);
763 glGenBuffers(1, &r->exporter_indices);
764 }
765
766 i32 x = min(exporter->pivot.x, exporter->needle.x);
767 i32 y = min(exporter->pivot.y, exporter->needle.y);
768 i32 w = MLT_ABS(exporter->pivot.x - exporter->needle.x);
769 i32 h = MLT_ABS(exporter->pivot.y - exporter->needle.y);
770
771 float left = 2*((float) x/(r->width))-1;
772 float right = 2*((GLfloat)(x+w)/(r->width))-1;
773 float top = -(2*((GLfloat)y /(r->height))-1);
774 float bottom = -(2*((GLfloat)(y+h)/(r->height))-1);
775
776 // Normalize to [-1,1]^2
777 v2f normalized_rect[] = {
778 { 2*((GLfloat) x/(r->width))-1, -(2*((GLfloat)y /(r->height))-1) },
779 { 2*((GLfloat) x/(r->width))-1, -(2*((GLfloat)(y+h)/(r->height))-1) },
780 { 2*((GLfloat)(x+w)/(r->width))-1, -(2*((GLfloat)(y+h)/(r->height))-1) },
781 { 2*((GLfloat)(x+w)/(r->width))-1, -(2*((GLfloat)y /(r->height))-1) },
782 };
783
784 float px = 2.0f;
785 float line_width = px / r->height;
786
787 float toparr[] = {
788 // Top quad
789 left, top - line_width/2,
790 left, top + line_width/2,
791 right, top + line_width/2,
792 right, top - line_width/2,
793
794 // Bottom quad
795 left, bottom-line_width/2,
796 left, bottom+line_width/2,
797 right, bottom+line_width/2,
798 right, bottom-line_width/2,
799
800 // Left
801 left-line_width/2, top,
802 left-line_width/2, bottom,
803 left+line_width/2, bottom,
804 left+line_width/2, top,
805
806 // Right
807 right-line_width/2, top,
808 right-line_width/2, bottom,
809 right+line_width/2, bottom,
810 right+line_width/2, top,
811 };
812 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_exporter);
813 DEBUG_gl_mark_buffer(r->vbo_exporter);
814 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)array_count(toparr)*sizeof(*toparr), toparr, GL_DYNAMIC_DRAW);
815
816 u16 indices[] = {
817 // top
818 0,1,2,
819 2,3,0,
820
821 // bottom
822 4,5,6,
823 6,7,4,
824
825 // left
826 8,9,10,
827 10,11,8,
828
829 // right
830 12,13,14,
831 14,15,12,
832 };
833 glBindBuffer(GL_ARRAY_BUFFER, r->exporter_indices);
834 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)array_count(indices)*sizeof(*indices), indices, GL_STATIC_DRAW);
835
836 r->exporter_indices_count = array_count(indices);
837 }
838
839 void
gpu_update_background(RenderBackend * r,v3f background_color)840 gpu_update_background(RenderBackend* r, v3f background_color)
841 {
842 r->background_color = background_color;
843 }
844
845 void
gpu_get_viewport_limits(RenderBackend * r,float * out_viewport_limits)846 gpu_get_viewport_limits(RenderBackend* r, float* out_viewport_limits)
847 {
848 if ( out_viewport_limits ) {
849 out_viewport_limits[0] = r->viewport_limits[0];
850 out_viewport_limits[1] = r->viewport_limits[1];
851 }
852 }
853
854 i32
gpu_get_num_clipped_strokes(Layer * root_layer)855 gpu_get_num_clipped_strokes(Layer* root_layer)
856 {
857 i32 count = 0;
858 #if MILTON_ENABLE_PROFILING
859 for ( Layer* l = root_layer; l != NULL; l = l->next ) {
860 StrokeList strokes = l->strokes;
861 for ( i64 si = 0; si < strokes.count; ++si ) {
862 Stroke* s = strokes[si];
863 RenderElement* re = get_render_element(s->render_handle);
864 if ( re && re->vbo_stroke != 0 ) {
865 ++count;
866 }
867 }
868 }
869 #endif
870 return count;
871 }
872
873 static void
set_screen_size(RenderBackend * r,float * fscreen)874 set_screen_size(RenderBackend* r, float* fscreen)
875 {
876 GLuint programs[] = {
877 r->stroke_program,
878 r->stroke_eraser_program,
879 r->stroke_info_program,
880 r->stroke_fill_program_pressure,
881 r->stroke_fill_program_pressure_distance,
882 r->stroke_fill_program_distance,
883 r->stroke_clear_program,
884 r->layer_blend_program,
885 r->texture_fill_program,
886 r->exporter_program,
887 r->picker_program,
888 r->postproc_program,
889 r->blur_program,
890 };
891 for ( u64 pi = 0; pi < array_count(programs); ++pi ) {
892 gl::set_uniform_vec2(programs[pi], "u_screen_size", 1, fscreen);
893 }
894 }
895
896 static
897 v2i
relative_to_render_center(RenderBackend * r,v2l point)898 relative_to_render_center(RenderBackend* r, v2l point)
899 {
900 v2i result = VEC2I(point - VEC2L(r->render_center*(1<<RENDER_CHUNK_SIZE_LOG2)));
901 return result;
902 }
903
904 void
gpu_update_canvas(RenderBackend * r,CanvasState * canvas,CanvasView * view)905 gpu_update_canvas(RenderBackend* r, CanvasState* canvas, CanvasView* view)
906 {
907 v2i center = view->zoom_center;
908 v2l pan = view->pan_center;
909
910 v2i new_render_center = VEC2I(pan / (i64)(1<<RENDER_CHUNK_SIZE_LOG2));
911 if ( new_render_center != r->render_center ) {
912 milton_log("Moving to new render center. %d, %d Clearing render data.\n", new_render_center.x, new_render_center.y);
913 r->render_center = new_render_center;
914 gpu_free_strokes(r, canvas);
915 }
916
917 GLuint ps[] = {
918 r->stroke_program,
919 r->stroke_eraser_program,
920 r->stroke_info_program,
921 r->stroke_fill_program_pressure,
922 r->stroke_fill_program_pressure_distance,
923 r->stroke_fill_program_distance,
924 r->stroke_clear_program,
925 };
926
927 f32 cos_angle = cosf(view->angle);
928 f32 sin_angle = sinf(view->angle);
929
930 // GLSL is column-major
931 f32 matrix[] = { cos_angle, sin_angle, -sin_angle, cos_angle };
932
933 f32 matrix_inverse[] = { cos_angle, -sin_angle, sin_angle, cos_angle };
934
935 for (sz i = 0; i < array_count(ps); ++i) {
936 gl::set_uniform_mat2(ps[i], "u_rotation", matrix);
937 gl::set_uniform_mat2(ps[i], "u_rotation_inverse", matrix_inverse);
938 gl::set_uniform_vec2i(ps[i], "u_pan_center", 1, relative_to_render_center(r, pan).d);
939 gl::set_uniform_vec2i(ps[i], "u_zoom_center", 1, center.d);
940 }
941
942 gpu_update_scale(r, view->scale);
943 float fscreen[] = { (float)view->screen_size.x, (float)view->screen_size.y };
944 set_screen_size(r, fscreen);
945 }
946
947 void
gpu_cook_stroke(Arena * arena,RenderBackend * r,Stroke * stroke,CookStrokeOpt cook_option)948 gpu_cook_stroke(Arena* arena, RenderBackend* r, Stroke* stroke, CookStrokeOpt cook_option)
949 {
950
951 RenderElement** p_render_element = reinterpret_cast<RenderElement**>(&stroke->render_handle);
952 RenderElement* render_element = *p_render_element;
953 if (render_element == NULL) {
954 render_element = arena_alloc_elem(arena, RenderElement);
955 *p_render_element = render_element;
956 }
957
958 r->stroke_z = (r->stroke_z + 1) % (MAX_DEPTH_VALUE-1);
959 const i32 stroke_z = r->stroke_z + 1;
960
961 if ( cook_option == CookStroke_NEW && render_element->vbo_stroke != 0 ) {
962 // We already have our data cooked
963 mlt_assert(render_element->vbo_pointa != 0);
964 mlt_assert(render_element->vbo_pointb != 0);
965 } else {
966 auto npoints = stroke->num_points;
967 if ( npoints == 1 ) {
968 // Create a 2-point stroke and recurse
969 Stroke duplicate = *stroke;
970 duplicate.num_points = 2;
971 Arena scratch_arena = arena_push(arena);
972 duplicate.points = arena_alloc_array(&scratch_arena, 2, v2l);
973 duplicate.pressures = arena_alloc_array(&scratch_arena, 2, f32);
974 duplicate.points[0] = stroke->points[0]; // It will be set relative to the center in the recursed call.
975 duplicate.points[1] = stroke->points[0];
976 duplicate.pressures[0] = stroke->pressures[0];
977 duplicate.pressures[1] = stroke->pressures[0];
978
979 gpu_cook_stroke(&scratch_arena, r, &duplicate, cook_option);
980
981 // Copy render element to stroke
982 stroke->render_handle = duplicate.render_handle;
983
984 arena_pop(&scratch_arena);
985 }
986 else if ( npoints > 1 ) {
987 // 3 (triangle) *
988 // 2 (two per segment) *
989 // N-1 (segments per stroke)
990 // Reduced to 4 by using indices
991 const size_t count_attribs = 4*((size_t)npoints-1);
992
993 // 6 (3 * 2 from count_attribs)
994 // N-1 (num segments)
995 const size_t count_indices = 6*((size_t)npoints-1);
996
997 size_t count_debug = 0;
998 v3f* bounds;
999 v3f* apoints;
1000 v3f* bpoints;
1001 v3f* debug = NULL;
1002 u16* indices;
1003 Arena scratch_arena = arena_push(arena,
1004 count_attribs*sizeof(decltype(*bounds)) // Bounds
1005 + 2*count_attribs*sizeof(decltype(*apoints)) // Attributes a,b
1006 + count_debug*sizeof(decltype(*debug)) // Visualization
1007 + count_indices*sizeof(decltype(*indices))); // Interpolation points
1008
1009 bounds = arena_alloc_array(&scratch_arena, count_attribs, v3f);
1010 apoints = arena_alloc_array(&scratch_arena, count_attribs, v3f);
1011 bpoints = arena_alloc_array(&scratch_arena, count_attribs, v3f);
1012 indices = arena_alloc_array(&scratch_arena, count_indices, u16);
1013 #if STROKE_DEBUG_VIZ
1014 debug = arena_alloc_array(&scratch_arena, count_debug, v3f);
1015 #endif
1016
1017 mlt_assert(r->scale > 0);
1018
1019 size_t bounds_i = 0;
1020 size_t apoints_i = 0;
1021 size_t bpoints_i = 0;
1022 size_t indices_i = 0;
1023 size_t debug_i = 0;
1024 for ( i64 i=0; i < npoints-1; ++i ) {
1025 v2i point_i = relative_to_render_center(r, stroke->points[i]);
1026 v2i point_j = relative_to_render_center(r, stroke->points[i+1]);
1027
1028 Brush brush = stroke->brush;
1029 float radius_i = stroke->pressures[i]*brush.radius;
1030 float radius_j = stroke->pressures[i+1]*brush.radius;
1031
1032 u16 idx = (u16)bounds_i;
1033 if ( point_i == point_j ) {
1034 i32 min_x = min(point_i.x - radius_i, point_j.x - radius_j);
1035 i32 min_y = min(point_i.y - radius_i, point_j.y - radius_j);
1036 i32 max_x = max(point_i.x + radius_i, point_j.x + radius_j);
1037 i32 max_y = max(point_i.y + radius_i, point_j.y + radius_j);
1038
1039 // Bounding geometry and attributes
1040
1041 mlt_assert (bounds_i < ((1<<16)-4));
1042
1043 bounds[bounds_i++] = { (float)min_x, (float)min_y, (float)stroke_z };
1044 bounds[bounds_i++] = { (float)min_x, (float)max_y, (float)stroke_z };
1045 bounds[bounds_i++] = { (float)max_x, (float)max_y, (float)stroke_z };
1046 bounds[bounds_i++] = { (float)max_x, (float)min_y, (float)stroke_z };
1047 } else {
1048 // Points are different. Do a coordinate change for a tighter box.
1049 v2f d = normalized(v2i_to_v2f(point_j - point_i));
1050 auto basis_change = [&d](v2f v) {
1051 v2f res = {
1052 v.x * d.x + v.y * d.y,
1053 v.x * d.y - v.y * d.x,
1054 };
1055
1056 return res;
1057 };
1058 v2f a = basis_change(v2i_to_v2f(point_i));
1059 v2f b = basis_change(v2i_to_v2f(point_j));
1060
1061 f32 rad = max(radius_i, radius_j);
1062
1063 f32 min_x = min(a.x, b.x) - rad;
1064 f32 min_y = min(a.y, b.y) - rad;
1065 f32 max_x = max(a.x, b.x) + rad;
1066 f32 max_y = max(a.y, b.y) + rad;
1067
1068 v2f A = basis_change(v2f{ min_x, min_y });
1069 v2f B = basis_change(v2f{ min_x, max_y });
1070 v2f C = basis_change(v2f{ max_x, max_y });
1071 v2f D = basis_change(v2f{ max_x, min_y });
1072
1073 mlt_assert (bounds_i < ((1<<16)-4));
1074
1075 bounds[bounds_i++] = { A.x, A.y, (float)stroke_z };
1076 bounds[bounds_i++] = { B.x, B.y, (float)stroke_z };
1077 bounds[bounds_i++] = { C.x, C.y, (float)stroke_z };
1078 bounds[bounds_i++] = { D.x, D.y, (float)stroke_z };
1079 }
1080
1081 indices[indices_i++] = (u16)(idx + 0);
1082 indices[indices_i++] = (u16)(idx + 1);
1083 indices[indices_i++] = (u16)(idx + 2);
1084
1085 indices[indices_i++] = (u16)(idx + 2);
1086 indices[indices_i++] = (u16)(idx + 0);
1087 indices[indices_i++] = (u16)(idx + 3);
1088
1089 float pressure_a = stroke->pressures[i];
1090 float pressure_b = stroke->pressures[i+1];
1091
1092 // Add attributes for each new vertex.
1093 for ( int repeat = 0; repeat < 4; ++repeat ) {
1094 apoints[apoints_i++] = { (float)point_i.x, (float)point_i.y, pressure_a };
1095 bpoints[bpoints_i++] = { (float)point_j.x, (float)point_j.y, pressure_b };
1096 #if STROKE_DEBUG_VIZ
1097 v3f debug_color;
1098
1099 if ( stroke->debug_flags[i] & Stroke::INTERPOLATED ) {
1100 debug_color = { 1.0f, 0.0f, 0.0f };
1101 }
1102 else {
1103 debug_color = { 0.0f, 1.0f, 0.0f };
1104 }
1105 debug[debug_i++] = debug_color;
1106 #endif
1107 }
1108 }
1109
1110 mlt_assert(bounds_i == count_attribs);
1111 mlt_assert(apoints_i == bpoints_i);
1112 mlt_assert(apoints_i == bounds_i);
1113
1114 // TODO: check for GL_OUT_OF_MEMORY
1115
1116 GLuint vbo_stroke = 0;
1117 GLuint vbo_pointa = 0;
1118 GLuint vbo_pointb = 0;
1119 GLuint indices_buffer = 0;
1120 GLuint vbo_debug = 0;
1121
1122
1123 GLenum hint = GL_STATIC_DRAW;
1124 if ( cook_option == CookStroke_UPDATE_WORKING_STROKE ) {
1125 hint = GL_DYNAMIC_DRAW;
1126 }
1127 if ( render_element->vbo_stroke != 0 ) {
1128 vbo_stroke = render_element->vbo_stroke;
1129 vbo_pointa = render_element->vbo_pointa;
1130 vbo_pointb = render_element->vbo_pointb;
1131 indices_buffer = render_element->indices;
1132 #if STROKE_DEBUG_VIZ
1133 vbo_debug = render_element->vbo_debug;
1134 #endif
1135
1136 auto clear_array_buffer = [hint](GLint vbo, size_t size) {
1137 glBindBuffer(GL_ARRAY_BUFFER, vbo);
1138 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(size), NULL, hint);
1139 };
1140 clear_array_buffer(vbo_stroke, bounds_i*sizeof(decltype(*bounds)));
1141 clear_array_buffer(vbo_pointa, bounds_i*sizeof(decltype(*apoints)));
1142 clear_array_buffer(vbo_pointb, bounds_i*sizeof(decltype(*bpoints)));
1143 clear_array_buffer(indices_buffer, indices_i*sizeof(decltype(*indices)));
1144 #if STROKE_DEBUG_VIZ
1145 clear_array_buffer(vbo_debug, debug_i*sizeof(decltype(*debug)));
1146 #endif
1147 }
1148 else {
1149 glGenBuffers(1, &vbo_stroke);
1150 glGenBuffers(1, &vbo_pointa);
1151 glGenBuffers(1, &vbo_pointb);
1152 glGenBuffers(1, &indices_buffer);
1153 #if STROKE_DEBUG_VIZ
1154 glGenBuffers(1, &vbo_debug);
1155 #endif
1156
1157 DEBUG_gl_mark_buffer(vbo_stroke);
1158 DEBUG_gl_mark_buffer(vbo_pointa);
1159 DEBUG_gl_mark_buffer(vbo_pointb);
1160 DEBUG_gl_mark_buffer(indices_buffer);
1161 }
1162
1163 /*Send data to GPU*/ {
1164 auto send_buffer_data = [hint](GLint vbo, size_t size, void* data) {
1165 glBindBuffer(GL_ARRAY_BUFFER, vbo);
1166 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(size), data, hint);
1167 };
1168
1169 send_buffer_data(vbo_stroke, bounds_i*sizeof(decltype(*bounds)), bounds);
1170 send_buffer_data(vbo_pointa, bounds_i*sizeof(decltype(*apoints)), apoints);
1171 send_buffer_data(vbo_pointb, bounds_i*sizeof(decltype(*bpoints)), bpoints);
1172 send_buffer_data(indices_buffer, indices_i*sizeof(decltype(*indices)), indices);
1173 #if STROKE_DEBUG_VIZ
1174 send_buffer_data(vbo_debug, debug_i*sizeof(decltype(*debug)), debug);
1175 #endif
1176 }
1177
1178 RenderElement* re = get_render_element(stroke->render_handle);
1179 re->vbo_stroke = vbo_stroke;
1180 re->vbo_pointa = vbo_pointa;
1181 re->vbo_pointb = vbo_pointb;
1182 re->indices = indices_buffer;
1183 #if STROKE_DEBUG_VIZ
1184 re->vbo_debug = vbo_debug;
1185 #endif
1186 re->count = (i64)(indices_i);
1187 re->color = { stroke->brush.color.r, stroke->brush.color.g, stroke->brush.color.b, stroke->brush.color.a };
1188 re->radius = stroke->brush.radius;
1189 re->min_opacity = stroke->brush.pressure_opacity_min;
1190 re->hardness = stroke->brush.hardness;
1191
1192 re->flags = 0;
1193 if (stroke->flags & StrokeFlag_ERASER) {
1194 re->flags |= RenderElementFlags_ERASER;
1195 }
1196 if (stroke->flags & StrokeFlag_PRESSURE_TO_OPACITY) {
1197 re->flags |= RenderElementFlags_PRESSURE_TO_OPACITY;
1198 }
1199 if (stroke->flags & StrokeFlag_DISTANCE_TO_OPACITY) {
1200 re->flags |= RenderElementFlags_DISTANCE_TO_OPACITY;
1201 }
1202
1203 mlt_assert(re->count > 1);
1204
1205 arena_pop(&scratch_arena);
1206 }
1207 }
1208 }
1209
1210 void
gpu_free_strokes(Stroke * strokes,i64 count,RenderBackend * r)1211 gpu_free_strokes(Stroke* strokes, i64 count, RenderBackend* r)
1212 {
1213 for ( i64 i = 0; i < count; ++i ) {
1214 Stroke* s = &strokes[i];
1215 RenderElement* re = get_render_element(s->render_handle);
1216 if ( re && re->vbo_stroke != 0 ) {
1217 mlt_assert(re->vbo_pointa != 0);
1218 mlt_assert(re->vbo_pointb != 0);
1219 mlt_assert(re->indices != 0);
1220
1221 DEBUG_gl_validate_buffer(re->vbo_stroke);
1222 DEBUG_gl_validate_buffer(re->vbo_pointa);
1223 DEBUG_gl_validate_buffer(re->vbo_pointb);
1224 DEBUG_gl_validate_buffer(re->indices);
1225
1226 glDeleteBuffers(1, &re->vbo_stroke);
1227 glDeleteBuffers(1, &re->vbo_pointa);
1228 glDeleteBuffers(1, &re->vbo_pointb);
1229 glDeleteBuffers(1, &re->indices);
1230
1231 DEBUG_gl_unmark_buffer(re->vbo_stroke);
1232 DEBUG_gl_unmark_buffer(re->vbo_pointa);
1233 DEBUG_gl_unmark_buffer(re->vbo_pointb);
1234 DEBUG_gl_unmark_buffer(re->indices);
1235
1236 *re = {};
1237 }
1238 }
1239 }
1240
1241 void
gpu_free_strokes(RenderBackend * r,CanvasState * canvas)1242 gpu_free_strokes(RenderBackend* r, CanvasState* canvas)
1243 {
1244 if ( canvas->root_layer != NULL ) {
1245 for ( Layer* l = canvas->root_layer;
1246 l != NULL;
1247 l = l->next ) {
1248 StrokeList* sl = &l->strokes;
1249 StrokeBucket* bucket = &sl->root;
1250 i64 count = sl->count;
1251 while ( bucket ) {
1252 if ( count >= STROKELIST_BUCKET_COUNT ) {
1253 count -= STROKELIST_BUCKET_COUNT;
1254 gpu_free_strokes(bucket->data, STROKELIST_BUCKET_COUNT, r);
1255 } else {
1256 gpu_free_strokes(bucket->data, count, r);
1257 }
1258 bucket = bucket->next;
1259 }
1260 }
1261 }
1262 }
1263
1264 void
gpu_clip_strokes_and_update(Arena * arena,RenderBackend * r,CanvasView * view,i64 scale,Layer * root_layer,Stroke * working_stroke,i32 x,i32 y,i32 w,i32 h,ClipFlags flags)1265 gpu_clip_strokes_and_update(Arena* arena,
1266 RenderBackend* r,
1267 CanvasView* view,
1268 i64 scale,
1269 Layer* root_layer, Stroke* working_stroke,
1270 i32 x, i32 y, i32 w, i32 h, ClipFlags flags)
1271 {
1272 DArray<RenderElement>* clip_array = &r->clip_array;
1273
1274 RenderElement layer_element = {};
1275 layer_element.flags |= RenderElementFlags_LAYER;
1276
1277 Rect screen_bounds = raster_to_canvas_bounding_rect(view, x, y, w, h, scale);
1278
1279 reset(clip_array);
1280
1281 if (screen_bounds.left != screen_bounds.right &&
1282 screen_bounds.top != screen_bounds.bottom) {
1283 #if MILTON_ENABLE_PROFILING
1284 {
1285 r->clipped_count = 0;
1286 }
1287 #endif
1288 for ( Layer* l = root_layer;
1289 l != NULL;
1290 l = l->next ) {
1291 if ( !(l->flags & LayerFlags_VISIBLE) ) {
1292 // Skip invisible layers.
1293 continue;
1294 }
1295
1296 StrokeBucket* bucket = &l->strokes.root;
1297 i64 bucket_i = 0;
1298
1299 while ( bucket ) {
1300 i64 count = 0;
1301 if ( l->strokes.count < bucket_i * STROKELIST_BUCKET_COUNT ) {
1302 // There is an allocated bucket but we have already iterated
1303 // through all the actual strokes.
1304 break;
1305 }
1306 if ( l->strokes.count - bucket_i*STROKELIST_BUCKET_COUNT >= STROKELIST_BUCKET_COUNT ) {
1307 count = STROKELIST_BUCKET_COUNT;
1308 } else {
1309 count = l->strokes.count % STROKELIST_BUCKET_COUNT;
1310 }
1311
1312 Rect bbox = bucket->bounding_rect;
1313
1314 b32 bucket_outside = screen_bounds.left > bbox.right
1315 || screen_bounds.top > bbox.bottom
1316 || screen_bounds.right < bbox.left
1317 || screen_bounds.bottom < bbox.top;
1318
1319 if ( !bucket_outside ) {
1320 for ( i64 i = 0; i < count; ++i ) {
1321 Stroke* s = &bucket->data[i];
1322
1323 if ( s != NULL ) {
1324 Rect bounds = s->bounding_rect;
1325
1326 b32 stroke_outside = screen_bounds.left > bounds.right
1327 || screen_bounds.top > bounds.bottom
1328 || screen_bounds.right < bounds.left
1329 || screen_bounds.bottom < bounds.top;
1330
1331 i32 area = (bounds.right-bounds.left) * (bounds.bottom-bounds.top);
1332 // Area might be 0 if the stroke is smaller than
1333 // a pixel. We don't draw it in that case.
1334 if ( !stroke_outside && area!=0 ) {
1335 gpu_cook_stroke(arena, r, s);
1336 push(clip_array, *get_render_element(s->render_handle));
1337 }
1338 else if ( stroke_outside && ( flags & ClipFlags_UPDATE_GPU_DATA ) ) {
1339 // If it is far away, delete.
1340 i32 distance = MLT_ABS(bounds.left - x + bounds.top - y);
1341 const i32 min_number_of_screens = 4;
1342 if ( bounds.top < y - min_number_of_screens*h
1343 || bounds.bottom > y+h + min_number_of_screens*h
1344 || bounds.left > x+w + min_number_of_screens*w
1345 || bounds.right < x - min_number_of_screens*w ) {
1346 gpu_free_strokes(s, 1, r);
1347 }
1348 }
1349 }
1350 }
1351 }
1352 else if ( flags & ClipFlags_UPDATE_GPU_DATA ) {
1353 gpu_free_strokes(bucket->data, count, r);
1354 }
1355 #if MILTON_ENABLE_PROFILING
1356 {
1357 for ( i64 i = 0; i < count; ++i ) {
1358 Stroke* s = &bucket->data[i];
1359 RenderElement* re = get_render_element(s->render_handle);
1360 if ( re && re->vbo_stroke != 0 ) {
1361 r->clipped_count++;
1362 }
1363 }
1364 }
1365 #endif
1366 bucket = bucket->next;
1367 bucket_i += 1;
1368 }
1369
1370 // Add the working stroke on the current layer.
1371 if ( working_stroke->layer_id == l->id ) {
1372 if ( working_stroke->num_points > 0 ) {
1373 gpu_cook_stroke(arena, r, working_stroke, CookStroke_UPDATE_WORKING_STROKE);
1374
1375 push(clip_array, *get_render_element(working_stroke->render_handle));
1376 }
1377 }
1378
1379 auto* p = push(clip_array, layer_element);
1380 p->layer_alpha = l->alpha;
1381 p->effects = l->effects;
1382 }
1383 }
1384 }
1385
1386 static void
gpu_fill_with_texture(RenderBackend * r,float alpha=1.0f)1387 gpu_fill_with_texture(RenderBackend* r, float alpha = 1.0f)
1388 {
1389 // Assumes that texture object is already bound.
1390 gl::use_program(r->texture_fill_program);
1391 gl::set_uniform_f(r->texture_fill_program, "u_alpha", alpha);
1392 {
1393 GLint t_loc = glGetAttribLocation(r->texture_fill_program, "a_position");
1394 if ( t_loc >= 0 ) {
1395 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_screen_quad);
1396 glEnableVertexAttribArray((GLuint)t_loc);
1397 glVertexAttribPointer(/*attrib location*/ (GLuint)t_loc,
1398 /*size*/ 2, GL_FLOAT, /*normalize*/ GL_FALSE,
1399 /*stride*/ 0, /*ptr*/ 0);
1400 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1401 }
1402 }
1403 }
1404
1405 enum BoxFilterPass
1406 {
1407 BoxFilterPass_VERTICAL = 0,
1408 BoxFilterPass_HORIZONTAL = 1,
1409 };
1410 static void
box_filter_pass(RenderBackend * r,int kernel_size,int direction)1411 box_filter_pass(RenderBackend* r, int kernel_size, int direction)
1412 {
1413 gl::use_program(r->blur_program);
1414 gl::set_uniform_i(r->blur_program, "u_kernel_size", kernel_size);
1415 GLint t_loc = glGetAttribLocation(r->blur_program, "a_position");
1416 if ( t_loc >= 0 ) {
1417 gl::set_uniform_i(r->blur_program, "u_direction", direction);
1418 {
1419 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_screen_quad);
1420 glEnableVertexAttribArray((GLuint)t_loc);
1421 glVertexAttribPointer(/*attrib location*/ (GLuint)t_loc,
1422 /*size*/ 2, GL_FLOAT, /*normalize*/ GL_FALSE,
1423 /*stride*/ 0, /*ptr*/ 0);
1424 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1425 }
1426 }
1427 }
1428
1429 static void
gpu_render_canvas(RenderBackend * r,i32 view_x,i32 view_y,i32 view_width,i32 view_height,float background_alpha=1.0f)1430 gpu_render_canvas(RenderBackend* r, i32 view_x, i32 view_y,
1431 i32 view_width, i32 view_height, float background_alpha=1.0f)
1432 {
1433 PUSH_GRAPHICS_GROUP("render_canvas");
1434
1435 // FLip it. GL is bottom-left.
1436 i32 x = view_x;
1437 i32 y = r->height - (view_y+view_height);
1438 i32 w = view_width;
1439 i32 h = view_height;
1440 glScissor(x, y, w, h);
1441
1442 glClearDepth(0.0f);
1443
1444 glBindFramebufferEXT(GL_FRAMEBUFFER, r->fbo);
1445
1446 GLenum texture_target;
1447 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
1448 texture_target = GL_TEXTURE_2D_MULTISAMPLE;
1449 } else {
1450 texture_target = GL_TEXTURE_2D;
1451 }
1452
1453 GLuint layer_texture = r->helper_texture;
1454
1455 if ( background_alpha != 0.0f ) {
1456 // Not sure if this works OK with background_alpha != 1.0f
1457 glClearColor(r->background_color.r, r->background_color.g,
1458 r->background_color.b, background_alpha);
1459 } else {
1460 glClearColor(0,0,0,0);
1461 }
1462
1463 glBindTexture(texture_target, r->eraser_texture);
1464
1465 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target,
1466 r->eraser_texture, 0);
1467
1468 glClear(GL_COLOR_BUFFER_BIT);
1469
1470 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target,
1471 r->canvas_texture, 0);
1472
1473 glClear(GL_COLOR_BUFFER_BIT);
1474
1475 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target,
1476 layer_texture, 0);
1477 glClearColor(0,0,0,0);
1478
1479 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1480
1481 glEnable(GL_DEPTH_TEST);
1482 glEnable(GL_BLEND);
1483 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1484 glDepthFunc(GL_NOTEQUAL);
1485
1486 DArray<RenderElement>* clip_array = &r->clip_array;
1487
1488 PUSH_GRAPHICS_GROUP("render elements");
1489 for ( i64 i = 0; i < (i64)clip_array->count; i++ ) {
1490 RenderElement* re = &clip_array->data[i];
1491
1492 if ( re->flags & RenderElementFlags_LAYER ) {
1493
1494 // Layer render element.
1495 // The current framebuffer's color attachment is layer_texture.
1496
1497 // Before we fill canvas_texture with the contents of
1498 // layer_texture, we apply all layer effects.
1499
1500 GLuint layer_post_effects = layer_texture;
1501 {
1502 // eraser_texture will be rewritten below with the
1503 // contents of canvas_texture. We use it here for
1504 // the layer effects.
1505 GLuint out_texture = r->eraser_texture;
1506 GLuint in_texture = layer_texture;
1507 glDisable(GL_BLEND);
1508 glDisable(GL_DEPTH_TEST);
1509 for ( LayerEffect* e = re->effects; e != NULL; e = e->next ) {
1510 if ( e->enabled == false ) { continue; }
1511
1512 if ( e->type == LayerEffectType_BLUR ) {
1513 glBindTexture(texture_target, in_texture);
1514 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1515 texture_target, out_texture, 0);
1516
1517 // Three box filter iterations approximate a Gaussian blur
1518 for (int blur_iter = 0; blur_iter < 3; ++blur_iter) {
1519 // Box filter implementation uses the separable property.
1520 // Apply horizontal pass and then vertical pass.
1521 int kernel_size = e->blur.kernel_size * e->blur.original_scale / r->scale;
1522 box_filter_pass(r, kernel_size, BoxFilterPass_VERTICAL);
1523 swap(out_texture, in_texture);
1524 glBindTexture(texture_target, in_texture);
1525 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1526 texture_target, out_texture, 0);
1527
1528
1529 box_filter_pass(r, kernel_size, BoxFilterPass_HORIZONTAL);
1530 swap(out_texture, in_texture);
1531 glBindTexture(texture_target, in_texture);
1532 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1533 texture_target, out_texture, 0);
1534
1535 }
1536 swap(out_texture, in_texture);
1537 glBindTexture(texture_target, in_texture);
1538 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1539 texture_target, out_texture, 0);
1540 layer_post_effects = out_texture;
1541 }
1542 }
1543 glEnable(GL_BLEND);
1544 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1545 glEnable(GL_DEPTH_TEST);
1546 }
1547
1548 // Blit layer contents to canvas_texture
1549 {
1550 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1551 texture_target, r->canvas_texture, 0);
1552 glBindTexture(texture_target, layer_post_effects);
1553
1554 glDisable(GL_DEPTH_TEST);
1555
1556 gpu_fill_with_texture(r, re->layer_alpha);
1557
1558 glEnable(GL_DEPTH_TEST);
1559 }
1560
1561 // Copy canvas_texture's contents to the eraser_texture.
1562 {
1563 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1564 texture_target, r->eraser_texture, 0);
1565 glBindTexture(texture_target, r->canvas_texture);
1566
1567 glDisable(GL_BLEND);
1568 glDisable(GL_DEPTH_TEST);
1569
1570 gpu_fill_with_texture(r);
1571
1572 // Clear the layer texture.
1573 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1574 texture_target, layer_texture, 0);
1575 glClearColor(0,0,0,0);
1576 glClear(GL_COLOR_BUFFER_BIT);
1577
1578 glBindTexture(texture_target, r->eraser_texture);
1579
1580 glEnable(GL_DEPTH_TEST);
1581 glEnable(GL_BLEND);
1582 }
1583 }
1584 // If this render element is not a layer, then it is a stroke.
1585 else {
1586 GLuint program_for_stroke = r->stroke_program;
1587
1588 auto stroke_pass = [r, texture_target](RenderElement* re, GLuint program_for_stroke) {
1589 i64 count = re->count;
1590 gl::use_program(program_for_stroke);
1591 gl::set_uniform_vec4(program_for_stroke, "u_brush_color", 1, re->color.d);
1592 gl::set_uniform_i(program_for_stroke, "u_radius", re->radius);
1593
1594 DEBUG_gl_validate_buffer(re->vbo_stroke);
1595 DEBUG_gl_validate_buffer(re->vbo_pointa);
1596 DEBUG_gl_validate_buffer(re->vbo_pointb);
1597 DEBUG_gl_validate_buffer(re->indices);
1598
1599 gl::vertex_attrib_v3f(program_for_stroke, "a_pointa", re->vbo_pointa);
1600 gl::vertex_attrib_v3f(program_for_stroke, "a_pointb", re->vbo_pointb);
1601 gl::vertex_attrib_v3f(program_for_stroke, "a_position", re->vbo_stroke);
1602
1603 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, re->indices);
1604
1605 glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, 0);
1606 };
1607
1608 if ( re->count > 0 ) {
1609 if (re->flags & RenderElementFlags_ERASER) {
1610 glBindTexture(texture_target, r->eraser_texture);
1611 stroke_pass(re, r->stroke_eraser_program);
1612 }
1613 else if ( (re->flags & (RenderElementFlags_PRESSURE_TO_OPACITY | RenderElementFlags_DISTANCE_TO_OPACITY)) ) {
1614 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1615 texture_target, r->stroke_info_texture, 0);
1616
1617
1618 glDisable(GL_DEPTH_TEST);
1619 glDisable(GL_BLEND);
1620 stroke_pass(re, r->stroke_clear_program);
1621
1622 glEnable(GL_BLEND);
1623 glBlendEquationSeparate(GL_MIN, GL_MAX);
1624
1625 stroke_pass(re, r->stroke_info_program);
1626
1627 glBlendEquation(GL_FUNC_ADD);
1628
1629 glEnable(GL_DEPTH_TEST);
1630 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1631 texture_target, layer_texture, 0);
1632 glBindTexture(texture_target, r->stroke_info_texture);
1633
1634 if ( (re->flags & RenderElementFlags_PRESSURE_TO_OPACITY) &&
1635 !(re->flags & RenderElementFlags_DISTANCE_TO_OPACITY)) {
1636 gl::set_uniform_f(r->stroke_fill_program_pressure, "u_opacity_min", re->min_opacity);
1637 stroke_pass(re, r->stroke_fill_program_pressure);
1638 }
1639 else if ( !(re->flags & RenderElementFlags_PRESSURE_TO_OPACITY) &&
1640 (re->flags & RenderElementFlags_DISTANCE_TO_OPACITY)) {
1641 gl::set_uniform_f(r->stroke_fill_program_distance, "u_hardness", re->hardness);
1642 stroke_pass(re, r->stroke_fill_program_distance);
1643 }
1644 else if ( (re->flags & RenderElementFlags_PRESSURE_TO_OPACITY) &&
1645 (re->flags & RenderElementFlags_DISTANCE_TO_OPACITY)) {
1646 gl::set_uniform_f(r->stroke_fill_program_pressure_distance, "u_opacity_min", re->min_opacity);
1647 gl::set_uniform_f(r->stroke_fill_program_pressure_distance, "u_hardness", re->hardness);
1648 stroke_pass(re, r->stroke_fill_program_pressure_distance);
1649 }
1650 else {
1651 INVALID_CODE_PATH;
1652 }
1653 }
1654 else {
1655 // Fast path
1656 stroke_pass(re, r->stroke_program);
1657 }
1658 } else {
1659 static int n = 0;
1660 milton_log("Warning: Render element with count 0 [%d times]\n", ++n);
1661 }
1662 }
1663 }
1664 POP_GRAPHICS_GROUP(); // render elements
1665 glViewport(0, 0, r->width, r->height);
1666 glScissor(0, 0, r->width, r->height);
1667
1668 POP_GRAPHICS_GROUP(); // render_canvas
1669 }
1670
1671 void
gpu_render(RenderBackend * r,i32 view_x,i32 view_y,i32 view_width,i32 view_height)1672 gpu_render(RenderBackend* r, i32 view_x, i32 view_y, i32 view_width, i32 view_height)
1673 {
1674 PUSH_GRAPHICS_GROUP("gpu_render");
1675
1676 glViewport(0, 0, r->width, r->height);
1677 glScissor(0, 0, r->width, r->height);
1678 glEnable(GL_BLEND);
1679
1680 print_framebuffer_status();
1681
1682 // TODO: Do less work when idling
1683 gpu_render_canvas(r, view_x, view_y, view_width, view_height);
1684
1685 GLenum texture_target;
1686 if ( gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
1687 texture_target = GL_TEXTURE_2D_MULTISAMPLE;
1688 } else {
1689 texture_target = GL_TEXTURE_2D;
1690 }
1691
1692 // Use helper_texture as a place to do AA.
1693
1694 // Blit the canvas to helper_texture
1695
1696 glDisable(GL_DEPTH_TEST);
1697
1698 PUSH_GRAPHICS_GROUP("blit to helper texture");
1699 if ( !gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
1700 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target,
1701 r->canvas_texture, 0);
1702 glBindTexture(texture_target, r->helper_texture);
1703 glCopyTexImage2D(texture_target, 0, GL_RGBA8, 0,0, r->width, r->height, 0);
1704
1705 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target,
1706 r->helper_texture, 0);
1707 glBindTexture(texture_target, r->canvas_texture);
1708 } else {
1709 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target,
1710 r->helper_texture, 0);
1711 glBindTexture(texture_target, r->canvas_texture);
1712
1713 gpu_fill_with_texture(r);
1714 }
1715 POP_GRAPHICS_GROUP();
1716
1717 // Render GUI on top of helper_texture
1718
1719 // Render color picker
1720 // TODO: Only render if view rect intersects picker rect
1721 if ( r->flags & RenderBackendFlags_GUI_VISIBLE ) {
1722 // Render picker
1723 gl::use_program(r->picker_program);
1724 GLint loc = glGetAttribLocation(r->picker_program, "a_position");
1725
1726 if ( loc >= 0 ) {
1727 DEBUG_gl_validate_buffer(r->vbo_picker);
1728 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_picker);
1729 glVertexAttribPointer(/*attrib location*/(GLuint)loc,
1730 /*size*/2, GL_FLOAT, /*normalize*/GL_FALSE,
1731 /*stride*/0, /*ptr*/0);
1732 glEnableVertexAttribArray((GLuint)loc);
1733 GLint loc_norm = glGetAttribLocation(r->picker_program, "a_norm");
1734
1735 if ( loc_norm >= 0 ) {
1736 DEBUG_gl_validate_buffer(r->vbo_picker_norm);
1737 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_picker_norm);
1738 glVertexAttribPointer(/*attrib location*/(GLuint)loc_norm,
1739 /*size*/2, GL_FLOAT, /*normalize*/GL_FALSE,
1740 /*stride*/0, /*ptr*/0);
1741 glEnableVertexAttribArray((GLuint)loc_norm);
1742
1743 }
1744 glDrawArrays(GL_TRIANGLE_FAN,0,4);
1745 }
1746 }
1747
1748 // Do post-processing on painting and on GUI elements. Draw to backbuffer
1749
1750 PUSH_GRAPHICS_GROUP("postproc");
1751 if ( !gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
1752 glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
1753
1754 // glActiveTexture(GL_TEXTURE0);
1755 glBindTexture(GL_TEXTURE_2D, r->helper_texture);
1756
1757 gl::set_uniform_i(r->postproc_program, "u_canvas", 0);
1758
1759 gl::use_program(r->postproc_program);
1760
1761 GLint loc = glGetAttribLocation(r->postproc_program, "a_position");
1762 if ( loc >= 0 ) {
1763 DEBUG_gl_validate_buffer(r->vbo_screen_quad);
1764 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_screen_quad);
1765 glVertexAttribPointer((GLuint)loc, 2, GL_FLOAT, GL_FALSE, 0, 0);
1766 glEnableVertexAttribArray((GLuint)loc);
1767 glVertexAttribPointer(/*attrib location*/ (GLuint)loc,
1768 /*size*/ 2, GL_FLOAT, /*normalize*/ GL_FALSE,
1769 /*stride*/ 0, /*ptr*/ 0);
1770
1771 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1772 }
1773 }
1774 else { // Resolve
1775 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0);
1776 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, r->fbo);
1777 glBlitFramebufferEXT(0, 0, r->width, r->height,
1778 0, 0, r->width, r->height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1779 }
1780 POP_GRAPHICS_GROUP();
1781
1782 // Render outlines after doing AA.
1783
1784 PUSH_GRAPHICS_GROUP("outlines");
1785 // Brush outline
1786 {
1787 gl::use_program(r->outline_program);
1788 GLint loc = glGetAttribLocation(r->outline_program, "a_position");
1789 if ( loc >= 0 ) {
1790 DEBUG_gl_validate_buffer(r->vbo_outline);
1791 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_outline);
1792
1793 glVertexAttribPointer(/*attrib location*/(GLuint)loc,
1794 /*size*/2, GL_FLOAT, /*normalize*/GL_FALSE,
1795 /*stride*/0, /*ptr*/0);
1796 glEnableVertexAttribArray((GLuint)loc);
1797 GLint loc_s = glGetAttribLocation(r->outline_program, "a_sizes");
1798 if ( loc_s >= 0 ) {
1799 DEBUG_gl_validate_buffer(r->vbo_outline_sizes);
1800 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_outline_sizes);
1801 glVertexAttribPointer(/*attrib location*/(GLuint)loc_s,
1802 /*size*/2, GL_FLOAT, /*normalize*/GL_FALSE,
1803 /*stride*/0, /*ptr*/0);
1804 glEnableVertexAttribArray((GLuint)loc_s);
1805 }
1806 }
1807 glDrawArrays(GL_TRIANGLE_FAN, 0,4);
1808 }
1809 glDisable(GL_BLEND);
1810
1811 // Exporter rect
1812 if ( r->imm_flags & ImmediateFlag_RECT ) {
1813 // Update data if rect is not degenerate.
1814 // Draw outline.
1815 gl::use_program(r->exporter_program);
1816 GLint loc = glGetAttribLocation(r->exporter_program, "a_position");
1817 if ( loc>=0 && r->vbo_exporter > 0 ) {
1818 DEBUG_gl_validate_buffer(r->vbo_exporter);
1819 gl::vertex_attrib_v2f(r->exporter_program, "a_position", r->vbo_exporter);
1820
1821 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, r->exporter_indices);
1822
1823 glDrawElements(GL_TRIANGLES, r->exporter_indices_count, GL_UNSIGNED_SHORT, 0);
1824 }
1825 }
1826 POP_GRAPHICS_GROUP(); // outlines
1827
1828 gl::use_program(0);
1829 POP_GRAPHICS_GROUP(); // gpu_render
1830 }
1831
1832 void
gpu_render_to_buffer(Milton * milton,u8 * buffer,i32 scale,i32 x,i32 y,i32 w,i32 h,f32 background_alpha)1833 gpu_render_to_buffer(Milton* milton, u8* buffer, i32 scale, i32 x, i32 y, i32 w, i32 h, f32 background_alpha)
1834 {
1835 CanvasView saved_view = *milton->view;
1836 RenderBackend* r = milton->renderer;
1837 CanvasView* view = milton->view;
1838
1839 i32 saved_width = r->width;
1840 i32 saved_height = r->height;
1841 GLuint saved_fbo = r->fbo;
1842
1843 i32 buf_w = w * scale;
1844 i32 buf_h = h * scale;
1845
1846 v2i center = milton->view->screen_size / 2;
1847 v2i pan_delta = v2i{x + (w / 2), y + (h / 2)} - center;
1848
1849 milton_set_zoom_at_point(milton, center);
1850
1851 f32 cos_angle = cosf(milton->view->angle);
1852 f32 sin_angle = sinf(milton->view->angle);
1853
1854 v2f pan_delta_rotated = v2f{pan_delta.x * cos_angle - pan_delta.y * sin_angle, pan_delta.y * cos_angle + pan_delta.x * sin_angle };
1855
1856 milton->view->pan_center =
1857 milton->view->pan_center + v2f_to_v2l(pan_delta_rotated)*milton->view->scale;
1858
1859 milton->view->screen_size = v2i{buf_w, buf_h};
1860 r->width = buf_w;
1861 r->height = buf_h;
1862
1863 milton->view->zoom_center = milton->view->screen_size / 2;
1864 if ( scale > 1 ) {
1865 milton->view->scale = (i32)ceill(((f32)milton->view->scale / (f32)scale));
1866 }
1867
1868 gpu_resize(r, view);
1869 gpu_update_canvas(r, milton->canvas, view);
1870
1871 // TODO: Check for out-of-memory errors.
1872
1873 mlt_assert(buf_w == r->width);
1874 mlt_assert(buf_h == r->height);
1875
1876 glViewport(0, 0, buf_w, buf_h);
1877 glScissor(0, 0, buf_w, buf_h);
1878 gpu_clip_strokes_and_update(&milton->root_arena, r, milton->view, milton->view->scale, milton->canvas->root_layer,
1879 &milton->working_stroke, 0, 0, buf_w, buf_h);
1880
1881 gpu_render_canvas(r, 0, 0, buf_w, buf_h, background_alpha);
1882
1883 // Post processing
1884 if ( !gl::check_flags(GLHelperFlags_TEXTURE_MULTISAMPLE) ) {
1885 gl::use_program(r->postproc_program);
1886 glBindTexture(GL_TEXTURE_2D, r->canvas_texture);
1887
1888 GLint loc = glGetAttribLocation(r->postproc_program, "a_position");
1889 if ( loc >= 0 ) {
1890 DEBUG_gl_validate_buffer(r->vbo_screen_quad);
1891 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_screen_quad);
1892 glVertexAttribPointer((GLuint)loc, 2, GL_FLOAT, GL_FALSE, 0, 0);
1893 glEnableVertexAttribArray((GLuint)loc);
1894
1895 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1896 }
1897 } else {
1898 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0);
1899 glBindFramebufferEXT(GL_READ_FRAMEBUFFER, r->fbo);
1900 glBlitFramebufferEXT(0, 0, buf_w, buf_h,
1901 0, 0, buf_w, buf_h, GL_COLOR_BUFFER_BIT, GL_LINEAR);
1902 glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
1903 }
1904
1905 glEnable(GL_DEPTH_TEST);
1906
1907 // Read onto buffer
1908 glReadPixels(0,0,
1909 buf_w, buf_h,
1910 GL_RGBA,
1911 GL_UNSIGNED_BYTE,
1912 (GLvoid*)buffer);
1913
1914 { // Flip texture
1915 u32* pixels = (u32*)buffer;
1916 for ( i64 j = 0; j < buf_h / 2; ++j ) {
1917 for ( i64 i = 0; i < buf_w; ++i ) {
1918 i64 idx_up = j * buf_w + i;
1919 i64 j_down = buf_h - 1 - j;
1920 i64 idx_down = j_down * buf_w + i;
1921 // Swap
1922 u32 pixel = pixels[idx_down];
1923 pixels[idx_down] = pixels[idx_up];
1924 pixels[idx_up] = pixel;
1925 }
1926 }
1927 }
1928
1929 // Cleanup.
1930
1931 r->fbo = saved_fbo;
1932 *milton->view = saved_view;
1933 r->width = saved_width;
1934 r->height = saved_height;
1935
1936 glBindFramebufferEXT(GL_FRAMEBUFFER, r->fbo);
1937
1938 gpu_resize(r, view);
1939 gpu_update_canvas(r, milton->canvas, view);
1940
1941 // Re-render
1942 gpu_clip_strokes_and_update(&milton->root_arena,
1943 r, milton->view, milton->view->scale, milton->canvas->root_layer,
1944 &milton->working_stroke, 0, 0, r->width,
1945 r->height);
1946 gpu_render(r, 0, 0, r->width, r->height);
1947 }
1948
1949 void
gpu_release_data(RenderBackend * r)1950 gpu_release_data(RenderBackend* r)
1951 {
1952 release(&r->clip_array);
1953 }
1954
1955
1956 void
imm_begin_frame(RenderBackend * r)1957 imm_begin_frame(RenderBackend* r)
1958 {
1959 r->imm_flags = 0;
1960 }
1961
1962 void
imm_rect(RenderBackend * r,float left,float right,float top,float bottom,float line_width)1963 imm_rect(RenderBackend* r, float left, float right, float top, float bottom, float line_width)
1964 {
1965 if ( r->vbo_exporter == 0 ) {
1966 glGenBuffers(1, &r->vbo_exporter);
1967 mlt_assert(r->exporter_indices == 0);
1968 glGenBuffers(1, &r->exporter_indices);
1969 }
1970
1971 float toparr[] = {
1972 // Top quad
1973 left, top - line_width/r->height,
1974 left, top + line_width/r->height,
1975 right, top + line_width/r->height,
1976 right, top - line_width/r->height,
1977
1978 // Bottom quad
1979 left, bottom-line_width/r->height,
1980 left, bottom+line_width/r->height,
1981 right, bottom+line_width/r->height,
1982 right, bottom-line_width/r->height,
1983
1984 // Left
1985 left-line_width/r->width, top,
1986 left-line_width/r->width, bottom,
1987 left+line_width/r->width, bottom,
1988 left+line_width/r->width, top,
1989
1990 // Right
1991 right-line_width/r->width, top,
1992 right-line_width/r->width, bottom,
1993 right+line_width/r->width, bottom,
1994 right+line_width/r->width, top,
1995 };
1996 glBindBuffer(GL_ARRAY_BUFFER, r->vbo_exporter);
1997 DEBUG_gl_mark_buffer(r->vbo_exporter);
1998 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)array_count(toparr)*sizeof(*toparr), toparr, GL_DYNAMIC_DRAW);
1999
2000 u16 indices[] = {
2001 // top
2002 0,1,2,
2003 2,3,0,
2004
2005 // bottom
2006 4,5,6,
2007 6,7,4,
2008
2009 // left
2010 8,9,10,
2011 10,11,8,
2012
2013 // right
2014 12,13,14,
2015 14,15,12,
2016 };
2017 glBindBuffer(GL_ARRAY_BUFFER, r->exporter_indices);
2018 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)array_count(indices)*sizeof(*indices), indices, GL_STATIC_DRAW);
2019
2020 r->exporter_indices_count = array_count(indices);
2021
2022 r->imm_flags |= ImmediateFlag_RECT;
2023 }
2024
2025 void
gpu_reset_stroke(RenderBackend * r,RenderHandle handle)2026 gpu_reset_stroke(RenderBackend* r, RenderHandle handle)
2027 {
2028 RenderElement* re = get_render_element(handle);
2029 if (re) {
2030 re->count = 0;
2031 }
2032 }
2033