1 /*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can
5 * be found in the LICENSE file.
6 *
7 */
8
9 //
10 //
11 //
12
13 #include <glad/glad.h>
14 #include <glfw/glfw3.h>
15
16 //
17 //
18 //
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdbool.h>
23 #include <math.h>
24
25 //
26 //
27 //
28
29 #include "common/cl/assert_cl.h"
30
31 //
32 //
33 //
34
35 #include "interop.h"
36
37 //
38 //
39 //
40
41 #include "skc_cl.h"
42 #include "runtime_cl_12.h"
43
44 //
45 //
46 //
47
48 #include "ts/transform_stack.h"
49
50 //
51 //
52 //
53
54 #if 1
55 #define SKC_IMAGE_FORMAT GL_RGBA8
56 #else
57 #define SKC_IMAGE_FORMAT GL_RGBA16F
58 #endif
59
60 //
61 //
62 //
63
64 #ifndef M_PI
65 #define M_PI 3.14159265358979323846
66 #endif
67
68 //
69 //
70 //
71
72 struct skc_interop
73 {
74 GLFWwindow * window;
75
76 cl_context context_cl;
77
78 GLuint fbo;
79 GLuint rbo;
80
81 struct skc_framebuffer_cl fb;
82
83 int width;
84 int height;
85
86 bool is_msecs;
87 bool is_srgb;
88 bool is_vsync_on;
89 bool is_fullscreen;
90 bool is_iconified;
91 bool is_resized;
92 bool is_spinning;
93 bool is_transform;
94
95 skc_float scale;
96 skc_float2 translate;
97 float rotate_theta;
98
99 int key;
100 };
101
102 //
103 // INITIALIZE GLFW/GLAD
104 //
105
106 static
107 void
skc_interop_error_callback(int error,char const * description)108 skc_interop_error_callback(int error, char const * description)
109 {
110 fputs(description,stderr);
111 }
112
113 //
114 //
115 //
116
117 static
118 void
skc_interop_iconify_callback(GLFWwindow * window,int iconified)119 skc_interop_iconify_callback(GLFWwindow * window, int iconified)
120 {
121 struct skc_interop * interop = glfwGetWindowUserPointer(window);
122
123 interop->is_iconified = iconified;
124 }
125
126 //
127 //
128 //
129
130 static
131 void
skc_interop_key_callback(GLFWwindow * window,int key,int scancode,int action,int mods)132 skc_interop_key_callback(GLFWwindow * window, int key, int scancode, int action, int mods)
133 {
134 struct skc_interop * interop = glfwGetWindowUserPointer(window);
135
136 if (action == GLFW_RELEASE)
137 return;
138
139 switch (key)
140 {
141 case GLFW_KEY_EQUAL:
142 interop->rotate_theta = 0.0f;
143 interop->is_transform = true;
144 break;
145
146 case GLFW_KEY_M:
147 interop->is_msecs ^= true;
148 break;
149
150 case GLFW_KEY_R:
151 interop->is_spinning ^= true;
152 break;
153
154 case GLFW_KEY_S:
155 interop->is_srgb ^= true;
156 if (interop->is_srgb)
157 glEnable(GL_FRAMEBUFFER_SRGB);
158 else
159 glDisable(GL_FRAMEBUFFER_SRGB);
160 break;
161
162 case GLFW_KEY_V:
163 interop->is_vsync_on ^= true;
164 glfwSwapInterval(interop->is_vsync_on ? 1 : 0);
165 break;
166
167 case GLFW_KEY_W:
168 glfwSetWindowSize(window,1024,1024);
169 break;
170
171 case GLFW_KEY_ESCAPE:
172 glfwSetWindowShouldClose(window,GL_TRUE);
173 break;
174
175 default:
176 interop->key = key;
177 }
178 }
179
180 static
181 void
skc_interop_window_size_callback(GLFWwindow * window,int width,int height)182 skc_interop_window_size_callback(GLFWwindow * window, int width, int height)
183 {
184 struct skc_interop * interop = glfwGetWindowUserPointer(window);
185
186 interop->width = width;
187 interop->height = height;
188 interop->is_resized = true;
189 interop->is_transform = true;
190
191 #if 0
192 skc_render_kernel_set_clip(0,0,width,height);
193 #endif
194 }
195
196 static
197 void
skc_interop_scale(struct skc_interop * interop,double const scale_offset)198 skc_interop_scale(struct skc_interop * interop, double const scale_offset)
199 {
200 #define SKC_SCALE_FACTOR 1.05
201
202 static double scale_exp = 0.0;
203
204 scale_exp += scale_offset;
205
206 interop->scale = (float)pow(SKC_SCALE_FACTOR,scale_exp);
207 }
208
209 static
210 void
skc_interop_scroll_callback(GLFWwindow * window,double xoffset,double yoffset)211 skc_interop_scroll_callback(GLFWwindow * window, double xoffset, double yoffset)
212 {
213 bool const ctrl =
214 (glfwGetKey(window,GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) ||
215 (glfwGetKey(window,GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS);
216
217 if (!ctrl)
218 return;
219
220 struct skc_interop * interop = glfwGetWindowUserPointer(window);
221
222 skc_interop_scale(interop,yoffset);
223
224 interop->is_transform = true;
225 }
226
227 static
228 void
skc_interop_translate(struct skc_interop * interop,float const dx,float const dy)229 skc_interop_translate(struct skc_interop * interop, float const dx, float const dy)
230 {
231 float const dx_scaled = dx / interop->scale;
232 float const dy_scaled = dy / interop->scale;
233
234 float const cos_theta = cosf(interop->rotate_theta); // replace with cospi if available
235 float const sin_theta = sinf(interop->rotate_theta); // replace with sinpi if available
236
237 interop->translate.x += dx_scaled*cos_theta + dy_scaled*sin_theta;
238 interop->translate.y += dy_scaled*cos_theta - dx_scaled*sin_theta;
239 }
240
241 static
242 void
skc_interop_cursor_position_callback(GLFWwindow * window,double x,double y)243 skc_interop_cursor_position_callback(GLFWwindow * window, double x, double y)
244 {
245
246 int const state = glfwGetMouseButton(window,GLFW_MOUSE_BUTTON_LEFT);
247
248 static bool is_mouse_dragging = false;
249 static float x_prev=0.0, y_prev=0.0;
250
251 float const mx = (float)x;
252 float const my = (float)y;
253
254 if (state == GLFW_PRESS)
255 {
256 struct skc_interop * interop = glfwGetWindowUserPointer(window);
257
258 if (is_mouse_dragging)
259 {
260 const bool ctrl =
261 (glfwGetKey(window,GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) ||
262 (glfwGetKey(window,GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS);
263
264 if (ctrl)
265 {
266 float const cx = 0.5f * interop->width;
267 float const cy = 0.5f * interop->height;
268
269 // find angle between mouse and center
270 float const vx = x_prev - cx;
271 float const vy = y_prev - cy;
272
273 float const wx = mx - cx;
274 float const wy = my - cy;
275
276 float const len = sqrtf((vx*vx + vy*vy) * (wx*wx + wy*wy));
277
278 if (len > 0.0f)
279 {
280 float const dot = vx*wx + vy*wy;
281 float const da = acosf(dot / len);
282
283 if (vx*wy - vy*wx >= 0.0f)
284 interop->rotate_theta += da;
285 else
286 interop->rotate_theta -= da;
287
288 interop->rotate_theta = fmodf(interop->rotate_theta,(float)(M_PI*2.0));
289 }
290 }
291 else
292 {
293 skc_interop_translate(interop,
294 mx - x_prev,
295 my - y_prev);
296 }
297
298 interop->is_transform = true;
299 }
300 else
301 {
302 is_mouse_dragging = true;
303 }
304
305 x_prev = mx;
306 y_prev = my;
307 }
308 else
309 {
310 is_mouse_dragging = false;
311 }
312 }
313
314 //
315 //
316 //
317
318 static
319 void
skc_interop_acquire(struct skc_interop * interop)320 skc_interop_acquire(struct skc_interop * interop)
321 {
322 // frame buffer object
323 glCreateFramebuffers(1,&interop->fbo);
324
325 // render buffer object w/a color buffer
326 glCreateRenderbuffers(1,&interop->rbo);
327
328 // size rbo
329 glNamedRenderbufferStorage(interop->rbo,
330 SKC_IMAGE_FORMAT,
331 interop->width,
332 interop->height);
333
334 // attach rbo to fbo
335 glNamedFramebufferRenderbuffer(interop->fbo,
336 GL_COLOR_ATTACHMENT0,
337 GL_RENDERBUFFER,
338 interop->rbo);
339 }
340
341 //
342 //
343 //
344
345 struct skc_interop *
skc_interop_create()346 skc_interop_create()
347 {
348 struct skc_interop * interop = malloc(sizeof(*interop));
349
350 *interop = (struct skc_interop)
351 {
352 .fb = { .type = SKC_FRAMEBUFFER_CL_GL_RENDERBUFFER,
353 .mem = NULL,
354 .interop = interop,
355 .post_render = skc_interop_blit },
356
357 .is_msecs = false,
358 .is_srgb = true,
359 .is_vsync_on = false,
360 .is_fullscreen = false,
361 .is_iconified = false,
362 .is_resized = true,
363 .is_spinning = false,
364 .is_transform = true,
365
366 .scale = 1.0f,
367 .translate = { 0.0f, 0.0f },
368 .rotate_theta = 0.0f,
369
370 .key = 0
371 };
372
373 //
374 // INITIALIZE GLFW/GLAD
375 //
376 glfwSetErrorCallback(skc_interop_error_callback);
377
378 if (!glfwInit())
379 exit(EXIT_FAILURE);
380
381 GLFWmonitor * const primary = glfwGetPrimaryMonitor();
382 GLFWvidmode const * const mode = glfwGetVideoMode(primary);
383
384 if (interop->is_fullscreen)
385 {
386 interop->width = mode->width;
387 interop->height = mode->height;
388 }
389 else
390 {
391 interop->width = 1600;
392 interop->height = 1600;
393 }
394
395 glfwWindowHint(GLFW_ALPHA_BITS, 0);
396 glfwWindowHint(GLFW_DEPTH_BITS, 0);
397 glfwWindowHint(GLFW_STENCIL_BITS, 0);
398
399 glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE);
400
401 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
402 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
403
404 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
405
406 interop->window = glfwCreateWindow(interop->width,
407 interop->height,
408 "Skia Compute",
409 interop->is_fullscreen ? primary : NULL,
410 NULL);
411
412 if (interop->window == NULL)
413 {
414 glfwTerminate();
415 exit(EXIT_FAILURE);
416 }
417
418 // save back pointer
419 glfwSetWindowUserPointer(interop->window,interop);
420
421 glfwMakeContextCurrent(interop->window);
422
423 // set up GLAD
424 gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
425
426 // ignore vsync for now
427 glfwSwapInterval(interop->is_vsync_on ? 1 : 0);
428
429 // only copy r/g/b
430 glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_FALSE);
431
432 // enable SRGB, disable scissor
433 glEnable(GL_FRAMEBUFFER_SRGB);
434 glDisable(GL_SCISSOR_TEST);
435
436 //
437 // SET USER POINTER AND CALLBACKS
438 //
439 glfwSetKeyCallback (interop->window,skc_interop_key_callback);
440 glfwSetFramebufferSizeCallback(interop->window,skc_interop_window_size_callback);
441 glfwSetScrollCallback (interop->window,skc_interop_scroll_callback);
442 glfwSetCursorPosCallback (interop->window,skc_interop_cursor_position_callback);
443 glfwSetWindowIconifyCallback (interop->window,skc_interop_iconify_callback);
444
445 //
446 //
447 //
448 fprintf(stderr,
449 "GL_VENDOR : %s\n"
450 "GL_RENDERER : %s\n",
451 glGetString(GL_VENDOR),
452 glGetString(GL_RENDERER));
453
454 //
455 // acquire an FBO/RBO
456 //
457 skc_interop_acquire(interop);
458
459 return interop;
460 }
461
462 //
463 //
464 //
465
466 void
skc_interop_destroy(struct skc_interop * interop)467 skc_interop_destroy(struct skc_interop * interop)
468 {
469 glfwDestroyWindow(interop->window);
470 glfwTerminate();
471
472 free(interop);
473 }
474
475 //
476 //
477 //
478
479 void
skc_interop_set_cl_context(struct skc_interop * interop,cl_context context_cl)480 skc_interop_set_cl_context(struct skc_interop * interop,
481 cl_context context_cl)
482 {
483 interop->context_cl = context_cl;
484 }
485
486 //
487 //
488 //
489
490 cl_context_properties
skc_interop_get_wgl_context()491 skc_interop_get_wgl_context()
492 {
493 return (cl_context_properties)wglGetCurrentContext();
494 }
495
496 cl_context_properties
skc_interop_get_wgl_dc()497 skc_interop_get_wgl_dc()
498 {
499 return (cl_context_properties)wglGetCurrentDC();
500 }
501
502 //
503 //
504 //
505
506 #define SKC_ROTATE_STEP ((float)(M_PI / 180.0))
507
508 void
skc_interop_transform(struct skc_interop * interop,struct ts_transform_stack * ts)509 skc_interop_transform(struct skc_interop * interop,
510 struct ts_transform_stack * ts)
511 {
512 #if 1
513 // move the origin from the lower left to the top left
514 ts_transform_stack_push_affine(ts,
515 1.0f, 0.0f,0.0f,
516 0.0f,-1.0f,(float)interop->height);
517 // multiply
518 ts_transform_stack_concat(ts);
519 #endif
520
521 #if 0
522 ts_transform_stack_push_matrix(ts,
523 0.87004626f, -0.35519487f, 72.14745f,
524 0.0f, 0.2600208f, 86.16314f,
525 0.0f, -0.0029599573f, 1.0f);
526 ts_transform_stack_concat(ts);
527 #endif
528
529 // spinner...
530 if (interop->is_spinning)
531 interop->rotate_theta = fmodf(interop->rotate_theta + SKC_ROTATE_STEP,(float)(M_PI*2.0));
532
533 // always rotate and scale around surface center point
534 ts_transform_stack_push_rotate_scale_xy(ts,
535 interop->rotate_theta,
536 interop->scale,
537 interop->scale,
538 0.5f*interop->width,
539 0.5f*interop->height);
540 ts_transform_stack_concat(ts);
541
542 // where did the mouse take us?
543 ts_transform_stack_push_translate(ts,
544 interop->translate.x,
545 interop->translate.y);
546 ts_transform_stack_concat(ts);
547 }
548
549 //
550 //
551 //
552
553 static
554 void
skc_interop_resize(struct skc_interop * interop)555 skc_interop_resize(struct skc_interop * interop)
556 {
557 interop->is_resized = false;
558
559 // release the image2d
560 if (interop->fb.mem != NULL)
561 cl(ReleaseMemObject(interop->fb.mem));
562
563 // resize rbo
564 glNamedRenderbufferStorage(interop->rbo,
565 SKC_IMAGE_FORMAT,
566 interop->width,
567 interop->height);
568
569 // attach rbo to fbo
570 glNamedFramebufferRenderbuffer(interop->fbo,
571 GL_COLOR_ATTACHMENT0,
572 GL_RENDERBUFFER,
573 interop->rbo);
574 //
575 //
576 //
577 cl_int cl_err;
578
579 interop->fb.mem = clCreateFromGLRenderbuffer(interop->context_cl,
580 CL_MEM_WRITE_ONLY,
581 interop->rbo,
582 &cl_err); cl_ok(cl_err);
583 //
584 // for debugging porpoises!
585 //
586 #if 0
587 cl_image_format format;
588
589 cl(GetImageInfo(interop->fb.mem,
590 CL_IMAGE_FORMAT,
591 sizeof(format),
592 &format,
593 NULL));
594 #endif
595 }
596
597 //
598 // FPS COUNTER FROM HERE:
599 //
600 // http://antongerdelan.net/opengl/glcontext2.html
601 //
602
603 static
604 void
skc_interop_fps(struct skc_interop * interop)605 skc_interop_fps(struct skc_interop * interop)
606 {
607 if (interop->is_fullscreen)
608 return;
609
610 // static fps counters
611 static double stamp_prev = 0.0;
612 static int frame_count = 0;
613
614 // locals
615 double const stamp_curr = glfwGetTime();
616 double const elapsed = stamp_curr - stamp_prev;
617
618 if (elapsed >= 0.5)
619 {
620 stamp_prev = stamp_curr;
621
622 char tmp[64];
623
624 if (interop->is_msecs)
625 {
626 double const msecs = min(elapsed * 1000 / frame_count,9999.9);
627
628 sprintf_s(tmp,64,"%5.1f MSECS - (%d x %d) - VSync %s - sRGB %s",
629 msecs,
630 interop->width,interop->height,
631 interop->is_vsync_on ? "ON" : "OFF",
632 interop->is_srgb ? "ENABLED" : "DISABLED");
633 }
634 else
635 {
636 double const fps = min((double)frame_count / elapsed,9999.9);
637
638 sprintf_s(tmp,64,"%5.1f FPS - (%d x %d) - VSync %s - sRGB %s",
639 fps,
640 interop->width,interop->height,
641 interop->is_vsync_on ? "ON" : "OFF",
642 interop->is_srgb ? "ENABLED" : "DISABLED");
643 }
644
645 glfwSetWindowTitle(interop->window,tmp);
646
647 frame_count = 0;
648 }
649
650 frame_count++;
651 }
652
653 //
654 //
655 //
656
657 bool
skc_interop_poll(struct skc_interop * interop,int * key)658 skc_interop_poll(struct skc_interop * interop, int * key)
659 {
660 // wait until uniconified
661 while (interop->is_iconified)
662 {
663 glfwWaitEvents();
664 continue;
665 }
666
667 // what's happended?
668 glfwPollEvents();
669
670 // resize?
671 if (interop->is_resized)
672 skc_interop_resize(interop);
673
674 // monitor fps
675 skc_interop_fps(interop);
676
677 if (key != NULL)
678 {
679 *key = interop->key;
680 interop->key = 0;
681 }
682
683 bool const is_transform = interop->is_transform || interop->is_spinning;
684
685 interop->is_transform = false;
686
687 return is_transform;
688 }
689
690 //
691 //
692 //
693
694 void
skc_interop_blit(struct skc_interop * interop)695 skc_interop_blit(struct skc_interop * interop)
696 {
697 // blit skc rbo
698 glBlitNamedFramebuffer(interop->fbo,0,
699 0,0,interop->width,interop->height,
700 0,0,interop->width,interop->height,
701 GL_COLOR_BUFFER_BIT,
702 GL_NEAREST);
703
704 // swap buffers
705 glfwSwapBuffers(interop->window);
706
707 #if 0
708 //
709 // FIXME -- this clear does nothing!
710 //
711 // As a hack we're clearing the interop'd RBO with a
712 // clEnqueueFillImage().
713 //
714 GLenum const attachments[] = { GL_COLOR_ATTACHMENT0 };
715 glInvalidateNamedFramebufferData(interop->fbo,1,attachments);
716 float const rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
717 glClearNamedFramebufferfv(interop->fbo,GL_COLOR,0,rgba);
718 #endif
719 }
720
721 //
722 //
723 //
724
725 skc_framebuffer_t
skc_interop_get_framebuffer(struct skc_interop * interop)726 skc_interop_get_framebuffer(struct skc_interop * interop)
727 {
728 // glFlush();
729 glFinish();
730
731 return &interop->fb;
732 }
733
734 //
735 //
736 //
737
738 bool
skc_interop_should_exit(struct skc_interop * interop)739 skc_interop_should_exit(struct skc_interop * interop)
740 {
741 return glfwWindowShouldClose(interop->window);
742 }
743
744 //
745 //
746 //
747
748 void
skc_interop_get_size(struct skc_interop * interop,uint32_t * width,uint32_t * height)749 skc_interop_get_size(struct skc_interop * interop,
750 uint32_t * width,
751 uint32_t * height)
752 {
753 *width = interop->width;
754 *height = interop->height;
755 }
756
757 //
758 //
759 //
760