First development release - WIP
This commit is contained in:
30
CMakeLists.txt
Normal file
30
CMakeLists.txt
Normal 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)
|
||||
67
README.md
67
README.md
@@ -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
60
include/lvrc.h
Normal 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
146
src/context.c
Normal 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
26
src/context.h
Normal 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
243
src/frame.c
Normal 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
5
src/frame.h
Normal 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
29
src/instance.c
Normal 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
25
src/instance.h
Normal 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
1
src/lvrc.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "lvrc_internal.h"
|
||||
9
src/lvrc_internal.h
Normal file
9
src/lvrc_internal.h
Normal 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
411
src/swapChain.c
Normal 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
29
src/swapChain.h
Normal 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
|
||||
Reference in New Issue
Block a user