mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 12:00:26 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aad697ebe |
No files matched your search
@@ -136,3 +136,16 @@ page of buttons shows this layout on the Frame.
|
||||
`debug.xr_deferred_copies` = 0 brings back the old copies for comparing,
|
||||
1 defers them on any runtime; `vr.log_performance` logs the frame's parts
|
||||
and, with the compositor's counters, each copy's time.
|
||||
- 2026-10-05 later: the doubling was the eyes not fusing (each eye sharp
|
||||
alone) with multiview, and gone drawing the eyes one by one, which is now
|
||||
SteamVR's default (`debug.multiview_steamvr` for multiview). The per-eye
|
||||
corrections were checked against the projection (x, y from the top after
|
||||
the flip, the eyes' offsets in clip units) and agree with the Quest, where
|
||||
multiview fuses; the Frame's eyes are wider and differ from each other
|
||||
more, so a driver giving the right eye the left one's correction (zink on
|
||||
Turnip: ViewIndex, Turnip's multi-position output) would show there. For
|
||||
finding it: a self-test of the driver's views when the layers are made
|
||||
(logcat "multiview: self-test"), a frame whose views are turned apart
|
||||
drawn one by one, the views' shape in the log as it changes ("VR views:"),
|
||||
and `debug.gl_environment` ("TU_DEBUG=nomultipos", "ZINK_DEBUG=nobgc",
|
||||
"MESA_SHADER_CACHE_DISABLE=true") set before the driver starts.
|
||||
@@ -205,6 +205,24 @@ static double config_number(const char *path, const char *key, double fallback)
|
||||
return value;
|
||||
}
|
||||
|
||||
/* a string from config.toml into text (size bytes), or "" when it is not
|
||||
set */
|
||||
static void config_text(const char *path, const char *key, char *text, size_t size)
|
||||
{
|
||||
toml_result_t result = toml_parse_file_ex(path);
|
||||
|
||||
text[0] = 0;
|
||||
if (!result.ok)
|
||||
return;
|
||||
{
|
||||
toml_datum_t datum = toml_seek(result.toptab, key);
|
||||
|
||||
if (datum.type == TOML_STRING && datum.u.s)
|
||||
snprintf(text, size, "%s", datum.u.s);
|
||||
}
|
||||
toml_free(result);
|
||||
}
|
||||
|
||||
/* POSIX TZ for the current local offset (the guest's musl has no zone
|
||||
database) */
|
||||
static void time_zone(char *buffer, size_t size)
|
||||
@@ -346,6 +364,25 @@ static void *game_main(void *unused)
|
||||
if (deferred >= 0.0)
|
||||
setenv("HALO_XR_DEFERRED", deferred != 0.0 ? "1" : "0", 1);
|
||||
}
|
||||
{
|
||||
/* (debug.gl_environment: "NAME=value ..." for the GL driver, set
|
||||
before it starts: the Steam Frame's Mesa (zink on Turnip) reads
|
||||
ZINK_DEBUG, TU_DEBUG, MESA_SHADER_CACHE_DISABLE and the like) */
|
||||
char text[512];
|
||||
char *item, *rest = NULL;
|
||||
|
||||
config_text(path, "debug.gl_environment", text, sizeof(text));
|
||||
for (item = strtok_r(text, " ;", &rest); item; item = strtok_r(NULL, " ;", &rest))
|
||||
{
|
||||
char *equals = strchr(item, '=');
|
||||
|
||||
if (!equals || equals == item)
|
||||
continue;
|
||||
*equals = 0;
|
||||
setenv(item, equals + 1, 1);
|
||||
host_logf(HOST_LOG_INFO, "GL driver environment: %s=%s (debug.gl_environment)", item, equals + 1);
|
||||
}
|
||||
}
|
||||
{
|
||||
int eye_width, eye_height;
|
||||
|
||||
|
||||
@@ -918,6 +918,7 @@ static struct
|
||||
/* what the game projects itself has its depth in z though not depth
|
||||
tested (lens flares: d3d8_multiview_projected_depth) */
|
||||
BOOL projected_depth;
|
||||
BOOL tested; /* multiview_self_test */
|
||||
} multiview;
|
||||
|
||||
/* the bound targets are the layers */
|
||||
@@ -965,6 +966,102 @@ int d3d8_multiview_supported(void)
|
||||
return device.gl_ready && xgpu_capabilities.multiview;
|
||||
}
|
||||
|
||||
/* Whether the driver gives each view its own correction, as the game's
|
||||
vertex programs take it (nv2a_vsh.c: a row of an array uniform picked by
|
||||
gl_ViewID_OVR): a point drawn into two 2x1 layers, the left pixel's in view
|
||||
0 and the right one's in view 1 by their rows, then read back. A driver
|
||||
that gave both views the first one's would draw the right eye as the left
|
||||
eye sees, which no two eyes fuse. Once, as the layers are first made. */
|
||||
static BOOL multiview_self_test(void)
|
||||
{
|
||||
static const char vertex_source[] =
|
||||
"#version 300 es\n"
|
||||
"#extension GL_OVR_multiview2 : require\n"
|
||||
"layout(num_views = 2) in;\n"
|
||||
"uniform vec4 rows[4];\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
"\tint row = int(gl_ViewID_OVR) * 2;\n"
|
||||
"\tvec4 position = vec4(0.0, 0.0, 0.5, 1.0);\n"
|
||||
"\tposition.x = dot(vec3(position.xw, 1.0), rows[row].xyz);\n"
|
||||
"\tgl_Position = position;\n"
|
||||
"\tgl_PointSize = 1.0;\n"
|
||||
"}\n";
|
||||
static const char fragment_source[] =
|
||||
"#version 300 es\n"
|
||||
"precision mediump float;\n"
|
||||
"out vec4 color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
"\tcolor = vec4(1.0);\n"
|
||||
"}\n";
|
||||
/* (x: view 0 to the left pixel's centre, view 1 to the right one's) */
|
||||
static const float rows[4][4] =
|
||||
{
|
||||
{ 1.0f, 0.0f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 0.0f },
|
||||
{ 1.0f, 0.0f, 0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f, 0.0f },
|
||||
};
|
||||
static const GLenum draw_buffer = GL_COLOR_ATTACHMENT0;
|
||||
GLuint vertex = compile_shader(GL_VERTEX_SHADER, vertex_source, "multiview test vertex");
|
||||
GLuint fragment = compile_shader(GL_FRAGMENT_SHADER, fragment_source, "multiview test fragment");
|
||||
GLuint program, texture, framebuffer;
|
||||
GLint linked = 0;
|
||||
unsigned char pixels[2][8];
|
||||
BOOL passed;
|
||||
int layer;
|
||||
|
||||
if (!vertex || !fragment)
|
||||
return TRUE;
|
||||
program = glCreateProgram();
|
||||
glAttachShader(program, vertex);
|
||||
glAttachShader(program, fragment);
|
||||
glLinkProgram(program);
|
||||
glDeleteShader(vertex);
|
||||
glDeleteShader(fragment);
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||||
if (!linked)
|
||||
{
|
||||
platform_log("multiview: the self-test's program does not link; not tested");
|
||||
return TRUE;
|
||||
}
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
|
||||
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 2, 1, 2);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0, 0, 2);
|
||||
glDrawBuffers(1, &draw_buffer);
|
||||
glViewport(0, 0, 2, 1);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glUseProgram(program);
|
||||
glUniform4fv(glGetUniformLocation(program, "rows"), 4, &rows[0][0]);
|
||||
glBindVertexArray(device.vertex_array);
|
||||
glDrawArrays(GL_POINTS, 0, 1);
|
||||
memset(pixels, 0, sizeof(pixels));
|
||||
for (layer = 0; layer < 2; layer++)
|
||||
{
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0, layer);
|
||||
glReadPixels(0, 0, 2, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels[layer]);
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &framebuffer);
|
||||
glDeleteTextures(1, &texture);
|
||||
xgpu_gl_state_invalidate();
|
||||
/* (view 0 lit only the left pixel, view 1 only the right one) */
|
||||
passed = pixels[0][0] > 128 && pixels[0][4] < 128 && pixels[1][0] < 128 && pixels[1][4] > 128;
|
||||
platform_log("multiview: self-test %s (view 0 drew %d %d, view 1 %d %d: each its own pixel)",
|
||||
passed ? "passed" : "FAILED", pixels[0][0], pixels[0][4], pixels[1][0], pixels[1][4]);
|
||||
return passed;
|
||||
}
|
||||
|
||||
/* rows: per view, the correction of x and of y (nv2a_vsh.c); resolve_rect:
|
||||
where each eye's picture lies in the shared camera's */
|
||||
int d3d8_multiview_begin(const float rows[4][4], const float resolve_rect[2][4])
|
||||
@@ -973,6 +1070,16 @@ int d3d8_multiview_begin(const float rows[4][4], const float resolve_rect[2][4])
|
||||
|
||||
if (!d3d8_multiview_supported() || !(back_buffer = render_target_get(&device.back_buffer)))
|
||||
return FALSE;
|
||||
if (!multiview.tested)
|
||||
{
|
||||
/* (failed: the eyes drawn one by one from now on, vr.c) */
|
||||
multiview.tested = TRUE;
|
||||
if (!multiview_self_test())
|
||||
{
|
||||
xgpu_capabilities.multiview = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (!multiview.framebuffer || multiview.width != back_buffer->target.gl_width ||
|
||||
multiview.height != back_buffer->target.gl_height)
|
||||
{
|
||||
|
||||
@@ -512,6 +512,10 @@ static const struct config_setting config_settings[] =
|
||||
{ "debug.gl_thread", _config_integer, "-1", "HALO_GL_THREAD", _environment_value, _platform_android,
|
||||
"Mesa's GL thread (its drivers: the Steam Frame's): 1 on, 0 off, -1 as the\n"
|
||||
"driver chooses. Read when the app starts." },
|
||||
{ "debug.gl_environment", _config_string, "\"\"", "HALO_GL_ENVIRONMENT", _environment_value, _platform_android,
|
||||
"Variables for the GL driver, \"NAME=value NAME2=value\", set before it starts\n"
|
||||
"(the Steam Frame's Mesa: ZINK_DEBUG, TU_DEBUG, ...), for trying. Read when\n"
|
||||
"the app starts." },
|
||||
{ "debug.xr_deferred_copies", _config_integer, "-1", "HALO_XR_DEFERRED", _environment_value, _platform_android,
|
||||
"The headset's layer copies: 1 deferred (no waits for the GL thread or the\n"
|
||||
"GPU in the middle of the frame), 0 each one restored and released at once,\n"
|
||||
|
||||
+58
-1
@@ -1447,6 +1447,63 @@ static int multiview_drawing;
|
||||
about the camera, as though infinitely far: no eye stands aside of it */
|
||||
static float multiview_rows[2][4][4];
|
||||
|
||||
/* The one camera between the eyes holds only while their views look the
|
||||
same way (the corrections move each eye's picture aside, and scale it, but
|
||||
cannot turn it): a frame whose views are turned apart, which a runtime may
|
||||
report after the start (an eye's calibration), draws the eyes one by one.
|
||||
The views' shape and how far apart they turn go to the log as they change. */
|
||||
#define MULTIVIEW_PARALLEL_DEGREES 0.05f
|
||||
|
||||
static int views_parallel(void)
|
||||
{
|
||||
static const float local_forward[3] = { 0.0f, 0.0f, -1.0f };
|
||||
static const float local_up[3] = { 0.0f, 1.0f, 0.0f };
|
||||
static float logged[HALO_XR_EYE_COUNT][4];
|
||||
static float logged_degrees = -1.0f;
|
||||
static unsigned long long logged_us;
|
||||
const struct halo_xr_view *views = vr.frame.views;
|
||||
float forward[2][3], up[2][3], along, degrees;
|
||||
int eye, changed = 0;
|
||||
unsigned long long now;
|
||||
|
||||
for (eye = 0; eye < HALO_XR_EYE_COUNT; eye++)
|
||||
{
|
||||
quaternion_rotate(views[eye].pose.orientation, local_forward, forward[eye]);
|
||||
quaternion_rotate(views[eye].pose.orientation, local_up, up[eye]);
|
||||
}
|
||||
along = fminf(forward[0][0] * forward[1][0] + forward[0][1] * forward[1][1] + forward[0][2] * forward[1][2],
|
||||
up[0][0] * up[1][0] + up[0][1] * up[1][1] + up[0][2] * up[1][2]);
|
||||
degrees = acosf(fminf(1.0f, along)) * 180.0f / PI;
|
||||
|
||||
for (eye = 0; eye < HALO_XR_EYE_COUNT; eye++)
|
||||
{
|
||||
const float shape[4] = { views[eye].tangent_left, views[eye].tangent_right, views[eye].tangent_down,
|
||||
views[eye].tangent_up };
|
||||
int edge;
|
||||
|
||||
for (edge = 0; edge < 4; edge++)
|
||||
changed |= fabsf(shape[edge] - logged[eye][edge]) > 0.005f;
|
||||
}
|
||||
changed |= fabsf(degrees - logged_degrees) > 0.02f;
|
||||
now = now_us();
|
||||
if (changed && now - logged_us > 5000000ULL)
|
||||
{
|
||||
platform_log("VR views: %.3f degrees apart; left eye %.3f..%.3f, %.3f..%.3f; right eye %.3f..%.3f, %.3f..%.3f",
|
||||
degrees, views[0].tangent_left, views[0].tangent_right, views[0].tangent_down, views[0].tangent_up,
|
||||
views[1].tangent_left, views[1].tangent_right, views[1].tangent_down, views[1].tangent_up);
|
||||
for (eye = 0; eye < HALO_XR_EYE_COUNT; eye++)
|
||||
{
|
||||
logged[eye][0] = views[eye].tangent_left;
|
||||
logged[eye][1] = views[eye].tangent_right;
|
||||
logged[eye][2] = views[eye].tangent_down;
|
||||
logged[eye][3] = views[eye].tangent_up;
|
||||
}
|
||||
logged_degrees = degrees;
|
||||
logged_us = now;
|
||||
}
|
||||
return degrees <= MULTIVIEW_PARALLEL_DEGREES;
|
||||
}
|
||||
|
||||
int halo_vr_multiview(void)
|
||||
{
|
||||
int available = vr.active && vr.multiview && d3d8_multiview_supported();
|
||||
@@ -1456,7 +1513,7 @@ int halo_vr_multiview(void)
|
||||
platform_log("VR: both eyes %s", available ? "in one draw (multiview)" : "drawn one by one (no multiview)");
|
||||
vr.multiview_logged = 1;
|
||||
}
|
||||
return available && halo_vr_stereo_frame();
|
||||
return available && halo_vr_stereo_frame() && views_parallel();
|
||||
}
|
||||
|
||||
void halo_vr_center_camera(const float game_position[3], float position[3], float forward[3], float up[3],
|
||||
|
||||
Reference in new issue
Block a user