From a839879baa4cb7ba375fc5f65aec58da51a93821 Mon Sep 17 00:00:00 2001 From: Bastien Date: Tue, 18 Sep 2018 20:00:49 +0200 Subject: [PATCH] First development release - WIP --- CMakeLists.txt | 30 ++++ README.md | 67 +++++++- include/lvrc.h | 60 +++++++ src/context.c | 146 ++++++++++++++++ src/context.h | 26 +++ src/frame.c | 243 ++++++++++++++++++++++++++ src/frame.h | 5 + src/instance.c | 29 ++++ src/instance.h | 25 +++ src/lvrc.c | 1 + src/lvrc_internal.h | 9 + src/swapChain.c | 411 ++++++++++++++++++++++++++++++++++++++++++++ src/swapChain.h | 29 ++++ 13 files changed, 1080 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 include/lvrc.h create mode 100644 src/context.c create mode 100644 src/context.h create mode 100644 src/frame.c create mode 100644 src/frame.h create mode 100644 src/instance.c create mode 100644 src/instance.h create mode 100644 src/lvrc.c create mode 100644 src/lvrc_internal.h create mode 100644 src/swapChain.c create mode 100644 src/swapChain.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..21a305e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.1) + +project(Linux-VR-Compositor C) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") + +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -ansi -pedantic -Werror=implicit-function-declaration -Werror=incompatible-pointer-types") + +option(ENABLE_XCB ON) + +find_package(OpenGL REQUIRED) +find_package(openhmd REQUIRED) + +add_library(lvrc include/lvrc.h src/lvrc.c src/lvrc_internal.h src/instance.c src/instance.h src/swapChain.c src/swapChain.h src/frame.c src/frame.h) + +target_link_libraries(lvrc PUBLIC openhmd ${OPENGL_LIBRARIES} EGL) +target_link_libraries(lvrc PUBLIC "drm" "gbm") +target_include_directories(lvrc PRIVATE "/usr/include/drm/") +target_include_directories(lvrc PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") + +if (ENABLE_XCB) + target_link_libraries(lvrc PUBLIC "xcb" "xcb-randr") + target_compile_definitions(lvrc PRIVATE ENABLE_XCB=1) +endif(ENABLE_XCB) + +export(TARGETS lvrc FILE lvrc-config.cmake) diff --git a/README.md b/README.md index 37920eb..2c0279c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,67 @@ -# Linux-VR-Compositor-Dev +# Linux-VR-Compositor +```C +#define GL_GLEXT_PROTOTYPES +#include // must include OpenGL before including the Linux VR Compositor header + +#include + +void main() +{ + // + // Initialize OpenHMD device as usual + ohmd_context * ctx = ohmd_ctx_create(); + ohmd_device * hmd = ...; + + // + // Create instance, the only public object exposed by the API + struct lvrcInstance * compositor = lvrcCreateInstance(hmd); + + // + // Initialize Linux VR Compositor SwapChain + bool bSwapChainCreated = lvrcInitSwapChain(compositor); + + // + // Initialize OpenGL using EGL + // This is mostly as usual, the only change is that you must use the provided NativeDisplay/NativeWindow + EGLDisplay display = eglGetDisplay(lvrcSwapChainGetNativeDisplay(compositor)); + EGLContext context = eglCreateContext(display, ...); + EGLSurface surface = eglCreateWindowSurface(display, lvrcSwapChainGetNativeWindow(compositor), ...); + + eglMakeCurrent(display, surface, surface, >context); + + // + // The compositor needs to create some resources that will be used during frame composition + // NOTE : the EGL context MUST be bound when calling this function + bool bCompositorInit = lvrcInitFrameResources(compositor); + + // + // Main Loop + while (true) + { + ohmd_ctx_update(ctx); // Update OpenHMD as usual + + lvrcBeginFrame(compositor); + + ... // Render to left & right textures as usual + + lvrcSubmitFrameLeft(compositor, left_texture); + + lvrcSubmitFrameRight(compositor, right_texture); + + eglSwapBuffers(display, surface); // Swap buffers as usual + + lvrcEndFrame(compositor); + } + + lvrcReleaseFrameResources(compositor); + + // You should destroy your EGL context here + + lvrcReleaseSwapChain(compositor); + + lvrcDestroyInstance(compositor); + + ohmd_ctx_destroy(ctx); +} +``` diff --git a/include/lvrc.h b/include/lvrc.h new file mode 100644 index 0000000..60bcb84 --- /dev/null +++ b/include/lvrc.h @@ -0,0 +1,60 @@ +#ifndef LINUX_VR_COMPOSITOR_H +#define LINUX_VR_COMPOSITOR_H + +#include + +#ifndef __GBM__ +#define __GBM__ +#endif // __GBM__ + +#include + +#include + +#define LVRC_API __attribute__ ((visibility ("default"))) + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Instance +// + +struct lvrcInstance; + +LVRC_API struct lvrcInstance * lvrcCreateInstance(ohmd_device * hmd); +LVRC_API void lvrcDestroyInstance(struct lvrcInstance * state); + +// +// Swap Chain +// + +LVRC_API bool lvrcInitSwapChain(struct lvrcInstance * state); +LVRC_API bool lvrcReleaseSwapChain(struct lvrcInstance * state); + +LVRC_API EGLNativeDisplayType lvrcSwapChainGetNativeDisplay(struct lvrcInstance * state); +LVRC_API EGLNativeWindowType lvrcSwapChainGetNativeWindow(struct lvrcInstance * state); + +LVRC_API unsigned int lvrcSwapChainGetWidth(struct lvrcInstance * state); +LVRC_API unsigned int lvrcSwapChainGetHeight(struct lvrcInstance * state); + +// +// Frame +// + +LVRC_API bool lvrcInitFrameResources(struct lvrcInstance * state); +LVRC_API bool lvrcReleaseFrameResources(struct lvrcInstance * state); + +LVRC_API bool lvrcBeginFrame(struct lvrcInstance * state); + +LVRC_API bool lvrcSubmitFrameLeft(struct lvrcInstance * state, GLuint texture); +LVRC_API bool lvrcSubmitFrameRight(struct lvrcInstance * state, GLuint texture); + +LVRC_API bool lvrcEndFrame(struct lvrcInstance * state); + +#ifdef __cplusplus +} +#endif + +#endif // LINUX_VR_COMPOSITOR_H diff --git a/src/context.c b/src/context.c new file mode 100644 index 0000000..781dca3 --- /dev/null +++ b/src/context.c @@ -0,0 +1,146 @@ +#include "context.h" + +#include +#include + +#include +#include +#include + +#include +#include + +static const EGLint attribs_GL[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + //EGL_ALPHA_SIZE, 8, + //EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE +}; + +static const EGLint attribs_GLES[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + //EGL_ALPHA_SIZE, 8, + //EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE +}; + +static const EGLint context_attributes[] = +{ + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE +}; + +lvrc_contextGL * CreateContext(ohmd_device * hmd, EGLenum API) +{ + const EGLint * attribs = NULL; + + if (API == EGL_OPENGL_API) + { + attribs = attribs_GL; + } + else + { + attribs = attribs_GLES; + } + + lvrc_contextGL * context = malloc(sizeof(lvrc_contextGL)); + + if (!context) + { + return NULL; + } + + memset(context, sizeof(lvrc_contextGL), 0); + + bool bInitSuccess = lvrcInitSwapChain(NULL); + + // + // get an EGL display connection + context->display = eglGetDisplay(context->swapChain->device); + + if (EGL_NO_DISPLAY == context->display) + { + fprintf(stderr, "eglGetDisplay() failed\n"); + return NULL; + } + + // + // initialize the EGL display connection + EGLint major, minor; + + if (!eglInitialize(context->display, &major, &minor)) + { + fprintf(stderr, "eglInitialize() failed\n"); + return NULL; + } + + // + // bind OpenGL API + eglBindAPI(API); + + // + // get an appropriate EGL frame buffer configuration + EGLint num_config; + EGLConfig configs[128]; + + if (!eglChooseConfig(context->display, attribs, configs, 128, &num_config)) + { + fprintf(stderr, "eglChooseConfig() failed\n"); + exit(-1); + } + + EGLConfig config = configs[0]; // FIXME + + // + // create context + context->context = eglCreateContext(context->display, config, EGL_NO_CONTEXT, context_attributes); + + if (EGL_NO_CONTEXT == context->context) + { + fprintf(stderr, "failed to create context\n"); + return NULL; + } + + // + // Create Surface + context->surface = eglCreateWindowSurface(context->display, config, context->swapChain->surface, NULL); + if (EGL_NO_SURFACE == context->surface) + { + fprintf(stderr, "eglCreateWindowSurface() failed\n"); + return NULL; + } + + // + // Set current context + if (!eglMakeCurrent(context->display, context->surface, context->surface, context->context)) + { + fprintf(stderr, "failed to make context current\n"); + return NULL; + } + + return context; +} + +void DestroyContext(lvrc_contextGL * context) +{ + // FIXME : release resources + + free(context); +} + +void ContextSwapBuffers(lvrc_contextGL * ctx) +{ + eglSwapBuffers(((lvrc_contextGL *)ctx)->display, ((lvrc_contextGL *)ctx)->surface); + + SwapBuffers(ctx->swapChain); +} diff --git a/src/context.h b/src/context.h new file mode 100644 index 0000000..a2fa8c4 --- /dev/null +++ b/src/context.h @@ -0,0 +1,26 @@ +#ifndef LINUX_VR_COMPOSITOR_CONTEXT_H +#define LINUX_VR_COMPOSITOR_CONTEXT_H + +#include + +#include "swapChain.h" + +#include + +typedef struct +{ + SwapChain * swapChain; + + // EGL + EGLDisplay display; + EGLSurface surface; + EGLContext context; + +} lvrc_contextGL; + +lvrc_contextGL * CreateContext(ohmd_device * octx, EGLenum API); +void DestroyContext(lvrc_contextGL * context); + +void ContextSwapBuffers(lvrc_contextGL * ctx); + +#endif // LINUX_VR_COMPOSITOR_CONTEXT_H diff --git a/src/frame.c b/src/frame.c new file mode 100644 index 0000000..6a60296 --- /dev/null +++ b/src/frame.c @@ -0,0 +1,243 @@ +#include "frame.h" + +#include "lvrc_internal.h" +#include "instance.h" + +#include +#include + +static const GLfloat textureVertices[] = +{ + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f +}; + +static GLuint shader = 0; + +static float left_lens_center[2]; +static float right_lens_center[2]; + +// ------------------------------------------------------------------- +// from OpenHMD + +static void compile_shader_src(GLuint shader, const char* src) +{ + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + + GLint status; + GLint length; + char log[4096] = {0}; + + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + glGetShaderInfoLog(shader, 4096, &length, log); + if(status == GL_FALSE){ + printf("compile failed %s\n", log); + } +} + +static GLuint compile_shader(const char* vertex, const char* fragment) +{ + // Create the handels + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + GLuint programShader = glCreateProgram(); + + // Attach the shaders to a program handel. + glAttachShader(programShader, vertexShader); + glAttachShader(programShader, fragmentShader); + + // Load and compile the Vertex Shader + compile_shader_src(vertexShader, vertex); + + // Load and compile the Fragment Shader + compile_shader_src(fragmentShader, fragment); + + // The shader objects are not needed any more, + // the programShader is the complete shader to be used. + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + glLinkProgram(programShader); + + GLint status; + GLint length; + char log[4096] = {0}; + + glGetProgramiv(programShader, GL_LINK_STATUS, &status); + glGetProgramInfoLog(programShader, 4096, &length, log); + if(status == GL_FALSE){ + printf("link failed %s\n", log); + } + + return programShader; +} + +// ------------------------------------------------------------------- + +bool lvrcInitFrameResources(struct lvrcInstance * instance) +{ + Instance * internalState = (Instance*)instance; + + if (!internalState->swapChain) + { + return false; + } + + ohmd_device * hmd = internalState->device; + + float viewport_scale[2]; + float distortion_coeffs[4]; + float aberr_scale[3]; + float sep; + //viewport is half the screen + ohmd_device_getf(hmd, OHMD_SCREEN_HORIZONTAL_SIZE, &(viewport_scale[0])); + viewport_scale[0] /= 2.0f; + ohmd_device_getf(hmd, OHMD_SCREEN_VERTICAL_SIZE, &(viewport_scale[1])); + //distortion coefficients + ohmd_device_getf(hmd, OHMD_UNIVERSAL_DISTORTION_K, &(distortion_coeffs[0])); + ohmd_device_getf(hmd, OHMD_UNIVERSAL_ABERRATION_K, &(aberr_scale[0])); + //calculate lens centers (assuming the eye separation is the distance between the lens centers) + ohmd_device_getf(hmd, OHMD_LENS_HORIZONTAL_SEPARATION, &sep); + ohmd_device_getf(hmd, OHMD_LENS_VERTICAL_POSITION, &(left_lens_center[1])); + ohmd_device_getf(hmd, OHMD_LENS_VERTICAL_POSITION, &(right_lens_center[1])); + left_lens_center[0] = viewport_scale[0] - sep/2.0f; + right_lens_center[0] = sep/2.0f; + //assume calibration was for lens view to which ever edge of screen is further away from lens center + float warp_scale = (left_lens_center[0] > right_lens_center[0]) ? left_lens_center[0] : right_lens_center[0]; + //float warp_scale = 0.305f; // test + float warp_adj = 1.0f; + +#if 0 + const char* vertex; + ohmd_gets(OHMD_GLSL_ES_DISTORTION_VERT_SRC, &vertex); + const char* fragment; + ohmd_gets(OHMD_GLSL_ES_DISTORTION_FRAG_SRC, &fragment); +#else + const char* vertex; + ohmd_gets(OHMD_GLSL_DISTORTION_VERT_SRC, &vertex); + const char* fragment; + ohmd_gets(OHMD_GLSL_DISTORTION_FRAG_SRC, &fragment); +#endif // + + shader = compile_shader(vertex, fragment); + assert(GL_NO_ERROR == glGetError()); + + glUseProgram(shader); + assert(GL_NO_ERROR == glGetError()); + + glUniform1i(glGetUniformLocation(shader, "warpTexture"), 0); + assert(GL_NO_ERROR == glGetError()); + glUniform2fv(glGetUniformLocation(shader, "ViewportScale"), 1, viewport_scale); + assert(GL_NO_ERROR == glGetError()); + glUniform3fv(glGetUniformLocation(shader, "aberr"), 1, aberr_scale); + assert(GL_NO_ERROR == glGetError()); + + glUniform1f(glGetUniformLocation(shader, "WarpScale"), warp_scale*warp_adj); + assert(GL_NO_ERROR == glGetError()); + glUniform4fv(glGetUniformLocation(shader, "HmdWarpParam"), 1, distortion_coeffs); + assert(GL_NO_ERROR == glGetError()); + + float mvp [16] = { 2.0f, 0.0f, 0.0f, -1.0f, /* | */ 0.0f, 2.0f, 0.0f, -1.0f, /* | */ 0.0f, 0.0f, 1.0f, 0.0f, /* | */ 0.0f, 0.0f, 0.0f, 1.0f }; + glUniformMatrix4fv(glGetUniformLocation(shader, "mvp"), 1, GL_TRUE, mvp); + assert(GL_NO_ERROR == glGetError()); + + glUseProgram(0); + + return true; +} + +bool lvrcReleaseFrameResources(struct lvrcInstance * state) +{ + // TODO + + return true; +} + +bool lvrcBeginFrame(struct lvrcInstance * state) +{ + return true; // nothing yet +} + +static void prepare(GLuint texture) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + assert(GL_NO_ERROR == glGetError()); + + glUseProgram(shader); +} + +static void draw() +{ + glVertexAttribPointer(glGetAttribLocation(shader, "coords"), 2, GL_FLOAT, 0, 0, textureVertices); + glEnableVertexAttribArray(glGetAttribLocation(shader, "coords")); + assert(GL_NO_ERROR == glGetError()); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + assert(GL_NO_ERROR == glGetError()); +} + +bool lvrcSubmitFrameLeft(struct lvrcInstance * instance, GLuint texture) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return false; + } + + unsigned int hmd_w = internalInstance->swapChain->mode->hdisplay; + unsigned int hmd_h = internalInstance->swapChain->mode->vdisplay; + + prepare(texture); + + glViewport(0, 0, hmd_w/2, hmd_h); + + glUniform2fv(glGetUniformLocation(shader, "LensCenter"), 1, left_lens_center); + assert(GL_NO_ERROR == glGetError()); + + draw(); + + return true; +} + +bool lvrcSubmitFrameRight(struct lvrcInstance * instance, GLuint texture) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return false; + } + + unsigned int hmd_w = internalInstance->swapChain->mode->hdisplay; + unsigned int hmd_h = internalInstance->swapChain->mode->vdisplay; + + prepare(texture); + + glViewport(hmd_w/2, 0, hmd_w/2, hmd_h); + + glUniform2fv(glGetUniformLocation(shader, "LensCenter"), 1, right_lens_center); + assert(GL_NO_ERROR == glGetError()); + + draw(); + + return true; +} + +bool lvrcEndFrame(struct lvrcInstance * instance) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return false; + } + + SwapBuffers(internalInstance->swapChain); + + return true; +} diff --git a/src/frame.h b/src/frame.h new file mode 100644 index 0000000..a0e4ed6 --- /dev/null +++ b/src/frame.h @@ -0,0 +1,5 @@ +#ifndef LINUX_VR_COMPOSITOR_FRAME_H +#define LINUX_VR_COMPOSITOR_FRAME_H + + +#endif // LINUX_VR_COMPOSITOR_FRAME_H diff --git a/src/instance.c b/src/instance.c new file mode 100644 index 0000000..e5eef0e --- /dev/null +++ b/src/instance.c @@ -0,0 +1,29 @@ +#include "instance.h" + +#include "lvrc_internal.h" + +#include + +#ifdef ENABLE_XCB +# include +#endif // ENABLE_XCB + +struct lvrcInstance * lvrcCreateInstance(ohmd_device * hmd) +{ + Instance * instance = calloc(1, sizeof(Instance)); + + instance->device = hmd; + +#ifdef ENABLE_XCB + instance->connection = xcb_connect(NULL, &instance->screen); +#endif // ENABLE_XCB + + instance->swapChain = NULL; + + return (struct lvrcInstance *)instance; +} + +void lvrcDestroyInstance(struct lvrcInstance * instance) +{ + free(instance); +} diff --git a/src/instance.h b/src/instance.h new file mode 100644 index 0000000..37717a2 --- /dev/null +++ b/src/instance.h @@ -0,0 +1,25 @@ +#ifndef LINUX_VR_COMPOSITOR_STATE_H +#define LINUX_VR_COMPOSITOR_STATE_H + +#include + +#ifdef ENABLE_XCB +# include +#endif // ENABLE_XCB + +#include "swapChain.h" + +typedef struct +{ + ohmd_device * device; + +#ifdef ENABLE_XCB + xcb_connection_t * connection; + int screen; +#endif // ENABLE_XCB + + SwapChain * swapChain; + +} Instance; + +#endif // LINUX_VR_COMPOSITOR_STATE_H diff --git a/src/lvrc.c b/src/lvrc.c new file mode 100644 index 0000000..adf3e2c --- /dev/null +++ b/src/lvrc.c @@ -0,0 +1 @@ +#include "lvrc_internal.h" diff --git a/src/lvrc_internal.h b/src/lvrc_internal.h new file mode 100644 index 0000000..ccf8823 --- /dev/null +++ b/src/lvrc_internal.h @@ -0,0 +1,9 @@ +#ifndef LINUX_VR_COMPOSITOR_INTERNAL_H +#define LINUX_VR_COMPOSITOR_INTERNAL_H + +#define GL_GLES_PROTOTYPES 1 +#include // FIXME + +#include + +#endif // LINUX_VR_COMPOSITOR_INTERNAL_H diff --git a/src/swapChain.c b/src/swapChain.c new file mode 100644 index 0000000..5d7de44 --- /dev/null +++ b/src/swapChain.c @@ -0,0 +1,411 @@ +#include "swapChain.h" + +#include "lvrc_internal.h" +#include "instance.h" + +#include + +#include +#include +#include + +#include +#include + +#if ENABLE_XCB +# include +# include +#endif // ENABLE_XCB + +#define EDID_SIZE (0x80) + +static int drm_find_psvr_fd(SwapChain * swapChain, int fd) +{ + drmModeRes * resources = NULL; + drmModeConnector * connector = NULL; + drmModeEncoder * encoder = NULL; + + resources = drmModeGetResources(fd); + if (!resources) + { + //fprintf(stderr, "drmModeGetResources failed\n"); + return(0); + } + + if (resources->count_connectors <= 0) + { + //fprintf(stderr, "no connector\n"); + return(0); + } + + int i = 0; + for (i = 0; i < resources->count_connectors; i++) + { + drmModeConnector * connectorToTest = drmModeGetConnector(fd, resources->connectors[i]); + + if (connectorToTest == NULL) + { + continue; + } + + if (connectorToTest->connection != DRM_MODE_CONNECTED) + { + continue; + } + + if (connectorToTest->count_modes <= 0) + { + continue; + } + + for (int j = 0; j < connectorToTest->count_props; j++) + { + drmModePropertyPtr prop = drmModeGetProperty(fd, connectorToTest->props[j]); + if (prop) + { + if (strcmp(prop->name, "EDID") == 0) + { + drmModePropertyBlobPtr blob_ptr = drmModeGetPropertyBlob(fd, connectorToTest->prop_values[j]); + if (blob_ptr) + { + char edid[EDID_SIZE]; + memcpy(&edid, blob_ptr->data, EDID_SIZE); + drmModeFreePropertyBlob(blob_ptr); + + if (strncmp(edid+95, "SIE HMD *08", 12) == 0) // ugly + { + connector = connectorToTest; + break; + } + } + } + } + } + + if (connector) + { + break; + } + + drmModeFreeConnector(connectorToTest); + } + + if (!connector) + { + return 0; + } + + if (i == resources->count_connectors) + { + //fprintf(stderr, "No currently active connector found.\n"); + return(0); + } + + if (resources->count_encoders <= 0) + { + //fprintf(stderr, "no encoder\n"); + return(0); + } + + for (i = 0; i < resources->count_encoders; i++) + { + encoder = drmModeGetEncoder(fd, resources->encoders[i]); + + if (encoder == NULL) + { + continue; + } + + if (encoder->encoder_id == connector->encoder_id) + { + break; + } + + drmModeFreeEncoder(encoder); + } + + static drmModeModeInfo force_timing; // PSVR + force_timing.clock = 297700; + force_timing.hdisplay = 1920; + force_timing.hsync_start = 2008; + force_timing.hsync_end = 2052; + force_timing.htotal = 2200; + force_timing.vdisplay = 1080; + force_timing.vsync_start = 1084; + force_timing.vsync_end = 1089; + force_timing.vtotal = 1125; + + swapChain->fd = fd; + + swapChain->connector = connector; + swapChain->encoder = encoder; + swapChain->mode = &force_timing; + + return 1; +} + +static const char device_name[] = "/dev/dri/card1"; + +static int drm_find_psvr_udev(SwapChain * swapChain) +{ + int fd = open(device_name, O_RDWR); // FIXME : use udev to available devices + + if (fd < 0) + { + fprintf(stderr, "Can't open device\n"); + return 0; + } + + return drm_find_psvr_fd(swapChain, fd); +} + +#ifdef ENABLE_XCB +static int drm_find_psvr_xcb(SwapChain * swapChain, xcb_connection_t * connection, int screen) +{ + xcb_randr_query_version_cookie_t rqv_c = xcb_randr_query_version(connection, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); + xcb_randr_query_version_reply_t *rqv_r = xcb_randr_query_version_reply(connection, rqv_c, NULL); + + if (!rqv_r || rqv_r->minor_version < 6) + { + printf("No new-enough RandR version\n"); + return(0); + } + + xcb_screen_iterator_t s_i; + + int i_s = 0; + + for (s_i = xcb_setup_roots_iterator(xcb_get_setup(connection)); s_i.rem; xcb_screen_next(&s_i), i_s++) + { + printf ("index %d screen %d\n", s_i.index, screen); + if (i_s == screen) + break; + } + + xcb_window_t root = s_i.data->root; + + printf("root %x\n", root); + + xcb_randr_get_screen_resources_cookie_t gsr_c = xcb_randr_get_screen_resources(connection, root); + + xcb_randr_get_screen_resources_reply_t *gsr_r = xcb_randr_get_screen_resources_reply(connection, gsr_c, NULL); + + if (!gsr_r) + { + printf("get_screen_resources failed\n"); + return(0); + } + + xcb_randr_output_t *ro = xcb_randr_get_screen_resources_outputs(gsr_r); + int o, c; + + xcb_randr_output_t output = 0; + + /* Find a connected but idle output */ + for (o = 0; output == 0 && o < gsr_r->num_outputs; o++) { + xcb_randr_get_output_info_cookie_t goi_c = xcb_randr_get_output_info(connection, ro[o], gsr_r->config_timestamp); + + xcb_randr_get_output_info_reply_t *goi_r = xcb_randr_get_output_info_reply(connection, goi_c, NULL); + + /* Find the first connected but unused output */ + if (goi_r->connection == XCB_RANDR_CONNECTION_CONNECTED && + goi_r->crtc == 0) { + output = ro[o]; + } + + free(goi_r); + } + + xcb_randr_crtc_t *rc = xcb_randr_get_screen_resources_crtcs(gsr_r); + + xcb_randr_crtc_t crtc = 0; + + /* Find an idle crtc */ + for (c = 0; crtc == 0 && c < gsr_r->num_crtcs; c++) { + xcb_randr_get_crtc_info_cookie_t gci_c = xcb_randr_get_crtc_info(connection, rc[c], gsr_r->config_timestamp); + + xcb_randr_get_crtc_info_reply_t *gci_r = xcb_randr_get_crtc_info_reply(connection, gci_c, NULL); + + /* Find the first connected but unused crtc */ + if (gci_r->mode == 0) + crtc = rc[c]; + + free(gci_r); + } + + free(gsr_r); + + printf("output %x crtc %x\n", output, crtc); + + xcb_randr_lease_t lease = xcb_generate_id(connection); + + xcb_randr_create_lease_cookie_t rcl_c = xcb_randr_create_lease(connection, + root, + lease, + 1, + 1, + &crtc, + &output); + xcb_randr_create_lease_reply_t *rcl_r = xcb_randr_create_lease_reply(connection, rcl_c, NULL); + + if (!rcl_r) { + printf("create_lease failed\n"); + exit(1); + } + + int *rcl_f = xcb_randr_create_lease_reply_fds(connection, rcl_r); + + return drm_find_psvr_fd(swapChain, rcl_f[0]); +} +#endif // ENABLE_XCB + +bool lvrcInitSwapChain(struct lvrcInstance * instance) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->device) + { + return false; + } + + SwapChain * swapChain = calloc(1, sizeof(SwapChain)); + +#ifdef ENABLE_XCB + if (drm_find_psvr_xcb(swapChain, internalInstance->connection, internalInstance->screen)) + { + printf("device found using XCB\n"); + } + else +#endif // ENABLE_XCB + if (drm_find_psvr_udev(swapChain)) + { + printf("device found using udev\n"); + } + else + { + fprintf(stderr, "couldn't find devicee\n"); + free(swapChain); + return false; + } + + swapChain->device = gbm_create_device(swapChain->fd); + + if (swapChain->device == NULL) + { + fprintf(stderr, "couldn't create gbm device\n"); + free(swapChain); + return false; + } + + swapChain->surface = gbm_surface_create(swapChain->device, swapChain->mode->hdisplay, swapChain->mode->vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + + if (swapChain->surface == NULL) + { + fprintf(stderr, "couldn't create gbm surface\n"); + free(swapChain); + return false; + } + + internalInstance->swapChain = swapChain; + + return true; +} + +bool lvrcReleaseSwapChain(struct lvrcInstance * instance) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return false; + } + + // TODO : destroy resources + + free(internalInstance->swapChain); + internalInstance->swapChain = NULL; + + return true; +} + +EGLNativeDisplayType lvrcSwapChainGetNativeDisplay(struct lvrcInstance * instance) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return NULL; + } + + return internalInstance->swapChain->device; +} + +EGLNativeWindowType lvrcSwapChainGetNativeWindow(struct lvrcInstance * instance) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return NULL; + } + + return internalInstance->swapChain->surface; +} + +unsigned int lvrcSwapChainGetWidth(struct lvrcInstance * instance) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return 0; + } + + return internalInstance->swapChain->mode->hdisplay; +} + +unsigned int lvrcSwapChainGetHeight(struct lvrcInstance * instance) +{ + Instance * internalInstance = (Instance*)instance; + + if (!internalInstance->swapChain) + { + return 0; + } + + return internalInstance->swapChain->mode->vdisplay; +} + +void SwapBuffers(SwapChain * swapChain) +{ + struct gbm_bo * newbo = gbm_surface_lock_front_buffer(swapChain->surface); + uint32_t handle = gbm_bo_get_handle(newbo).u32; + uint32_t stride = gbm_bo_get_stride(newbo); + + { + uint32_t newfb = 0; + drmModeAddFB(swapChain->fd, swapChain->mode->hdisplay, swapChain->mode->vdisplay, 24, 32, stride, handle, &newfb); + + int rc = drmModeSetCrtc(swapChain->fd, swapChain->encoder->crtc_id, newfb, 0, 0, &swapChain->connector->connector_id, 1, swapChain->mode); + + if (rc < 0) + { + fprintf(stderr, "drmModeSetCrtc() failed\n"); + return; + } + + if (swapChain->fbid) + { + drmModeRmFB(swapChain->fd, swapChain->fbid); + } + + swapChain->fbid = newfb; + } + + if (swapChain->bo) + { + gbm_surface_release_buffer(swapChain->surface, swapChain->bo); + } + + swapChain->bo = newbo; +} diff --git a/src/swapChain.h b/src/swapChain.h new file mode 100644 index 0000000..b5230e4 --- /dev/null +++ b/src/swapChain.h @@ -0,0 +1,29 @@ +#ifndef LINUX_VR_COMPOSITOR_SWAPCHAIN_H +#define LINUX_VR_COMPOSITOR_SWAPCHAIN_H + +#include + +#include +#include + +#include + +typedef struct +{ + int fd; + + drmModeConnector * connector; + drmModeEncoder * encoder; + drmModeModeInfo * mode; + + struct gbm_device * device; + struct gbm_surface * surface; + + uint32_t fbid; + struct gbm_bo * bo; + +} SwapChain; + +void SwapBuffers(SwapChain * swapChain); + +#endif // LINUX_VR_COMPOSITOR_SWAPCHAIN_H