feat: Add Lynx headset support (#1823)

* feat:  Add Lynx headset support

* Disable srgb correction
This commit is contained in:
Riccardo Zaglia 2023-09-07 18:10:31 +08:00 committed by GitHub
parent 2e928913b3
commit 057fce4acc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 140 additions and 118 deletions

View File

@ -15,6 +15,7 @@ This is a fork of [ALVR](https://github.com/polygraphene/ALVR).
| Pico 4/Neo 3 | :heavy_check_mark: |
| Vive Focus 3/XR Elite | :heavy_check_mark: |
| YVR 1/2 | :heavy_check_mark: |
| Lynx R1 | :heavy_check_mark: |
| Google Cardboard | :heavy_check_mark: ([PhoneVR](https://github.com/PhoneVR-Developers/PhoneVR)) |
| Smartphone/Monado | :construction: * |
| GearVR | :construction: * (maybe) |

View File

@ -22,6 +22,7 @@ struct FfiStreamConfig {
float foveationCenterShiftY;
float foveationEdgeRatioX;
float foveationEdgeRatioY;
unsigned int enableSrgbCorrection;
};
// gltf_model.h

View File

@ -62,7 +62,7 @@ typedef struct {
enum ovrProgramType {
STREAMER_PROG,
LOBBY_PROG,
MAX_PROGS // Not to be used as a type, just a placeholder for len
MAX_PROGS // Not to be used as a type, just a placeholder for len
};
typedef struct {
@ -95,12 +95,11 @@ typedef struct {
} ovrVertexAttribute;
ovrVertexAttribute ProgramVertexAttributes[] = {
{VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition", {true, true }},
{VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor", {true, false}},
{VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv", {true, true }},
{VERTEX_ATTRIBUTE_LOCATION_TRANSFORM, "vertexTransform", {false, false}},
{VERTEX_ATTRIBUTE_LOCATION_NORMAL, "vertexNormal", {false, true }}
};
{VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition", {true, true}},
{VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor", {true, false}},
{VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv", {true, true}},
{VERTEX_ATTRIBUTE_LOCATION_TRANSFORM, "vertexTransform", {false, false}},
{VERTEX_ATTRIBUTE_LOCATION_NORMAL, "vertexNormal", {false, true}}};
enum E1test {
UNIFORM_VIEW_ID,
@ -428,7 +427,10 @@ void ovrGeometry_DestroyVAO(ovrGeometry *geometry) {
static const char *programVersion = "#version 300 es\n";
bool ovrProgram_Create(ovrProgram *program, const char *vertexSource, const char *fragmentSource, ovrProgramType progType) {
bool ovrProgram_Create(ovrProgram *program,
const char *vertexSource,
const char *fragmentSource,
ovrProgramType progType) {
GLint r;
LOGI("Compiling shaders.");
@ -468,12 +470,16 @@ bool ovrProgram_Create(ovrProgram *program, const char *vertexSource, const char
// Bind the vertex attribute locations.
for (size_t i = 0; i < sizeof(ProgramVertexAttributes) / sizeof(ProgramVertexAttributes[0]);
i++) {
// Only bind vertex attributes which are used/active in shader else causes uncessary bugs via compiler optimization/aliasing
// Only bind vertex attributes which are used/active in shader else causes uncessary bugs
// via compiler optimization/aliasing
if (ProgramVertexAttributes[i].usedInProg[progType]) {
GL(glBindAttribLocation(program->streamProgram,
ProgramVertexAttributes[i].location,
ProgramVertexAttributes[i].name));
LOGD("Binding ProgramVertexAttribute [id.%d] %s to location %d", i, ProgramVertexAttributes[i].name, ProgramVertexAttributes[i].location);
ProgramVertexAttributes[i].location,
ProgramVertexAttributes[i].name));
LOGD("Binding ProgramVertexAttribute [id.%d] %s to location %d",
i,
ProgramVertexAttributes[i].name,
ProgramVertexAttributes[i].location);
}
}
@ -551,19 +557,22 @@ void ovrRenderer_Create(ovrRenderer *renderer,
int hudTexture,
std::vector<GLuint> textures[2],
FFRData ffrData,
bool isLobby) {
bool isLobby,
bool enableSrgbCorrection) {
if (!isLobby) {
renderer->srgbCorrectionPass = std::make_unique<SrgbCorrectionPass>(streamTexture);
renderer->enableFFR = ffrData.enabled;
if (renderer->enableFFR) {
FoveationVars fv = CalculateFoveationVars(ffrData);
renderer->srgbCorrectionPass->Initialize(fv.optimizedEyeWidth, fv.optimizedEyeHeight);
renderer->srgbCorrectionPass->Initialize(
fv.optimizedEyeWidth, fv.optimizedEyeHeight, !enableSrgbCorrection);
renderer->ffr = std::make_unique<FFR>(renderer->srgbCorrectionPass->GetOutputTexture());
renderer->ffr->Initialize(fv);
renderer->streamRenderTexture = renderer->ffr->GetOutputTexture()->GetGLTexture();
} else {
renderer->srgbCorrectionPass->Initialize(width, height);
renderer->streamRenderTexture = renderer->srgbCorrectionPass->GetOutputTexture()->GetGLTexture();
renderer->srgbCorrectionPass->Initialize(width, height, !enableSrgbCorrection);
renderer->streamRenderTexture =
renderer->srgbCorrectionPass->GetOutputTexture()->GetGLTexture();
}
}
@ -580,7 +589,8 @@ void ovrRenderer_Create(ovrRenderer *renderer,
ovrProgram_Create(&renderer->streamProgram, VERTEX_SHADER, FRAGMENT_SHADER, STREAMER_PROG);
ovrProgram_Create(&renderer->lobbyProgram, LOBBY_VERTEX_SHADER, LOBBY_FRAGMENT_SHADER, LOBBY_PROG);
ovrProgram_Create(
&renderer->lobbyProgram, LOBBY_VERTEX_SHADER, LOBBY_FRAGMENT_SHADER, LOBBY_PROG);
ovrGeometry_CreatePanel(&renderer->Panel);
ovrGeometry_CreateVAO(&renderer->Panel);
@ -734,7 +744,7 @@ void initGraphicsNative() {
g_ctx.streamTexture = std::make_unique<Texture>(false, 0, true);
g_ctx.hudTexture = std::make_unique<Texture>(
false, 0, false, 1280, 720, GL_RGBA8, GL_RGBA, std::vector<uint8_t>(1280 * 720 * 4, 0));
const GLubyte *sVendor, *sRenderer, *sVersion, *sExts;
GL(sVendor = glGetString(GL_VENDOR));
@ -747,10 +757,14 @@ void initGraphicsNative() {
}
void destroyGraphicsNative() {
LOGV("Resetting stream texture and hud texture %p, %p", g_ctx.streamTexture.get(), g_ctx.hudTexture.get());
LOGV("Resetting stream texture and hud texture %p, %p",
g_ctx.streamTexture.get(),
g_ctx.hudTexture.get());
g_ctx.streamTexture.reset();
g_ctx.hudTexture.reset();
LOGV("Resetted stream texture and hud texture to %p, %p", g_ctx.streamTexture.get(), g_ctx.hudTexture.get());
LOGV("Resetted stream texture and hud texture to %p, %p",
g_ctx.streamTexture.get(),
g_ctx.hudTexture.get());
}
// on resume
@ -774,7 +788,8 @@ void prepareLobbyRoom(int viewWidth,
g_ctx.hudTexture->GetGLTexture(),
g_ctx.lobbySwapchainTextures,
{false},
true);
true,
false);
}
// on pause
@ -819,7 +834,8 @@ void streamStartNative(FfiStreamConfig config) {
config.foveationCenterShiftY,
config.foveationEdgeRatioX,
config.foveationEdgeRatioY},
false);
false,
config.enableSrgbCorrection);
}
void updateLobbyHudTexture(const unsigned char *data) {
@ -838,14 +854,14 @@ void renderLobbyNative(const FfiViewInput eyeInputs[2]) {
if (!g_ctx.hudTextureBitmap.empty()) {
GL(glBindTexture(GL_TEXTURE_2D, g_ctx.hudTexture->GetGLTexture()));
GL(glTexSubImage2D(GL_TEXTURE_2D,
0,
0,
0,
HUD_TEXTURE_WIDTH,
HUD_TEXTURE_HEIGHT,
GL_RGBA,
GL_UNSIGNED_BYTE,
&g_ctx.hudTextureBitmap[0]));
0,
0,
0,
HUD_TEXTURE_WIDTH,
HUD_TEXTURE_HEIGHT,
GL_RGBA,
GL_UNSIGNED_BYTE,
&g_ctx.hudTextureBitmap[0]));
}
g_ctx.hudTextureBitmap.clear();
}

View File

@ -32,15 +32,29 @@ const string SRGB_CORRECTION_FRAGMENT_SHADER = R"glsl(#version 300 es
color.rgb = condition * lowValues + (1.0 - condition) * highValues;
}
)glsl";
}
const string PASSTHOUGH_FRAGMENT_SHADER = R"glsl(#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision mediump float;
uniform samplerExternalOES tex0;
in vec2 uv;
out vec4 color;
void main()
{
color = texture(tex0, uv);
}
)glsl";
} // namespace
SrgbCorrectionPass::SrgbCorrectionPass(Texture *inputSurface) : mInputSurface(inputSurface) {}
void SrgbCorrectionPass::Initialize(uint32_t width, uint32_t height) {
void SrgbCorrectionPass::Initialize(uint32_t width, uint32_t height, bool passthrough) {
mOutputTexture.reset(new Texture(false, 0, false, width * 2, height));
mOutputTextureState = make_unique<RenderState>(mOutputTexture.get());
auto fragmentShader = SRGB_CORRECTION_FRAGMENT_SHADER;
auto fragmentShader =
passthrough ? PASSTHOUGH_FRAGMENT_SHADER : SRGB_CORRECTION_FRAGMENT_SHADER;
mStagingPipeline = unique_ptr<RenderPipeline>(
new RenderPipeline({mInputSurface}, QUAD_2D_VERTEX_SHADER, fragmentShader));
}

View File

@ -9,7 +9,7 @@ class SrgbCorrectionPass {
public:
SrgbCorrectionPass(gl_render_utils::Texture *inputSurface);
void Initialize(uint32_t width, uint32_t height);
void Initialize(uint32_t width, uint32_t height, bool passthrough);
void Render() const;

View File

@ -535,7 +535,12 @@ pub unsafe extern "C" fn alvr_start_stream_opengl(config: AlvrStreamConfig) {
edge_ratio_y: config.foveation_edge_ratio_y,
});
opengl::start_stream(view_resolution, swapchain_textures, foveated_rendering);
opengl::start_stream(
view_resolution,
swapchain_textures,
foveated_rendering,
true,
);
}
#[no_mangle]

View File

@ -71,6 +71,7 @@ pub fn start_stream(
view_resolution: UVec2,
swapchain_textures: [Vec<u32>; 2],
foveated_rendering: Option<FoveatedRenderingConfig>,
enable_srgb_correction: bool,
) {
#[cfg(target_os = "android")]
unsafe {
@ -107,6 +108,7 @@ pub fn start_stream(
.as_ref()
.map(|f| f.edge_ratio_y)
.unwrap_or_default(),
enableSrgbCorrection: enable_srgb_correction as u32,
};
streamStartNative(config);

View File

@ -155,4 +155,12 @@ value = "1"
# Yvr entries
[[package.metadata.android.application.meta_data]]
name = "com.yvr.intent.category.VR"
value = "vr_only"
value = "vr_only"
# Lynx entries
[[package.metadata.android.queries.intent]]
actions = ["org.khronos.openxr.OpenXRRuntimeService"]
[[package.metadata.android.queries.package]]
name = "com.ultraleap.tracking.service"
[[package.metadata.android.queries.package]]
name = "com.ultraleap.openxr.api_layer"

View File

@ -45,7 +45,7 @@ pub fn initialize_hands_interaction(
Platform::Pico4 => PICO4_CONTROLLER_PROFILE_PATH,
Platform::Focus3 => FOCUS3_CONTROLLER_PROFILE_PATH,
Platform::Yvr => YVR_CONTROLLER_PROFILE_PATH,
Platform::Other => QUEST_CONTROLLER_PROFILE_PATH,
_ => QUEST_CONTROLLER_PROFILE_PATH,
};
let interaction_profile_id = alvr_common::hash_string(interaction_profile_path);

View File

@ -35,6 +35,7 @@ pub enum Platform {
Pico4,
Focus3,
Yvr,
Lynx,
Other,
}
@ -350,29 +351,30 @@ fn update_streaming_input(ctx: &mut StreamingInputContext) {
pub fn entry_point() {
alvr_client_core::init_logging();
let platform = match (
alvr_client_core::manufacturer_name().as_str(),
alvr_client_core::device_model().as_str(),
) {
let manufacturer_name = alvr_client_core::manufacturer_name();
let device_model = alvr_client_core::device_model();
info!("Manufacturer: {manufacturer_name}, device model: {device_model}");
let platform = match (manufacturer_name.as_str(), device_model.as_str()) {
("Oculus", _) => Platform::Quest,
("Pico", "Pico Neo 3") => Platform::PicoNeo3,
("Pico", _) => Platform::Pico4,
("HTC", _) => Platform::Focus3,
("YVR", _) => Platform::Yvr,
("Lynx Mixed Reality", _) => Platform::Lynx,
_ => Platform::Other,
};
let xr_entry = match platform {
Platform::Quest => unsafe {
xr::Entry::load_from(Path::new("libopenxr_loader_quest.so")).unwrap()
},
Platform::PicoNeo3 | Platform::Pico4 => unsafe {
xr::Entry::load_from(Path::new("libopenxr_loader_pico.so")).unwrap()
},
Platform::Yvr => unsafe {
xr::Entry::load_from(Path::new("libopenxr_loader_yvr.so")).unwrap()
},
_ => unsafe { xr::Entry::load().unwrap() },
let loader_suffix = match platform {
Platform::Quest => "quest",
Platform::PicoNeo3 | Platform::Pico4 => "pico",
Platform::Yvr => "yvr",
Platform::Lynx => "lynx",
_ => "generic",
};
let xr_entry = unsafe {
xr::Entry::load_from(Path::new(&format!("libopenxr_loader_{loader_suffix}.so"))).unwrap()
};
#[cfg(target_os = "android")]
@ -784,6 +786,7 @@ pub fn entry_point() {
.collect(),
],
settings.video.foveated_rendering.into_option(),
platform != Platform::Lynx,
);
alvr_client_core::send_playspace(

View File

@ -21,19 +21,16 @@ pub fn download(sh: &Shell, url: &str, destination: &Path) -> Result<(), xshell:
cmd!(sh, "curl -L -o {destination} --url {url}").run()
}
pub fn download_and_extract_zip(
sh: &Shell,
url: &str,
destination: &Path,
) -> Result<(), xshell::Error> {
pub fn download_and_extract_zip(url: &str, destination: &Path) -> Result<(), xshell::Error> {
let sh = Shell::new().unwrap();
let temp_dir_guard = sh.create_temp_dir()?;
let zip_file = temp_dir_guard.path().join("temp_download.zip");
download(sh, url, &zip_file)?;
download(&sh, url, &zip_file)?;
sh.remove_path(destination).ok();
sh.create_dir(destination)?;
unzip(sh, &zip_file, destination)
unzip(&sh, &zip_file, destination)
}
pub fn date_utc_yyyymmdd(sh: &Shell) -> Result<String, xshell::Error> {

View File

@ -20,7 +20,6 @@ pub fn prepare_x264_windows() {
let deps_dir = afs::deps_dir();
command::download_and_extract_zip(
&sh,
&format!(
"{}/{VERSION}.r{REVISION}/libx264_{VERSION}.r{REVISION}_msvc16.zip",
"https://github.com/ShiftMediaProject/x264/releases/download",
@ -56,11 +55,8 @@ Cflags: -I${{includedir}}
}
pub fn prepare_ffmpeg_windows() {
let sh = Shell::new().unwrap();
let download_path = afs::deps_dir().join("windows");
command::download_and_extract_zip(
&sh,
&format!(
"https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/{}",
"ffmpeg-n5.1-latest-win64-gpl-shared-5.1.zip"
@ -103,7 +99,6 @@ pub fn build_ffmpeg_linux(nvenc_flag: bool) {
let download_path = afs::deps_dir().join("linux");
command::download_and_extract_zip(
&sh,
"https://codeload.github.com/FFmpeg/FFmpeg/zip/n6.0",
&download_path,
)
@ -206,72 +201,52 @@ pub fn build_ffmpeg_linux(nvenc_flag: bool) {
}
fn get_android_openxr_loaders() {
let sh = Shell::new().unwrap();
fn get_openxr_loader(name: &str, url: &str, source_dir: &str) {
let temp_dir = afs::build_dir().join("temp_download");
let destination_dir = afs::deps_dir().join("android_openxr/arm64-v8a");
fs::create_dir_all(&destination_dir).unwrap();
let destination_dir = afs::deps_dir().join("android_openxr/arm64-v8a");
fs::create_dir_all(&destination_dir).unwrap();
command::download_and_extract_zip(url, &temp_dir).unwrap();
fs::copy(
temp_dir.join(source_dir).join("libopenxr_loader.so"),
destination_dir.join(format!("libopenxr_loader_{name}.so")),
)
.unwrap();
fs::remove_dir_all(&temp_dir).ok();
}
let temp_dir = afs::build_dir().join("temp_download");
// Generic
command::download_and_extract_zip(
&sh,
get_openxr_loader(
"generic",
&format!(
"https://github.com/KhronosGroup/OpenXR-SDK-Source/releases/download/{}",
"release-1.0.27/openxr_loader_for_android-1.0.27.aar",
),
&temp_dir,
)
.unwrap();
fs::copy(
temp_dir.join("prefab/modules/openxr_loader/libs/android.arm64-v8a/libopenxr_loader.so"),
destination_dir.join("libopenxr_loader.so"),
)
.unwrap();
fs::remove_dir_all(&temp_dir).ok();
"prefab/modules/openxr_loader/libs/android.arm64-v8a",
);
// Quest
command::download_and_extract_zip(
&sh,
get_openxr_loader(
"quest",
"https://securecdn.oculus.com/binaries/download/?id=6316350341736833", // version 53
&temp_dir,
)
.unwrap();
fs::copy(
temp_dir.join("OpenXR/Libs/Android/arm64-v8a/Release/libopenxr_loader.so"),
destination_dir.join("libopenxr_loader_quest.so"),
)
.unwrap();
fs::remove_dir_all(&temp_dir).ok();
"OpenXR/Libs/Android/arm64-v8a/Release",
);
// Pico
command::download_and_extract_zip(
&sh,
get_openxr_loader(
"pico",
"https://sdk.picovr.com/developer-platform/sdk/PICO_OpenXR_SDK_220.zip",
&temp_dir,
)
.unwrap();
fs::copy(
temp_dir.join("libs/android.arm64-v8a/libopenxr_loader.so"),
destination_dir.join("libopenxr_loader_pico.so"),
)
.unwrap();
fs::remove_dir_all(&temp_dir).ok();
"libs/android.arm64-v8a",
);
// Yvr
command::download_and_extract_zip(
&sh,
get_openxr_loader(
"yvr",
"https://developer.yvrdream.com/yvrdoc/sdk/openxr/yvr_openxr_mobile_sdk_1.0.0.zip",
&temp_dir,
)
.unwrap();
fs::copy(
temp_dir
.join("yvr_openxr_mobile_sdk_1.0.0/OpenXR/Libs/Android/arm64-v8a/libopenxr_loader.so"),
destination_dir.join("libopenxr_loader_yvr.so"),
)
.unwrap();
fs::remove_dir_all(temp_dir).ok();
"yvr_openxr_mobile_sdk_1.0.0/OpenXR/Libs/Android/arm64-v8a",
);
get_openxr_loader(
"lynx",
"https://portal.lynx-r.com/downloads/download/16", // version 1.0.0
"jni/arm64-v8a",
);
}
pub fn build_android_deps(skip_admin_priv: bool) {