diff options
Diffstat (limited to 'src/compute/skc/platforms/cl_12/gl/interop.c')
-rw-r--r-- | src/compute/skc/platforms/cl_12/gl/interop.c | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/src/compute/skc/platforms/cl_12/gl/interop.c b/src/compute/skc/platforms/cl_12/gl/interop.c new file mode 100644 index 0000000000..6697bb7e83 --- /dev/null +++ b/src/compute/skc/platforms/cl_12/gl/interop.c @@ -0,0 +1,629 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can + * be found in the LICENSE file. + * + */ + +// +// +// + +#include <glad/glad.h> +#include <glfw/glfw3.h> + +// +// +// + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <math.h> + +// +// +// + +#include "common/cl/assert_cl.h" +#include "types.h" + +// +// +// + +#include "interop.h" +#include "context.h" +#include "runtime_cl_12.h" + +// +// +// + +#include "svg2skc/transform_stack.h" + +// +// +// + +#if 1 +#define SKC_IMAGE_FORMAT GL_RGBA8 +#else +#define SKC_IMAGE_FORMAT GL_RGBA16F +#endif + +// +// +// + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +// +// +// + +struct skc_interop_fb +{ + cl_context context; + + GLuint fbo; + GLuint rbo; + + cl_mem mem; + + int width; + int height; + + bool is_srgb; + bool is_vsync_on; + bool is_fullscreen; + bool is_iconified; + bool is_resized; + bool is_spinning; + bool is_info; + + skc_float scale; + skc_float2 translate; + float rotate_theta; +}; + +static struct skc_interop_fb fb = + { + .mem = NULL, + + .is_srgb = true, + .is_vsync_on = false, + .is_fullscreen = false, + .is_iconified = false, + .is_resized = true, + .is_spinning = false, + .is_info = false, + + .scale = 1.0f, + .translate = { 0.0f, 0.0f }, + .rotate_theta = 0.0f + }; + +// +// FPS COUNTER FROM HERE: +// +// http://antongerdelan.net/opengl/glcontext2.html +// + +static +void +skc_interop_fps(GLFWwindow * window) +{ + if (fb.is_fullscreen) + return; + + // static fps counters + static double stamp_prev = 0.0; + static int frame_count = 0; + + // locals + double const stamp_curr = glfwGetTime(); + double const elapsed = stamp_curr - stamp_prev; + + if (elapsed >= 0.5) + { + stamp_prev = stamp_curr; + + double const fps = (double)frame_count / elapsed; + + char tmp[64]; + + sprintf_s(tmp,64,"(%d x %d) - VSync %s - sRGB %s - FPS: %.2f", + fb.width,fb.height, + fb.is_vsync_on ? "ON" : "OFF", + fb.is_srgb ? "ENABLED" : "DISABLED", + fps); + + glfwSetWindowTitle(window,tmp); + + frame_count = 0; + } + + frame_count++; +} + +// +// INITIALIZE GLFW/GLAD +// + +static +void +skc_interop_error_callback(int error, char const * description) +{ + fputs(description,stderr); +} + +// +// +// + +static +void +skc_interop_iconify_callback(GLFWwindow * window, int iconified) +{ + fb.is_iconified = iconified; +} + +// +// +// + +static +void +skc_interop_key_callback(GLFWwindow * window, int key, int scancode, int action, int mods) +{ + if (action == GLFW_RELEASE) + return; + + switch (key) + { + case GLFW_KEY_EQUAL: + fb.rotate_theta = 0.0f; + break; + + case GLFW_KEY_I: + fb.is_info = true; + break; + + case GLFW_KEY_R: + fb.is_spinning ^= true; + break; + + case GLFW_KEY_S: + fb.is_srgb ^= true; + if (fb.is_srgb) + glEnable(GL_FRAMEBUFFER_SRGB); + else + glDisable(GL_FRAMEBUFFER_SRGB); + break; + + case GLFW_KEY_V: + fb.is_vsync_on ^= true; + glfwSwapInterval(fb.is_vsync_on ? 1 : 0); + break; + + case GLFW_KEY_W: + glfwSetWindowSize(window,1024,1024); + break; + + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window,GL_TRUE); + break; + } +} + +static +void +skc_interop_window_size_callback(GLFWwindow * window, int width, int height) +{ + fb.width = width; + fb.height = height; + fb.is_resized = true; + +#if 0 + skc_render_kernel_set_clip(0,0,width,height); +#endif +} + +static +void +skc_interop_scale(double const scale_offset) +{ +#define SKC_SCALE_FACTOR 1.05 + + static double scale_exp = 0.0; + + scale_exp += scale_offset; + fb.scale = (float)pow(SKC_SCALE_FACTOR,scale_exp); +} + +static +void +skc_interop_scroll_callback(GLFWwindow * window, double xoffset, double yoffset) +{ + bool const ctrl = + (glfwGetKey(window,GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || + (glfwGetKey(window,GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS); + + if (!ctrl) + return; + + skc_interop_scale(yoffset); +} + +static +void +skc_interop_translate(float const dx, float const dy) +{ + float const dx_scaled = dx / fb.scale; + float const dy_scaled = dy / fb.scale; + + float const cos_theta = cosf(fb.rotate_theta); // replace with cospi if available + float const sin_theta = sinf(fb.rotate_theta); // replace with sinpi if available + + fb.translate.x += dx_scaled*cos_theta + dy_scaled*sin_theta; + fb.translate.y += dy_scaled*cos_theta - dx_scaled*sin_theta; +} + +static +void +skc_interop_cursor_position_callback(GLFWwindow * window, double x, double y) +{ + int const state = glfwGetMouseButton(window,GLFW_MOUSE_BUTTON_LEFT); + + static bool is_mouse_dragging = false; + static float x_prev=0.0, y_prev=0.0; + + float const mx = (float)x; + float const my = (float)y; + + if (state == GLFW_PRESS) + { + if (is_mouse_dragging) + { + const bool ctrl = + (glfwGetKey(window,GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || + (glfwGetKey(window,GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS); + + if (ctrl) + { + float const cx = 0.5f * fb.width; + float const cy = 0.5f * fb.height; + + // find angle between mouse and center + float const vx = x_prev - cx; + float const vy = y_prev - cy; + + float const wx = mx - cx; + float const wy = my - cy; + + float const len = sqrtf((vx*vx + vy*vy) * (wx*wx + wy*wy)); + + if (len > 0.0f) + { + float const dot = vx*wx + vy*wy; + float const da = acosf(dot / len); + + if (vx*wy - vy*wx >= 0.0f) + fb.rotate_theta += da; + else + fb.rotate_theta -= da; + + fb.rotate_theta = fmodf(fb.rotate_theta,(float)(M_PI*2.0)); + } + } + else + { + skc_interop_translate(mx - x_prev, + my - y_prev); + } + } + else + { + is_mouse_dragging = true; + } + + x_prev = mx; + y_prev = my; + } + else + { + is_mouse_dragging = false; + } +} + +// +// +// + +static +void +skc_interop_resize() +{ + fb.is_resized = false; + + // release the image2d + if (fb.mem != NULL) + cl(ReleaseMemObject(fb.mem)); + + // resize rbo + glNamedRenderbufferStorage(fb.rbo, + SKC_IMAGE_FORMAT, + fb.width, + fb.height); + + // attach rbo to fbo + glNamedFramebufferRenderbuffer(fb.fbo, + GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, + fb.rbo); + // + // + // + cl_int cl_err; + + fb.mem = clCreateFromGLRenderbuffer(fb.context, + CL_MEM_WRITE_ONLY, + fb.rbo, + &cl_err); cl_ok(cl_err); + // + // for debugging porpoises! + // + cl_image_format format; + + cl(GetImageInfo(fb.mem, + CL_IMAGE_FORMAT, + sizeof(format), + &format, + NULL)); +} + +// +// +// + +static +void +skc_interop_acquire() +{ + // frame buffer object + glCreateFramebuffers(1,&fb.fbo); + + // render buffer object w/a color buffer + glCreateRenderbuffers(1,&fb.rbo); + + // size rbo + glNamedRenderbufferStorage(fb.rbo, + SKC_IMAGE_FORMAT, + fb.width, + fb.height); + + // attach rbo to fbo + glNamedFramebufferRenderbuffer(fb.fbo, + GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, + fb.rbo); +} + +void +skc_interop_register(skc_context_t context) +{ + fb.context = context->runtime->cl.context; +} + +// +// +// + +void +skc_interop_init(GLFWwindow * * window) +{ + // + // INITIALIZE GLFW/GLAD + // + glfwSetErrorCallback(skc_interop_error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + GLFWmonitor * const primary = glfwGetPrimaryMonitor(); + GLFWvidmode const * const mode = glfwGetVideoMode(primary); + + if (fb.is_fullscreen) + { + fb.width = mode->width; + fb.height = mode->height; + } + else + { + fb.width = 1600; + fb.height = 1024; + } + + glfwWindowHint(GLFW_ALPHA_BITS, 0); + glfwWindowHint(GLFW_DEPTH_BITS, 0); + glfwWindowHint(GLFW_STENCIL_BITS, 0); + + glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); + + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + *window = glfwCreateWindow(fb.width,fb.height, + "Skia Compute", + fb.is_fullscreen ? primary : NULL, + NULL); + + if (*window == NULL) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(*window); + + // set up GLAD + gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); + + // ignore vsync for now + glfwSwapInterval(fb.is_vsync_on ? 1 : 0); + + // only copy r/g/b + glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_FALSE); + + // enable SRGB, disable scissor + glEnable(GL_FRAMEBUFFER_SRGB); + glDisable(GL_SCISSOR_TEST); + + // + // SET USER POINTER AND CALLBACKS + // + glfwSetKeyCallback (*window,skc_interop_key_callback); + glfwSetFramebufferSizeCallback(*window,skc_interop_window_size_callback); + glfwSetScrollCallback (*window,skc_interop_scroll_callback); + glfwSetCursorPosCallback (*window,skc_interop_cursor_position_callback); + glfwSetWindowIconifyCallback (*window,skc_interop_iconify_callback); + + // + // + // + fprintf(stderr, + "GL_VENDOR : %s\n" + "GL_RENDERER : %s\n", + glGetString(GL_VENDOR), + glGetString(GL_RENDERER)); + + // + // acquire an FBO/RBO + // + skc_interop_acquire(); +} + +// +// +// + +#define SKC_ROTATE_STEP ((float)(M_PI / 180.0)) + +static +void +skc_interop_transform(struct skc_transform_stack * ts) +{ + // OpenGL'ism + skc_transform_stack_push_affine(ts, + 1.0f, 0.0f,0.0f, + 0.0f,-1.0f,(float)fb.height); + // multiply + skc_transform_stack_concat(ts); + + // spinner... + if (fb.is_spinning) + fb.rotate_theta = fmodf(fb.rotate_theta + SKC_ROTATE_STEP,(float)(M_PI*2.0)); + + // always rotate and scale around surface center point + skc_transform_stack_push_rotate_scale_xy(ts, + fb.rotate_theta, + fb.scale,fb.scale, + 0.5f*fb.width,0.5f*fb.height); + skc_transform_stack_concat(ts); + + // where did the mouse take us? + skc_transform_stack_push_translate(ts, + fb.translate.x,fb.translate.y); + skc_transform_stack_concat(ts); +} + + +void +skc_interop_poll(GLFWwindow * window, + struct skc_transform_stack * ts) +{ + // wait until uniconified + while (fb.is_iconified) + { + glfwWaitEvents(); + continue; + } + + // what's happended? + glfwPollEvents(); + + // resize? + if (fb.is_resized) + skc_interop_resize(); + + // monitor fps + skc_interop_fps(window); + + skc_interop_transform(ts); +} + +// +// +// + +void +skc_interop_blit(GLFWwindow * window) +{ + // blit skc rbo + glBlitNamedFramebuffer(fb.fbo,0, + 0,0,fb.width,fb.height, + 0,0,fb.width,fb.height, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + +#if 0 + // + // FIXME -- this clear does nothing! + // + // As a hack we're clearing the interop'd RBO with a + // clEnqueueFillImage(). + // + float const rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + // GLenum const attachments[] = { GL_COLOR_ATTACHMENT0 }; + // glInvalidateNamedFramebufferData(fb.fbo,1,attachments); + glClearNamedFramebufferfv(fb.fbo,GL_COLOR,0,rgba); +#endif + + // swap buffers + glfwSwapBuffers(window); +} + +// +// +// + +void * +skc_interop_get_fb(GLFWwindow * window) +{ + glFlush(); + + return fb.mem; +} + +// +// +// + +void +skc_interop_get_dim(uint32_t dim[2]) +{ + dim[0] = fb.width; + dim[1] = fb.height; +} + +// +// +// + + |