diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index ae664c7d23..64d9884014 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -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 &steps, bool skipGLCalls) { if (skipGLCalls) { // Some bookkeeping still needs to be done. @@ -263,7 +278,25 @@ void GLQueueRunner::RunInitSteps(const std::vector &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); diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index 13c5cfd701..824f6db015 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -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 &steps, bool skipGLCalls); void RunSteps(const std::vector &steps, bool skipGLCalls); diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 9e4fafb50f..7c7f36313e 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -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 diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index 4e3a03e8ba..52483fcb21 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -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. diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 27592296eb..e7c0a336c6 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -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