Files
mitch030504--AstroQuest-Frame/quest-host/cpp/gl_frames.h
T
bigmak94andClaude Opus 5.5 d96618af1d Quest app 0.14 to 0.17: a field-of-view menu at the start, counted from the headset's own view
Work of 2026-10-04, committed as it was (README-QUEST-VR.md tells each step):

- 0.14: every start shows a panel in the headset to choose how much of the field of view
  the game draws (left and right on the D-pad or the left stick, X to play). Less is the
  same pixels over fewer degrees, a sharper picture; the choice is remembered. Below all of
  it, each eye gets a soft-edged squircle border, drawn where the host copies the frame.
- 0.15: the percent is of the headset's own view, read from OpenXR before Play is offered
  and handed to the emulator before the game starts (SHADPS4_VR_HEADSET_FOV_TAN), 50 to 100.
- 0.16: the border sits around each eye's straight ahead, which a headset's uneven view
  does not have in the middle of the picture.
- 0.17: both eyes draw the same, even view around straight ahead that takes in the
  headset's own (SHADPS4_VR_FOV_SYMMETRIC), so that what one eye shows the other shows too
  when the view is narrowed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:15:43 +02:00

87 lines
3.0 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// The GL side of showing the emulator's frames, shared by the OpenXR host and by the self test
// that exercises the same path without a headset session.
#include <cstdint>
#include <vector>
// Extension entry points (EGL images over hardware buffers) are only declared on request.
#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
// The extension header relies on the types the one above defines.
#include <GLES2/gl2ext.h>
struct AHardwareBuffer;
class CoreProcess;
/// An OpenGL ES 3 context that draws to nothing: everything is rendered to textures.
struct GlContext {
EGLDisplay display{EGL_NO_DISPLAY};
EGLConfig config{};
EGLContext context{EGL_NO_CONTEXT};
EGLSurface surface{EGL_NO_SURFACE};
bool Create();
void Destroy();
};
/// The images the emulator renders into: hardware buffers handed to it as dma-bufs, each of them
/// also a GL texture here, so that a frame gets from one process to the other without a copy.
class FrameBuffers {
public:
static constexpr uint32_t Count = 3;
/// Allocates the buffers and offers them to the core. Needs a current GL context.
bool Create(EGLDisplay display, uint32_t width, uint32_t height, CoreProcess& core);
void Destroy(EGLDisplay display);
bool IsValid(uint32_t index) const {
return index < textures.size();
}
GLuint Texture(uint32_t index) const {
return textures[index];
}
private:
std::vector<AHardwareBuffer*> buffers;
std::vector<EGLImageKHR> images;
std::vector<GLuint> textures;
};
/// Copies a frame to the framebuffer that is bound, upside up and in the right color encoding.
class FrameBlitter {
public:
bool Create();
void Destroy();
/// `srgb_target` is for sRGB targets: they encode whatever is written to them, while the
/// emulator delivers values that are display-ready already. Where the GPU can be told to
/// leave that encoding out, the frame is written as it is; elsewhere it is decoded first,
/// which costs a power function per pixel.
/// `reduced_fov` rounds each eye's boundary into a feathered squircle. The game already
/// rendered the narrower projection; masking its corners preserves its pixel density.
/// `fov` is that frame's out/in/up/down tangents: the aperture follows each eye's optical
/// axis, which need not be at the centre of an asymmetric projection's texture.
void Draw(GLuint texture, uint32_t width, uint32_t height, bool swap_red_blue,
bool srgb_target, bool reduced_fov = false, const float* fov = nullptr) const;
/// Whether frames go into sRGB targets unchanged (see Draw).
bool WritesUnencoded() const {
return write_control;
}
private:
GLuint program{};
GLuint vertex_array{};
GLint uniform_swap{-1};
GLint uniform_decode{-1};
GLint uniform_mask{-1};
GLint uniform_fov{-1};
bool write_control{};
};