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