Use single pass stereo rendering when supported;

This commit is contained in:
bjorn 2018-07-21 05:30:13 -07:00
parent 7d5f2ba75a
commit 2dc79a48a8
7 changed files with 147 additions and 100 deletions

View File

@ -121,13 +121,14 @@ void lovrGraphicsSetCamera(Camera* camera, bool clear) {
if (!camera) { if (!camera) {
int width, height; int width, height;
lovrGraphicsGetDimensions(&width, &height); lovrGraphicsGetDimensions(&width, &height);
state.camera.stereo = false;
state.camera.canvas = NULL; state.camera.canvas = NULL;
state.camera.viewport[0] = 0; state.camera.viewport[0][0] = 0;
state.camera.viewport[1] = 0; state.camera.viewport[0][1] = 0;
state.camera.viewport[2] = width; state.camera.viewport[0][2] = width;
state.camera.viewport[3] = height; state.camera.viewport[0][3] = height;
mat4_identity(state.camera.viewMatrix); mat4_identity(state.camera.viewMatrix[0]);
mat4_perspective(state.camera.projection, .01f, 100.f, 67 * M_PI / 180., (float) width / height); mat4_perspective(state.camera.projection[0], .01f, 100.f, 67 * M_PI / 180., (float) width / height);
} else { } else {
state.camera = *camera; state.camera = *camera;
} }

View File

@ -81,10 +81,11 @@ typedef struct {
} GraphicsStats; } GraphicsStats;
typedef struct { typedef struct {
bool stereo;
Canvas* canvas; Canvas* canvas;
uint32_t viewport[4]; float viewport[2][4];
float viewMatrix[16]; float viewMatrix[2][16];
float projection[16]; float projection[2][16];
} Camera; } Camera;
typedef struct { typedef struct {

View File

@ -57,8 +57,9 @@ static struct {
Texture* textures[MAX_TEXTURES]; Texture* textures[MAX_TEXTURES];
uint32_t vertexArray; uint32_t vertexArray;
uint32_t vertexBuffer; uint32_t vertexBuffer;
uint32_t viewport[4]; float viewport[4];
bool srgb; bool srgb;
bool supportsSinglepass;
GraphicsLimits limits; GraphicsLimits limits;
GraphicsStats stats; GraphicsStats stats;
} state; } state;
@ -371,9 +372,9 @@ static void lovrGpuBindVertexBuffer(uint32_t vertexBuffer) {
} }
} }
static void lovrGpuSetViewport(uint32_t viewport[4]) { static void lovrGpuSetViewport(float viewport[4]) {
if (memcmp(state.viewport, viewport, 4 * sizeof(uint32_t))) { if (memcmp(state.viewport, viewport, 4 * sizeof(float))) {
memcpy(state.viewport, viewport, 4 * sizeof(uint32_t)); memcpy(state.viewport, viewport, 4 * sizeof(float));
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
} }
} }
@ -400,6 +401,7 @@ void lovrGpuInit(bool srgb, gpuProc (*getProcAddress)(const char*)) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
state.srgb = srgb; 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.blendMode = -1;
state.blendAlphaMode = -1; state.blendAlphaMode = -1;
state.culling = false; state.culling = false;
@ -626,28 +628,35 @@ void lovrGpuDraw(DrawCommand* command) {
} }
// Transform // Transform
lovrShaderSetMatrix(shader, "lovrProjection", command->camera.projection, 16);
lovrShaderSetMatrix(shader, "lovrView", command->camera.viewMatrix, 16);
lovrShaderSetMatrix(shader, "lovrModel", command->transform, 16); lovrShaderSetMatrix(shader, "lovrModel", command->transform, 16);
lovrShaderSetMatrix(shader, "lovrViews", command->camera.viewMatrix, 32);
lovrShaderSetMatrix(shader, "lovrProjections", command->camera.projection, 32);
float modelView[16]; float modelView[32];
mat4_multiply(mat4_set(modelView, command->camera.viewMatrix), command->transform); mat4_multiply(mat4_set(modelView, command->camera.viewMatrix[0]), command->transform);
lovrShaderSetMatrix(shader, "lovrTransform", modelView, 16); mat4_multiply(mat4_set(modelView + 16, command->camera.viewMatrix[1]), command->transform);
lovrShaderSetMatrix(shader, "lovrTransforms", modelView, 32);
if (lovrShaderHasUniform(shader, "lovrNormalMatrix")) { if (lovrShaderHasUniform(shader, "lovrNormalMatrices")) {
if (mat4_invert(modelView)) { if (mat4_invert(modelView) && mat4_invert(modelView + 16)) {
mat4_transpose(modelView); mat4_transpose(modelView);
mat4_transpose(modelView + 16);
} else { } else {
mat4_identity(modelView); mat4_identity(modelView);
mat4_identity(modelView + 16);
} }
float normalMatrix[9] = { float normalMatrices[18] = {
modelView[0], modelView[1], modelView[2], modelView[0], modelView[1], modelView[2],
modelView[4], modelView[5], modelView[6], modelView[4], modelView[5], modelView[6],
modelView[8], modelView[9], modelView[10], 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 // 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 // Bind attributes
lovrMeshBind(mesh, shader); lovrMeshBind(mesh, shader);
// Draw (TODEW) bool stereo = pipeline->canvasCount == 0 && command->camera.stereo == true;
uint32_t rangeStart, rangeCount; int drawCount = 1 + (stereo == true && !state.supportsSinglepass);
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++; // 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() { void lovrGpuPresent() {

View File

@ -277,7 +277,6 @@ static ModelData* fakeControllerNewModelData(Controller* controller) {
return NULL; return NULL;
} }
#include <stdio.h>
static void fakeRenderTo(void (*callback)(void*), void* userdata) { static void fakeRenderTo(void (*callback)(void*), void* userdata) {
if (!state.window || !state.mirrored) { if (!state.window || !state.mirrored) {
return; return;
@ -285,20 +284,24 @@ static void fakeRenderTo(void (*callback)(void*), void* userdata) {
int width, height; int width, height;
fakeGetDisplayDimensions(&width, &height); fakeGetDisplayDimensions(&width, &height);
Camera camera = { .viewport = { 0, 0, width, height } }; Camera camera = {
.stereo = true,
mat4_perspective(camera.projection, state.clipNear, state.clipFar, 67 * M_PI / 180., (float) width / height); .canvas = NULL,
mat4_identity(camera.viewMatrix); .viewport = {
mat4_translate(camera.viewMatrix, 0, state.offset, 0); { 0, 0, width, height },
mat4_multiply(camera.viewMatrix, state.transform); { width, 0, width, height }
mat4_invert(camera.viewMatrix); }
};
for (int eye = 0; eye < 2; eye++) {
camera.viewport[0] = width * eye;
lovrGraphicsSetCamera(&camera, eye == 0);
callback(userdata);
}
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); lovrGraphicsSetCamera(NULL, false);
} }

View File

@ -669,26 +669,30 @@ static void openvrRenderTo(void (*callback)(void*), void* userdata) {
mat4_fromMat34(head, state.renderPoses[state.headsetIndex].mDeviceToAbsoluteTracking.m); mat4_fromMat34(head, state.renderPoses[state.headsetIndex].mDeviceToAbsoluteTracking.m);
Camera camera = { Camera camera = {
.stereo = true,
.canvas = state.canvas, .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++) { for (int i = 0; i < 2; i++) {
EVREye vrEye = (i == 0) ? EVREye_Eye_Left : EVREye_Eye_Right; EVREye vrEye = (i == 0) ? EVREye_Eye_Left : EVREye_Eye_Right;
mat4_fromMat44(camera.projection, state.system->GetProjectionMatrix(vrEye, state.clipNear, state.clipFar).m); mat4_fromMat44(camera.projection[i], state.system->GetProjectionMatrix(vrEye, state.clipNear, state.clipFar).m);
mat4_identity(camera.viewMatrix); mat4_identity(camera.viewMatrix[i]);
mat4_translate(camera.viewMatrix, 0, state.offset, 0); mat4_translate(camera.viewMatrix[i], 0, state.offset, 0);
mat4_multiply(camera.viewMatrix, head); mat4_multiply(camera.viewMatrix[i], head);
mat4_multiply(camera.viewMatrix, mat4_fromMat34(eye, state.system->GetEyeToHeadTransform(vrEye).m)); mat4_multiply(camera.viewMatrix[i], mat4_fromMat34(eye, state.system->GetEyeToHeadTransform(vrEye).m));
mat4_invert(camera.viewMatrix); mat4_invert(camera.viewMatrix[i]);
camera.viewport[0] = state.renderWidth * i;
lovrGraphicsSetCamera(&camera, i == 0);
callback(userdata);
lovrGraphicsSetCanvas(NULL, 0);
} }
lovrGraphicsSetCamera(&camera, true);
callback(userdata);
lovrGraphicsSetCamera(NULL, false);
lovrGraphicsSetCanvas(NULL, 0);
lovrCanvasResolve(state.canvas); lovrCanvasResolve(state.canvas);
state.isRendering = false;
// Submit // Submit
uintptr_t texture = (uintptr_t) lovrTextureGetId((Texture*) state.canvas); 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); state.compositor->Submit(EVREye_Eye_Right, &eyeTexture, &right, EVRSubmitFlags_Submit_Default);
lovrGpuDirtyTexture(0); lovrGpuDirtyTexture(0);
lovrGraphicsSetCamera(NULL, false);
state.isRendering = false;
if (state.isMirrored) { if (state.isMirrored) {
lovrGraphicsPushPipeline(); lovrGraphicsPushPipeline();
lovrGraphicsSetColor((Color) { 1, 1, 1, 1 }); lovrGraphicsSetColor((Color) { 1, 1, 1, 1 });

View File

@ -89,14 +89,22 @@ static void onMountChanged(bool mounted) {
static void onFrame(float* leftView, float* rightView, float* leftProjection, float* rightProjection, void* userdata) { static void onFrame(float* leftView, float* rightView, float* leftProjection, float* rightProjection, void* userdata) {
int width, height; int width, height;
webvrGetDisplayDimensions(&width, &height); webvrGetDisplayDimensions(&width, &height);
Camera camera = { .viewport = { 0, 0, width, height } };
for (int eye = 0; eye < 2; eye++) { Camera camera = {
camera.viewport[0] = width * eye; .stereo = true,
memcpy(camera.projection, eye == 0 ? leftProjection : rightProjection); .canvas = NULL,
memcpy(camera.viewMatrix, eye == 0 ? leftView : rightView); .viewport = {
lovrGraphicsSetCamera(&camera, eye == 0); { 0, 0, width, height },
state.renderCallback(userdata); { 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); lovrGraphicsSetCamera(NULL, false);
} }

View File

@ -26,9 +26,15 @@ const char* lovrShaderVertexPrefix = ""
"precision mediump float; \n" "precision mediump float; \n"
#else #else
"#version 150 \n" "#version 150 \n"
"#extension GL_NV_viewport_array2 : enable \n"
"#extension GL_NV_stereo_view_rendering : enable \n"
#endif #endif
"#define VERTEX VERTEX \n" "#define VERTEX VERTEX \n"
"#define MAX_BONES 48 \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 lovrPosition; \n"
"in vec3 lovrNormal; \n" "in vec3 lovrNormal; \n"
"in vec2 lovrTexCoord; \n" "in vec2 lovrTexCoord; \n"
@ -38,11 +44,12 @@ const char* lovrShaderVertexPrefix = ""
"in vec4 lovrBoneWeights; \n" "in vec4 lovrBoneWeights; \n"
"out vec2 texCoord; \n" "out vec2 texCoord; \n"
"out vec4 vertexColor; \n" "out vec4 vertexColor; \n"
"uniform int lovrEye; \n"
"uniform mat4 lovrModel; \n" "uniform mat4 lovrModel; \n"
"uniform mat4 lovrView; \n" "uniform mat4 lovrViews[2]; \n"
"uniform mat4 lovrProjection; \n" "uniform mat4 lovrProjections[2]; \n"
"uniform mat4 lovrTransform; \n" "uniform mat4 lovrTransforms[2]; \n"
"uniform mat3 lovrNormalMatrix; \n" "uniform mat3 lovrNormalMatrices[2]; \n"
"uniform mat3 lovrMaterialTransform; \n" "uniform mat3 lovrMaterialTransform; \n"
"uniform float lovrPointSize; \n" "uniform float lovrPointSize; \n"
"uniform mat4 lovrPose[MAX_BONES]; \n" "uniform mat4 lovrPose[MAX_BONES]; \n"
@ -85,6 +92,15 @@ const char* lovrShaderVertexSuffix = ""
" lovrPose[lovrBones[2]] * lovrBoneWeights[2] + \n" " lovrPose[lovrBones[2]] * lovrBoneWeights[2] + \n"
" lovrPose[lovrBones[3]] * lovrBoneWeights[3]; \n" " lovrPose[lovrBones[3]] * lovrBoneWeights[3]; \n"
" gl_PointSize = lovrPointSize; \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" " gl_Position = position(lovrProjection, lovrTransform, pose * vec4(lovrPosition, 1.0)); \n"
"}"; "}";