From 2dc79a48a812a676e2ca767ea772475a08a00ac4 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 21 Jul 2018 05:30:13 -0700 Subject: [PATCH] Use single pass stereo rendering when supported; --- src/graphics/graphics.c | 13 ++--- src/graphics/graphics.h | 7 +-- src/graphics/opengl.c | 117 +++++++++++++++++++++++----------------- src/headset/fake.c | 31 ++++++----- src/headset/openvr.c | 31 +++++------ src/headset/webvr.c | 24 ++++++--- src/resources/shaders.c | 24 +++++++-- 7 files changed, 147 insertions(+), 100 deletions(-) diff --git a/src/graphics/graphics.c b/src/graphics/graphics.c index 894f33b8..1d7843ce 100644 --- a/src/graphics/graphics.c +++ b/src/graphics/graphics.c @@ -121,13 +121,14 @@ void lovrGraphicsSetCamera(Camera* camera, bool clear) { if (!camera) { int width, height; lovrGraphicsGetDimensions(&width, &height); + state.camera.stereo = false; state.camera.canvas = NULL; - state.camera.viewport[0] = 0; - state.camera.viewport[1] = 0; - state.camera.viewport[2] = width; - state.camera.viewport[3] = height; - mat4_identity(state.camera.viewMatrix); - mat4_perspective(state.camera.projection, .01f, 100.f, 67 * M_PI / 180., (float) width / height); + state.camera.viewport[0][0] = 0; + state.camera.viewport[0][1] = 0; + state.camera.viewport[0][2] = width; + state.camera.viewport[0][3] = height; + mat4_identity(state.camera.viewMatrix[0]); + mat4_perspective(state.camera.projection[0], .01f, 100.f, 67 * M_PI / 180., (float) width / height); } else { state.camera = *camera; } diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index a9d6c54e..f8868601 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -81,10 +81,11 @@ typedef struct { } GraphicsStats; typedef struct { + bool stereo; Canvas* canvas; - uint32_t viewport[4]; - float viewMatrix[16]; - float projection[16]; + float viewport[2][4]; + float viewMatrix[2][16]; + float projection[2][16]; } Camera; typedef struct { diff --git a/src/graphics/opengl.c b/src/graphics/opengl.c index 8569c8ce..b28c7603 100644 --- a/src/graphics/opengl.c +++ b/src/graphics/opengl.c @@ -57,8 +57,9 @@ static struct { Texture* textures[MAX_TEXTURES]; uint32_t vertexArray; uint32_t vertexBuffer; - uint32_t viewport[4]; + float viewport[4]; bool srgb; + bool supportsSinglepass; GraphicsLimits limits; GraphicsStats stats; } state; @@ -371,9 +372,9 @@ static void lovrGpuBindVertexBuffer(uint32_t vertexBuffer) { } } -static void lovrGpuSetViewport(uint32_t viewport[4]) { - if (memcmp(state.viewport, viewport, 4 * sizeof(uint32_t))) { - memcpy(state.viewport, viewport, 4 * sizeof(uint32_t)); +static void lovrGpuSetViewport(float viewport[4]) { + if (memcmp(state.viewport, viewport, 4 * sizeof(float))) { + memcpy(state.viewport, viewport, 4 * sizeof(float)); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); } } @@ -400,6 +401,7 @@ void lovrGpuInit(bool srgb, gpuProc (*getProcAddress)(const char*)) { glEnable(GL_BLEND); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); state.srgb = srgb; + state.supportsSinglepass = GLAD_GL_ARB_viewport_array && GLAD_GL_NV_viewport_array2 && GLAD_GL_NV_stereo_view_rendering; state.blendMode = -1; state.blendAlphaMode = -1; state.culling = false; @@ -626,28 +628,35 @@ void lovrGpuDraw(DrawCommand* command) { } // Transform - lovrShaderSetMatrix(shader, "lovrProjection", command->camera.projection, 16); - lovrShaderSetMatrix(shader, "lovrView", command->camera.viewMatrix, 16); lovrShaderSetMatrix(shader, "lovrModel", command->transform, 16); + lovrShaderSetMatrix(shader, "lovrViews", command->camera.viewMatrix, 32); + lovrShaderSetMatrix(shader, "lovrProjections", command->camera.projection, 32); - float modelView[16]; - mat4_multiply(mat4_set(modelView, command->camera.viewMatrix), command->transform); - lovrShaderSetMatrix(shader, "lovrTransform", modelView, 16); + float modelView[32]; + mat4_multiply(mat4_set(modelView, command->camera.viewMatrix[0]), command->transform); + mat4_multiply(mat4_set(modelView + 16, command->camera.viewMatrix[1]), command->transform); + lovrShaderSetMatrix(shader, "lovrTransforms", modelView, 32); - if (lovrShaderHasUniform(shader, "lovrNormalMatrix")) { - if (mat4_invert(modelView)) { + if (lovrShaderHasUniform(shader, "lovrNormalMatrices")) { + if (mat4_invert(modelView) && mat4_invert(modelView + 16)) { mat4_transpose(modelView); + mat4_transpose(modelView + 16); } else { mat4_identity(modelView); + mat4_identity(modelView + 16); } - float normalMatrix[9] = { + float normalMatrices[18] = { modelView[0], modelView[1], modelView[2], modelView[4], modelView[5], modelView[6], modelView[8], modelView[9], modelView[10], + + modelView[16], modelView[17], modelView[18], + modelView[20], modelView[21], modelView[22], + modelView[24], modelView[25], modelView[26] }; - lovrShaderSetMatrix(shader, "lovrNormalMatrix", normalMatrix, 9); + lovrShaderSetMatrix(shader, "lovrNormalMatrices", normalMatrices, 18); } // Pose @@ -718,47 +727,55 @@ void lovrGpuDraw(DrawCommand* command) { } } - // Viewport - if (pipeline->canvasCount > 0) { - int width = lovrTextureGetWidth((Texture*) pipeline->canvas[0], 0); - int height = lovrTextureGetHeight((Texture*) pipeline->canvas[0], 0); - lovrGpuSetViewport((uint32_t[4]) { 0, 0, width, height }); - } else { - lovrGpuSetViewport(command->camera.viewport); - } - - // Bind uniforms - lovrShaderBind(shader); - // Bind attributes lovrMeshBind(mesh, shader); - // Draw (TODEW) - uint32_t rangeStart, rangeCount; - lovrMeshGetDrawRange(mesh, &rangeStart, &rangeCount); - uint32_t indexCount; - size_t indexSize; - lovrMeshReadIndices(mesh, &indexCount, &indexSize); - GLenum glDrawMode = convertMeshDrawMode(lovrMeshGetDrawMode(mesh)); - if (indexCount > 0) { - size_t count = rangeCount ? rangeCount : indexCount; - GLenum indexType = indexSize == sizeof(uint16_t) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; - size_t offset = rangeStart * indexSize; - if (instances > 1) { - glDrawElementsInstanced(glDrawMode, count, indexType, (GLvoid*) offset, instances); - } else { - glDrawElements(glDrawMode, count, indexType, (GLvoid*) offset); - } - } else { - size_t count = rangeCount ? rangeCount : lovrMeshGetVertexCount(mesh); - if (instances > 1) { - glDrawArraysInstanced(glDrawMode, rangeStart, count, instances); - } else { - glDrawArrays(glDrawMode, rangeStart, count); - } - } + bool stereo = pipeline->canvasCount == 0 && command->camera.stereo == true; + int drawCount = 1 + (stereo == true && !state.supportsSinglepass); - state.stats.drawCalls++; + // Draw (TODEW) + for (int i = 0; i < drawCount; i++) { + if (pipeline->canvasCount > 0) { + int width = lovrTextureGetWidth((Texture*) pipeline->canvas[0], 0); + int height = lovrTextureGetHeight((Texture*) pipeline->canvas[0], 0); + lovrGpuSetViewport((float[4]) { 0, 0, width, height }); + } else if (state.supportsSinglepass) { + glViewportArrayv(0, 2, command->camera.viewport); + } else { + lovrGpuSetViewport(command->camera.viewport[i]); + } + + // Bind uniforms + int eye = (stereo && state.supportsSinglepass) ? -1 : i; + lovrShaderSetInt(shader, "lovrEye", &eye, 1); + lovrShaderBind(shader); + + uint32_t rangeStart, rangeCount; + lovrMeshGetDrawRange(mesh, &rangeStart, &rangeCount); + uint32_t indexCount; + size_t indexSize; + lovrMeshReadIndices(mesh, &indexCount, &indexSize); + GLenum glDrawMode = convertMeshDrawMode(lovrMeshGetDrawMode(mesh)); + if (indexCount > 0) { + size_t count = rangeCount ? rangeCount : indexCount; + GLenum indexType = indexSize == sizeof(uint16_t) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + size_t offset = rangeStart * indexSize; + if (instances > 1) { + glDrawElementsInstanced(glDrawMode, count, indexType, (GLvoid*) offset, instances); + } else { + glDrawElements(glDrawMode, count, indexType, (GLvoid*) offset); + } + } else { + size_t count = rangeCount ? rangeCount : lovrMeshGetVertexCount(mesh); + if (instances > 1) { + glDrawArraysInstanced(glDrawMode, rangeStart, count, instances); + } else { + glDrawArrays(glDrawMode, rangeStart, count); + } + } + + state.stats.drawCalls++; + } } void lovrGpuPresent() { diff --git a/src/headset/fake.c b/src/headset/fake.c index 0acab143..fdbd237f 100644 --- a/src/headset/fake.c +++ b/src/headset/fake.c @@ -277,7 +277,6 @@ static ModelData* fakeControllerNewModelData(Controller* controller) { return NULL; } -#include static void fakeRenderTo(void (*callback)(void*), void* userdata) { if (!state.window || !state.mirrored) { return; @@ -285,20 +284,24 @@ static void fakeRenderTo(void (*callback)(void*), void* userdata) { int width, height; fakeGetDisplayDimensions(&width, &height); - Camera camera = { .viewport = { 0, 0, width, height } }; - - mat4_perspective(camera.projection, state.clipNear, state.clipFar, 67 * M_PI / 180., (float) width / height); - mat4_identity(camera.viewMatrix); - mat4_translate(camera.viewMatrix, 0, state.offset, 0); - mat4_multiply(camera.viewMatrix, state.transform); - mat4_invert(camera.viewMatrix); - - for (int eye = 0; eye < 2; eye++) { - camera.viewport[0] = width * eye; - lovrGraphicsSetCamera(&camera, eye == 0); - callback(userdata); - } + Camera camera = { + .stereo = true, + .canvas = NULL, + .viewport = { + { 0, 0, width, height }, + { width, 0, width, height } + } + }; + mat4_perspective(camera.projection[0], state.clipNear, state.clipFar, 67 * M_PI / 180., (float) width / height); + mat4_identity(camera.viewMatrix[0]); + mat4_translate(camera.viewMatrix[0], 0, state.offset, 0); + mat4_multiply(camera.viewMatrix[0], state.transform); + mat4_invert(camera.viewMatrix[0]); + mat4_set(camera.projection[1], camera.projection[0]); + mat4_set(camera.viewMatrix[1], camera.viewMatrix[0]); + lovrGraphicsSetCamera(&camera, true); + callback(userdata); lovrGraphicsSetCamera(NULL, false); } diff --git a/src/headset/openvr.c b/src/headset/openvr.c index 5cfed2a8..d9348163 100644 --- a/src/headset/openvr.c +++ b/src/headset/openvr.c @@ -669,26 +669,30 @@ static void openvrRenderTo(void (*callback)(void*), void* userdata) { mat4_fromMat34(head, state.renderPoses[state.headsetIndex].mDeviceToAbsoluteTracking.m); Camera camera = { + .stereo = true, .canvas = state.canvas, - .viewport = { 0, 0, state.renderWidth, state.renderHeight } + .viewport = { + { 0, 0, state.renderWidth, state.renderHeight }, + { state.renderWidth, 0, state.renderWidth, state.renderHeight } + } }; for (int i = 0; i < 2; i++) { EVREye vrEye = (i == 0) ? EVREye_Eye_Left : EVREye_Eye_Right; - mat4_fromMat44(camera.projection, state.system->GetProjectionMatrix(vrEye, state.clipNear, state.clipFar).m); - mat4_identity(camera.viewMatrix); - mat4_translate(camera.viewMatrix, 0, state.offset, 0); - mat4_multiply(camera.viewMatrix, head); - mat4_multiply(camera.viewMatrix, mat4_fromMat34(eye, state.system->GetEyeToHeadTransform(vrEye).m)); - mat4_invert(camera.viewMatrix); - - camera.viewport[0] = state.renderWidth * i; - lovrGraphicsSetCamera(&camera, i == 0); - callback(userdata); - lovrGraphicsSetCanvas(NULL, 0); + mat4_fromMat44(camera.projection[i], state.system->GetProjectionMatrix(vrEye, state.clipNear, state.clipFar).m); + mat4_identity(camera.viewMatrix[i]); + mat4_translate(camera.viewMatrix[i], 0, state.offset, 0); + mat4_multiply(camera.viewMatrix[i], head); + mat4_multiply(camera.viewMatrix[i], mat4_fromMat34(eye, state.system->GetEyeToHeadTransform(vrEye).m)); + mat4_invert(camera.viewMatrix[i]); } + lovrGraphicsSetCamera(&camera, true); + callback(userdata); + lovrGraphicsSetCamera(NULL, false); + lovrGraphicsSetCanvas(NULL, 0); lovrCanvasResolve(state.canvas); + state.isRendering = false; // Submit uintptr_t texture = (uintptr_t) lovrTextureGetId((Texture*) state.canvas); @@ -700,9 +704,6 @@ static void openvrRenderTo(void (*callback)(void*), void* userdata) { state.compositor->Submit(EVREye_Eye_Right, &eyeTexture, &right, EVRSubmitFlags_Submit_Default); lovrGpuDirtyTexture(0); - lovrGraphicsSetCamera(NULL, false); - state.isRendering = false; - if (state.isMirrored) { lovrGraphicsPushPipeline(); lovrGraphicsSetColor((Color) { 1, 1, 1, 1 }); diff --git a/src/headset/webvr.c b/src/headset/webvr.c index 67ccdccd..54ba0609 100644 --- a/src/headset/webvr.c +++ b/src/headset/webvr.c @@ -89,14 +89,22 @@ static void onMountChanged(bool mounted) { static void onFrame(float* leftView, float* rightView, float* leftProjection, float* rightProjection, void* userdata) { int width, height; webvrGetDisplayDimensions(&width, &height); - Camera camera = { .viewport = { 0, 0, width, height } }; - for (int eye = 0; eye < 2; eye++) { - camera.viewport[0] = width * eye; - memcpy(camera.projection, eye == 0 ? leftProjection : rightProjection); - memcpy(camera.viewMatrix, eye == 0 ? leftView : rightView); - lovrGraphicsSetCamera(&camera, eye == 0); - state.renderCallback(userdata); - } + + Camera camera = { + .stereo = true, + .canvas = NULL, + .viewport = { + { 0, 0, width, height }, + { width, 0, width, height } + } + }; + + memcpy(camera.projection[0], leftProjection); + memcpy(camera.projection[1], rightProjection); + memcpy(camera.viewMatrix[0], leftView); + memcpy(camera.viewMatrix[1], rightView); + lovrGraphicsSetCamera(&camera, true); + state.renderCallback(userdata); lovrGraphicsSetCamera(NULL, false); } diff --git a/src/resources/shaders.c b/src/resources/shaders.c index 6125b0b5..430383cf 100644 --- a/src/resources/shaders.c +++ b/src/resources/shaders.c @@ -26,9 +26,15 @@ const char* lovrShaderVertexPrefix = "" "precision mediump float; \n" #else "#version 150 \n" +"#extension GL_NV_viewport_array2 : enable \n" +"#extension GL_NV_stereo_view_rendering : enable \n" #endif "#define VERTEX VERTEX \n" "#define MAX_BONES 48 \n" +"#define lovrView lovrViews[lovrEye] \n" +"#define lovrProjection lovrProjections[lovrEye] \n" +"#define lovrTransform lovrTransforms[lovrEye] \n" +"#define lovrNormalMatrix lovrNormalMatrices[lovrEye] \n" "in vec3 lovrPosition; \n" "in vec3 lovrNormal; \n" "in vec2 lovrTexCoord; \n" @@ -38,11 +44,12 @@ const char* lovrShaderVertexPrefix = "" "in vec4 lovrBoneWeights; \n" "out vec2 texCoord; \n" "out vec4 vertexColor; \n" +"uniform int lovrEye; \n" "uniform mat4 lovrModel; \n" -"uniform mat4 lovrView; \n" -"uniform mat4 lovrProjection; \n" -"uniform mat4 lovrTransform; \n" -"uniform mat3 lovrNormalMatrix; \n" +"uniform mat4 lovrViews[2]; \n" +"uniform mat4 lovrProjections[2]; \n" +"uniform mat4 lovrTransforms[2]; \n" +"uniform mat3 lovrNormalMatrices[2]; \n" "uniform mat3 lovrMaterialTransform; \n" "uniform float lovrPointSize; \n" "uniform mat4 lovrPose[MAX_BONES]; \n" @@ -85,6 +92,15 @@ const char* lovrShaderVertexSuffix = "" " lovrPose[lovrBones[2]] * lovrBoneWeights[2] + \n" " lovrPose[lovrBones[3]] * lovrBoneWeights[3]; \n" " gl_PointSize = lovrPointSize; \n" +"#if defined(GL_NV_viewport_array2) && defined(GL_NV_stereo_view_rendering) \n" +" if (lovrEye < 0) { \n" +" gl_Position = position(lovrProjections[0], lovrTransforms[0], pose * vec4(lovrPosition, 1.0)); \n" +" gl_SecondaryPositionNV = position(lovrProjections[1], lovrTransforms[1], pose * vec4(lovrPosition, 1.0)); \n" +" gl_ViewportMask[0] = (1 << 0); \n" +" gl_SecondaryViewportMaskNV[0] = (1 << 1); \n" +" return; \n" +" }\n" +"#endif \n" " gl_Position = position(lovrProjection, lovrTransform, pose * vec4(lovrPosition, 1.0)); \n" "}";