Home | History | Annotate | Download | only in interop
      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
    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
    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
    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
    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
    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
    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
    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
    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
    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 *
    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
    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
    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
    491 skc_interop_get_wgl_context()
    492 {
    493   return (cl_context_properties)wglGetCurrentContext();
    494 }
    495 
    496 cl_context_properties
    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
    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
    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
    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
    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
    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
    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
    739 skc_interop_should_exit(struct skc_interop * interop)
    740 {
    741   return glfwWindowShouldClose(interop->window);
    742 }
    743 
    744 //
    745 //
    746 //
    747 
    748 void
    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