OpenXR - Integrate multiview into system

This commit is contained in:
Lubos 2022-08-14 18:51:45 +02:00
parent 47349b2c0d
commit ab6c91c203
5 changed files with 163 additions and 62 deletions

View File

@ -110,6 +110,21 @@ static std::string GetInfoLog(GLuint name, Getiv getiv, GetLog getLog) {
return infoLog;
}
int GLQueueRunner::GetStereoBufferIndex(const char *uniformName) {
if (strcmp(uniformName, "u_view") == 0) return 0;
else if (strcmp(uniformName, "u_proj") == 0) return 1;
else return -1;
}
std::string GLQueueRunner::GetStereoBufferLayout(const char *uniformName) {
if (strcmp(uniformName, "u_view") == 0) return "ViewMatrices";
else if (strcmp(uniformName, "u_proj") == 0) return "ProjectionMatrix";
//undefined
assert(false);
return "undefined";
}
void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls) {
if (skipGLCalls) {
// Some bookkeeping still needs to be done.
@ -263,7 +278,25 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
for (size_t j = 0; j < program->queries_.size(); j++) {
auto &query = program->queries_[j];
_dbg_assert_(query.name);
#ifdef OPENXR
int location = -1;
int index = GetStereoBufferIndex(query.name);
if (index) {
std::string layout = GetStereoBufferLayout(query.name);
glUniformBlockBinding(program->program, glGetUniformBlockIndex(program->program, layout.c_str()), index);
GLuint buffer = 0;
glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, location);
glBufferData(GL_UNIFORM_BUFFER,2 * 16 * sizeof(float),NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
location = buffer;
} else {
location = glGetUniformLocation(program->program, query.name);
}
#else
int location = glGetUniformLocation(program->program, query.name);
#endif
if (location < 0 && query.required) {
WARN_LOG(G3D, "Required uniform query for '%s' failed", query.name);
}
@ -997,6 +1030,26 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
CHECK_GL_ERROR_IF_DEBUG();
break;
}
case GLRRenderCommand::UNIFORMSTEREOMATRIX:
{
_dbg_assert_(curProgram);
int loc = c.uniformMatrix4.loc ? *c.uniformMatrix4.loc : -1;
if (c.uniformMatrix4.name) {
loc = curProgram->GetUniformLoc(c.uniformMatrix4.name);
}
if (loc >= 0) {
int size = 2 * 16 * sizeof(float);
GLuint layout = GetStereoBufferIndex(c.uniformMatrix4.name);
glBindBufferBase(GL_UNIFORM_BUFFER, layout, loc);
glBindBuffer(GL_UNIFORM_BUFFER, loc);
void *viewMatrices = glMapBufferRange(GL_UNIFORM_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(viewMatrices, c.uniformMatrix4.m, size);
glUnmapBuffer(GL_UNIFORM_BUFFER);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
CHECK_GL_ERROR_IF_DEBUG();
break;
}
case GLRRenderCommand::UNIFORMMATRIX:
{
_dbg_assert_(curProgram);

View File

@ -47,6 +47,7 @@ enum class GLRRenderCommand : uint8_t {
UNIFORM4UI,
UNIFORM4F,
UNIFORMMATRIX,
UNIFORMSTEREOMATRIX,
TEXTURESAMPLER,
TEXTURELOD,
VIEWPORT,
@ -128,7 +129,7 @@ struct GLRRenderData {
struct {
const char *name; // if null, use loc
const GLint *loc;
float m[16];
float m[32];
} uniformMatrix4;
struct {
uint32_t clearColor;
@ -356,6 +357,9 @@ public:
caps_ = caps;
}
int GetStereoBufferIndex(const char *uniformName);
std::string GetStereoBufferLayout(const char *uniformName);
void RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls);
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls);

View File

@ -742,6 +742,18 @@ public:
curRenderStep_->commands.push_back(data);
}
void SetUniformM4x4Stereo(const GLint *loc, const float *left, const float *right) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
#ifdef _DEBUG
_dbg_assert_(curProgram_);
#endif
GLRRenderData data{ GLRRenderCommand::UNIFORMSTEREOMATRIX };
data.uniformMatrix4.loc = loc;
memcpy(&data.uniformMatrix4.m[0], left, sizeof(float) * 16);
memcpy(&data.uniformMatrix4.m[16], right, sizeof(float) * 16);
curRenderStep_->commands.push_back(data);
}
void SetUniformM4x4(const char *name, const float *udata) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
#ifdef _DEBUG

View File

@ -421,6 +421,10 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
}
WRITE(p, "};\n");
} else {
#ifdef OPENXR
WRITE(p, "#define NUM_VIEWS 2\n");
WRITE(p, "#extension GL_OVR_multiview2 : enable\n");
#endif
if (enableBones) {
const char * const * boneWeightDecl = boneWeightAttrDecl;
if (!strcmp(compat.attribute, "in")) {
@ -469,7 +473,11 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
WRITE(p, "uniform mat4 u_proj_through;\n");
*uniformMask |= DIRTY_PROJTHROUGHMATRIX;
} else if (useHWTransform) {
#ifdef OPENXR
WRITE(p, "layout(shared) uniform ProjectionMatrix { uniform mat4 u_proj; };\n");
#else
WRITE(p, "uniform mat4 u_proj;\n");
#endif
*uniformMask |= DIRTY_PROJMATRIX;
}
@ -477,7 +485,11 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
// When transforming by hardware, we need a great deal more uniforms...
// TODO: Use 4x3 matrices where possible. Though probably doesn't matter much.
WRITE(p, "uniform mat4 u_world;\n");
#ifdef OPENXR
WRITE(p, "layout(shared) uniform ViewMatrices { uniform mat4 u_view; };\n");
#else
WRITE(p, "uniform mat4 u_view;\n");
#endif
*uniformMask |= DIRTY_WORLDMATRIX | DIRTY_VIEWMATRIX;
if (doTextureTransform) {
WRITE(p, "uniform mediump mat4 u_texmtx;\n");
@ -900,13 +912,18 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
WRITE(p, " mediump vec3 worldnormal = normalizeOr001(mul(vec4(skinnednormal, 0.0), u_world).xyz);\n");
}
WRITE(p, " vec4 viewPos = vec4(mul(vec4(worldpos, 1.0), u_view).xyz, 1.0);\n");
std::string matrixPostfix;
#ifdef OPENXR
matrixPostfix = "[gl_ViewID_OVR]";
#endif
WRITE(p, " vec4 viewPos = vec4(mul(vec4(worldpos, 1.0), u_view%s).xyz, 1.0);\n", matrixPostfix.c_str());
// Final view and projection transforms.
if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj, viewPos));\n");
WRITE(p, " vec4 outPos = depthRoundZVP(mul(u_proj%s, viewPos));\n", matrixPostfix.c_str());
} else {
WRITE(p, " vec4 outPos = mul(u_proj, viewPos);\n");
WRITE(p, " vec4 outPos = mul(u_proj%s, viewPos);\n", matrixPostfix.c_str());
}
// TODO: Declare variables for dots for shade mapping if needed.

View File

@ -299,6 +299,56 @@ static inline void ScaleProjMatrix(Matrix4x4 &in, bool useBufferedRendering) {
in.translateAndScale(trans, scale);
}
static inline void FlipProjMatrix(Matrix4x4 &in, bool useBufferedRendering) {
const bool invertedY = useBufferedRendering ? (gstate_c.vpHeight < 0) : (gstate_c.vpHeight > 0);
if (invertedY) {
in[1] = -in[1];
in[5] = -in[5];
in[9] = -in[9];
in[13] = -in[13];
}
const bool invertedX = gstate_c.vpWidth < 0;
if (invertedX) {
in[0] = -in[0];
in[4] = -in[4];
in[8] = -in[8];
in[12] = -in[12];
}
// In Phantasy Star Portable 2, depth range sometimes goes negative and is clamped by glDepthRange to 0,
// causing graphics clipping glitch (issue #1788). This hack modifies the projection matrix to work around it.
if (gstate_c.Supports(GPU_USE_DEPTH_RANGE_HACK)) {
float zScale = gstate.getViewportZScale() / 65535.0f;
float zCenter = gstate.getViewportZCenter() / 65535.0f;
// if far depth range < 0
if (zCenter + zScale < 0.0f) {
// if perspective projection
if (in[11] < 0.0f) {
float depthMax = gstate.getDepthRangeMax() / 65535.0f;
float depthMin = gstate.getDepthRangeMin() / 65535.0f;
float a = in[10];
float b = in[14];
float n = b / (a - 1.0f);
float f = b / (a + 1.0f);
f = (n * f) / (n + ((zCenter + zScale) * (n - f) / (depthMax - depthMin)));
a = (n + f) / (n - f);
b = (2.0f * n * f) / (n - f);
if (!my_isnan(a) && !my_isnan(b)) {
in[10] = a;
in[14] = b;
}
}
}
}
}
void LinkedShader::use(const ShaderID &VSID) {
render_->BindProgram(program);
// Note that we no longer track attr masks here - we do it for the input layouts instead.
@ -355,68 +405,32 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu
// Update any dirty uniforms before we draw
if (dirty & DIRTY_PROJMATRIX) {
Matrix4x4 flippedMatrix;
#ifdef OPENXR
Matrix4x4 leftEyeMatrix, rightEyeMatrix;
if (flatScreen || is2D) {
memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float));
memcpy(&leftEyeMatrix, gstate.projMatrix, 16 * sizeof(float));
memcpy(&rightEyeMatrix, gstate.projMatrix, 16 * sizeof(float));
} else {
VR_TweakProjection(gstate.projMatrix, flippedMatrix.m, VR_PROJECTION_MATRIX_LEFT_EYE);
VR_TweakProjection(gstate.projMatrix, leftEyeMatrix.m, VR_PROJECTION_MATRIX_LEFT_EYE);
VR_TweakProjection(gstate.projMatrix, rightEyeMatrix.m, VR_PROJECTION_MATRIX_RIGHT_EYE);
VR_TweakMirroring(gstate.projMatrix);
}
FlipProjMatrix(leftEyeMatrix, useBufferedRendering);
FlipProjMatrix(rightEyeMatrix, useBufferedRendering);
ScaleProjMatrix(leftEyeMatrix, useBufferedRendering);
ScaleProjMatrix(rightEyeMatrix, useBufferedRendering);
render_->SetUniformM4x4Stereo(&u_proj, leftEyeMatrix.m, rightEyeMatrix.m);
#else
Matrix4x4 flippedMatrix;
memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float));
#endif
const bool invertedY = useBufferedRendering ? (gstate_c.vpHeight < 0) : (gstate_c.vpHeight > 0);
if (invertedY) {
flippedMatrix[1] = -flippedMatrix[1];
flippedMatrix[5] = -flippedMatrix[5];
flippedMatrix[9] = -flippedMatrix[9];
flippedMatrix[13] = -flippedMatrix[13];
}
const bool invertedX = gstate_c.vpWidth < 0;
if (invertedX) {
flippedMatrix[0] = -flippedMatrix[0];
flippedMatrix[4] = -flippedMatrix[4];
flippedMatrix[8] = -flippedMatrix[8];
flippedMatrix[12] = -flippedMatrix[12];
}
// In Phantasy Star Portable 2, depth range sometimes goes negative and is clamped by glDepthRange to 0,
// causing graphics clipping glitch (issue #1788). This hack modifies the projection matrix to work around it.
if (gstate_c.Supports(GPU_USE_DEPTH_RANGE_HACK)) {
float zScale = gstate.getViewportZScale() / 65535.0f;
float zCenter = gstate.getViewportZCenter() / 65535.0f;
// if far depth range < 0
if (zCenter + zScale < 0.0f) {
// if perspective projection
if (flippedMatrix[11] < 0.0f) {
float depthMax = gstate.getDepthRangeMax() / 65535.0f;
float depthMin = gstate.getDepthRangeMin() / 65535.0f;
float a = flippedMatrix[10];
float b = flippedMatrix[14];
float n = b / (a - 1.0f);
float f = b / (a + 1.0f);
f = (n * f) / (n + ((zCenter + zScale) * (n - f) / (depthMax - depthMin)));
a = (n + f) / (n - f);
b = (2.0f * n * f) / (n - f);
if (!my_isnan(a) && !my_isnan(b)) {
flippedMatrix[10] = a;
flippedMatrix[14] = b;
}
}
}
}
FlipProjMatrix(flippedMatrix, useBufferedRendering);
ScaleProjMatrix(flippedMatrix, useBufferedRendering);
render_->SetUniformM4x4(&u_proj, flippedMatrix.m);
#endif
render_->SetUniformF1(&u_rotation, useBufferedRendering ? 0 : (float)g_display_rotation);
}
if (dirty & DIRTY_PROJTHROUGHMATRIX)
@ -523,14 +537,15 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu
}
if (dirty & DIRTY_VIEWMATRIX) {
#ifdef OPENXR
if (flatScreen || is2D) {
SetMatrix4x3(render_, &u_view, gstate.viewMatrix);
} else {
float m4x4[16];
ConvertMatrix4x3To4x4Transposed(m4x4, gstate.viewMatrix);
VR_TweakView(m4x4, gstate.projMatrix, VR_VIEW_MATRIX_LEFT_EYE);
render_->SetUniformM4x4(&u_view, m4x4);
float leftEyeView[16];
float rightEyeView[16];
ConvertMatrix4x3To4x4Transposed(leftEyeView, gstate.viewMatrix);
ConvertMatrix4x3To4x4Transposed(rightEyeView, gstate.viewMatrix);
if (!flatScreen && !is2D) {
VR_TweakView(leftEyeView, gstate.projMatrix, VR_VIEW_MATRIX_LEFT_EYE);
VR_TweakView(rightEyeView, gstate.projMatrix, VR_VIEW_MATRIX_RIGHT_EYE);
}
render_->SetUniformM4x4Stereo(&u_view, leftEyeView, rightEyeView);
#else
SetMatrix4x3(render_, &u_view, gstate.viewMatrix);
#endif