First development release - WIP

This commit is contained in:
2018-09-18 20:00:49 +02:00
parent 9d8cc24939
commit a839879baa
13 changed files with 1080 additions and 1 deletions

30
CMakeLists.txt Normal file
View File

@@ -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)

View File

@@ -1,2 +1,67 @@
# Linux-VR-Compositor-Dev # Linux-VR-Compositor
```C
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h> // must include OpenGL before including the Linux VR Compositor header
#include <lvrc.h>
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);
}
```

60
include/lvrc.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef LINUX_VR_COMPOSITOR_H
#define LINUX_VR_COMPOSITOR_H
#include <openhmd.h>
#ifndef __GBM__
#define __GBM__
#endif // __GBM__
#include <EGL/egl.h>
#include <stdbool.h>
#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

146
src/context.c Normal file
View File

@@ -0,0 +1,146 @@
#include "context.h"
#include <GLES2/gl2.h>
#include <lvrc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
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);
}

26
src/context.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef LINUX_VR_COMPOSITOR_CONTEXT_H
#define LINUX_VR_COMPOSITOR_CONTEXT_H
#include <openhmd.h>
#include "swapChain.h"
#include <EGL/egl.h>
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

243
src/frame.c Normal file
View File

@@ -0,0 +1,243 @@
#include "frame.h"
#include "lvrc_internal.h"
#include "instance.h"
#include <stdio.h>
#include <assert.h>
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;
}

5
src/frame.h Normal file
View File

@@ -0,0 +1,5 @@
#ifndef LINUX_VR_COMPOSITOR_FRAME_H
#define LINUX_VR_COMPOSITOR_FRAME_H
#endif // LINUX_VR_COMPOSITOR_FRAME_H

29
src/instance.c Normal file
View File

@@ -0,0 +1,29 @@
#include "instance.h"
#include "lvrc_internal.h"
#include <malloc.h>
#ifdef ENABLE_XCB
# include <xcb/xcb.h>
#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);
}

25
src/instance.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef LINUX_VR_COMPOSITOR_STATE_H
#define LINUX_VR_COMPOSITOR_STATE_H
#include <openhmd.h>
#ifdef ENABLE_XCB
# include <xcb/xcb.h>
#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

1
src/lvrc.c Normal file
View File

@@ -0,0 +1 @@
#include "lvrc_internal.h"

9
src/lvrc_internal.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef LINUX_VR_COMPOSITOR_INTERNAL_H
#define LINUX_VR_COMPOSITOR_INTERNAL_H
#define GL_GLES_PROTOTYPES 1
#include <GLES2/gl2.h> // FIXME
#include <lvrc.h>
#endif // LINUX_VR_COMPOSITOR_INTERNAL_H

411
src/swapChain.c Normal file
View File

@@ -0,0 +1,411 @@
#include "swapChain.h"
#include "lvrc_internal.h"
#include "instance.h"
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#if ENABLE_XCB
# include <xcb/xcb.h>
# include <xcb/randr.h>
#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;
}

29
src/swapChain.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef LINUX_VR_COMPOSITOR_SWAPCHAIN_H
#define LINUX_VR_COMPOSITOR_SWAPCHAIN_H
#include <openhmd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
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