mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
VR: Fix inverted depth and GL gamma, keep the flat screen upright
- Depth was inverted on both backends: the VR shader replaced Z with the game's clip-space Z after the viewport transform, skipping its Z scale. Since the viewport transform moved into the shader, that's wrong. Replace it before the transform instead, as libretro already did. - GL eye buffers are sRGB, and GLES encoded our sRGB colors again (too bright). Disable the encoding with GL_EXT_sRGB_write_control. - Keep the flat screen upright instead of tilting it to the head's pitch at recenter time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
abe2203657
commit
249b653962
4 files changed
+47
-14
No files matched your search
@@ -3,6 +3,29 @@
|
||||
#if XR_USE_GRAPHICS_API_OPENGL || XR_USE_GRAPHICS_API_OPENGL_ES
|
||||
|
||||
#include "Common/GPU/OpenGL/GLCommon.h"
|
||||
#include <cstring>
|
||||
|
||||
#ifndef GL_FRAMEBUFFER_SRGB_EXT
|
||||
#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9
|
||||
#endif
|
||||
|
||||
// GLFeatures.h clashes with the EGL macros the OpenXR headers bring in, so this asks GL itself.
|
||||
static bool HasSRGBWriteControl() {
|
||||
static int has = -1;
|
||||
if (has < 0) {
|
||||
GLint count = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &count);
|
||||
has = 0;
|
||||
for (GLint i = 0; i < count; i++) {
|
||||
const char *ext = (const char *)glGetStringi(GL_EXTENSIONS, i);
|
||||
if (ext && !strcmp(ext, "GL_EXT_sRGB_write_control")) {
|
||||
has = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return has == 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -267,6 +290,11 @@ void* ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) {
|
||||
#if XR_USE_GRAPHICS_API_OPENGL_ES || XR_USE_GRAPHICS_API_OPENGL
|
||||
if (UseGL()) {
|
||||
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->GLFrameBuffers[frameBuffer->TextureSwapChainIndex]));
|
||||
// The swapchain is sRGB and our colors are sRGB-encoded already: GLES would encode them again on
|
||||
// write (too bright). Context state, but the other framebuffers aren't sRGB, so it only affects this.
|
||||
if (HasSRGBWriteControl()) {
|
||||
GL(glDisable(GL_FRAMEBUFFER_SRGB_EXT));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
|
||||
@@ -206,8 +206,9 @@ void VR_Recenter(engine_t* engine) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update menu orientation
|
||||
VR_SetConfigFloat(VR_CONFIG_MENU_PITCH, hmdorientation.x);
|
||||
// Update menu orientation. The screen stays upright: tilting it to the head's pitch at recenter
|
||||
// time (often looking down while putting the headset on) left it at an angle for the session.
|
||||
VR_SetConfigFloat(VR_CONFIG_MENU_PITCH, 0.0f);
|
||||
VR_SetConfigFloat(VR_CONFIG_MENU_YAW, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
@@ -544,11 +544,10 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY)) {
|
||||
WRITE(p, " vec4 outPos = mul(u_proj_lens, viewPos);\n");
|
||||
WRITE(p, " vec4 orgPos = mul(u_proj, viewPos);\n");
|
||||
#ifdef __LIBRETRO__
|
||||
// Preserve game depth before clipping and viewport conversion. The VR lens
|
||||
// changes XY, but depth must still agree with through-mode draws and clears.
|
||||
// changes XY, but depth must still agree with through-mode draws and clears,
|
||||
// and go through the viewport's Z scale (often negative on the PSP).
|
||||
WRITE(p, " outPos.z = orgPos.z * (outPos.w / orgPos.w);\n");
|
||||
#endif
|
||||
} else {
|
||||
WRITE(p, " vec4 outPos = mul(u_proj, viewPos);\n");
|
||||
}
|
||||
@@ -1020,13 +1019,6 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
|
||||
WRITE(p, " %sgl_Position = outPos;\n", compat.vsOutPrefix);
|
||||
|
||||
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY)) {
|
||||
#ifndef __LIBRETRO__
|
||||
// Z correction for the depth buffer
|
||||
if (useHWTransform) {
|
||||
WRITE(p, " %sgl_Position.z = orgPos.z / abs(orgPos.w) * abs(outPos.w);\n", compat.vsOutPrefix);
|
||||
}
|
||||
#endif
|
||||
|
||||
// HUD scaling
|
||||
WRITE(p, " %sgl_Position.x *= u_scaleX;\n", compat.vsOutPrefix);
|
||||
WRITE(p, " %sgl_Position.y *= u_scaleY;\n", compat.vsOutPrefix);
|
||||
|
||||
@@ -74,7 +74,7 @@ only on dirty split GTA's scene into two layers moving at different speeds.
|
||||
|
||||
### The OpenGL-only parts, and their Vulkan versions
|
||||
|
||||
OpenGL VR goes once these are confirmed on a headset:
|
||||
OpenGL VR stays an option for a while. What it does its own way, and the Vulkan equivalent:
|
||||
|
||||
- **Skyplane** (`compatvr.ini`, the GTA Stories games): `PreprocessSkyplane` edits GL render steps at
|
||||
replay, `PreprocessSkyplaneVulkan` does the same to Vulkan steps in `PreprocessSteps`, before
|
||||
@@ -95,6 +95,17 @@ The other VR compat flags (`ForceMono`, `ForceFlatScreen`, `IdentityViewHack`, `
|
||||
`ProjectionHack`, `UnitsPerMeter`) are read in shared code (`PPSSPPVR.cpp`, the shader generator),
|
||||
so they don't depend on the backend.
|
||||
|
||||
### Fixes found on the headset
|
||||
|
||||
- **Depth** was inverted, on GL too: the shader replaced the lens projection's Z with the game's
|
||||
(`orgPos`) at the very end, skipping the viewport's Z scale, which broke when the viewport transform
|
||||
moved into the shader. It's replaced before the transform now (libretro already did this).
|
||||
- **Gamma on GL:** the GL eye swapchains are `GL_SRGB8_ALPHA8`, and GLES encodes writes to an sRGB
|
||||
framebuffer, so our already sRGB-encoded colors came out too bright. Writes skip the encoding now
|
||||
(`GL_EXT_sRGB_write_control`).
|
||||
- **The flat screen** stays upright. It used to take the head's pitch at recenter time, often looking
|
||||
down while putting the headset on. Recentering is the system gesture (hold the Meta button).
|
||||
|
||||
## Phase 2: PC VR
|
||||
|
||||
The desktop window (Windows, then Linux) stays the primary swapchain and shows a mirror. The
|
||||
@@ -105,5 +116,6 @@ becomes the non-pacing one. The OpenXR loader on desktop is our dynamic one (`Op
|
||||
|
||||
## Status
|
||||
|
||||
- [ ] Phase 1
|
||||
- [x] Phase 1: runs on a Quest 2 (menus, Burnout Legends, GTA LCS in flat mode, stereo and 6DoF on)
|
||||
at 72 fps. Skyplane not checked specifically yet.
|
||||
- [ ] Phase 2
|
||||
Reference in new issue
Block a user