diff --git a/app/src/main/java/com/simongellis/vvb/emulator/LeiaRenderer.kt b/app/src/main/java/com/simongellis/vvb/emulator/LeiaRenderer.kt index 29526796..d5e021cb 100644 --- a/app/src/main/java/com/simongellis/vvb/emulator/LeiaRenderer.kt +++ b/app/src/main/java/com/simongellis/vvb/emulator/LeiaRenderer.kt @@ -33,6 +33,10 @@ class LeiaRenderer(emulator: Emulator, settings: Settings) : Renderer { nativeOnDrawFrame() } + override fun onModeChanged(enable3d: Boolean){ + nativeOnModeChanged(enable3d); + } + class Settings( val screenZoom: Float, val aspectRatio: Int, @@ -45,4 +49,5 @@ class LeiaRenderer(emulator: Emulator, settings: Settings) : Renderer { private external fun nativeOnSurfaceCreated() private external fun nativeOnSurfaceChanged(width: Int, height: Int) private external fun nativeOnDrawFrame() + private external fun nativeOnModeChanged(enable3d: Boolean) } \ No newline at end of file diff --git a/app/src/main/java/com/simongellis/vvb/emulator/Renderer.kt b/app/src/main/java/com/simongellis/vvb/emulator/Renderer.kt index eb5ee2dd..58917532 100644 --- a/app/src/main/java/com/simongellis/vvb/emulator/Renderer.kt +++ b/app/src/main/java/com/simongellis/vvb/emulator/Renderer.kt @@ -5,4 +5,5 @@ import android.opengl.GLSurfaceView interface Renderer : GLSurfaceView.Renderer { fun destroy() fun onResume() {} + fun onModeChanged(enable3d: Boolean) {} } \ No newline at end of file diff --git a/app/src/main/java/com/simongellis/vvb/game/GameView.kt b/app/src/main/java/com/simongellis/vvb/game/GameView.kt index 55a7bb8a..150c904c 100644 --- a/app/src/main/java/com/simongellis/vvb/game/GameView.kt +++ b/app/src/main/java/com/simongellis/vvb/game/GameView.kt @@ -22,13 +22,10 @@ import com.leia.android.lights.LeiaDisplayManager.BacklightMode.MODE_3D class GameView : ConstraintLayout, BacklightModeListener { private val _binding: GameViewBinding - private val _renderer: Renderer + private var _renderer: RendererWrapper private val _preferences: GamePreferences // LitByLeia - private var prev_desired_backlight_state = false - private val mExpectedBacklightMode: BacklightMode? = null - private var mBacklightHasShutDown = false private var mDisplayManager: LeiaDisplayManager? = null var controller: Controller? = null @@ -48,7 +45,7 @@ class GameView : ConstraintLayout, BacklightModeListener { init { val emulator = Emulator.instance _preferences = GamePreferences(context) - _renderer = when(_preferences.videoMode) { + val innerRenderer = when(_preferences.videoMode) { VideoMode.ANAGLYPH -> AnaglyphRenderer(emulator, _preferences.anaglyphSettings) VideoMode.CARDBOARD -> CardboardRenderer(emulator, _preferences.cardboardSettings) VideoMode.MONO_LEFT -> MonoRenderer(emulator, _preferences.monoSettings(Eye.LEFT)) @@ -56,6 +53,7 @@ class GameView : ConstraintLayout, BacklightModeListener { VideoMode.STEREO -> StereoRenderer(emulator, _preferences.stereoSettings) VideoMode.LEIA -> LeiaRenderer(emulator, _preferences.leiaSettings) } + _renderer = RendererWrapper(innerRenderer) val layoutInflater = LayoutInflater.from(context) _binding = GameViewBinding.inflate(layoutInflater, this) @@ -81,8 +79,8 @@ class GameView : ConstraintLayout, BacklightModeListener { setBackgroundColor(Color.BLACK) mDisplayManager = LeiaSDK.getDisplayManager(context) - if(mDisplayManager !== null){ - mDisplayManager?.registerBacklightModeListener(this) + mDisplayManager?.apply { + registerBacklightModeListener(this@GameView) checkShouldToggle3D(true) } } @@ -103,36 +101,36 @@ class GameView : ConstraintLayout, BacklightModeListener { _renderer.destroy() } + override fun onWindowFocusChanged(hasWindowFocus: Boolean) { + super.onWindowFocusChanged(hasWindowFocus) + checkShouldToggle3D(_preferences.isLeia && hasWindowFocus) + } + /** BacklightModeListener Interface requirement */ override fun onBacklightModeChanged(backlightMode: BacklightMode) { - //Log.e("EmulationActivity", "onBacklightModeChanged: callback received"); - // Do something to remember the backlight is no longer on - // Later, we have to let the native side know this occurred. - if (mExpectedBacklightMode == MODE_3D && - mExpectedBacklightMode != backlightMode - ) { - //Log.e("EmulationActivity", "onBacklightModeChanged: mBacklightHasShutDown = true;"); - mBacklightHasShutDown = true + if (_preferences.isLeia) { + //val emulator = Emulator.instance + // in 2D mode, just display one eye + _renderer.onModeChanged(backlightMode == BacklightMode.MODE_3D); } } - fun checkShouldToggle3D(desired_state: Boolean) { + private fun checkShouldToggle3D(desiredState: Boolean) { if(mDisplayManager === null) { return } - if (desired_state && _preferences.isLeia) { - Enable3D() + if (desiredState && _preferences.isLeia) { + enable3d() } else { - Disable3D() + disable3D() } - prev_desired_backlight_state = desired_state } - fun Enable3D() { + private fun enable3d() { mDisplayManager?.requestBacklightMode(MODE_3D) } - fun Disable3D() { + private fun disable3D() { mDisplayManager?.requestBacklightMode(MODE_2D) } } \ No newline at end of file diff --git a/app/src/main/java/com/simongellis/vvb/game/RendererWrapper.kt b/app/src/main/java/com/simongellis/vvb/game/RendererWrapper.kt new file mode 100644 index 00000000..19e3365f --- /dev/null +++ b/app/src/main/java/com/simongellis/vvb/game/RendererWrapper.kt @@ -0,0 +1,61 @@ +package com.simongellis.vvb.game + +import com.simongellis.vvb.emulator.Renderer +import java.util.concurrent.locks.ReentrantReadWriteLock +import javax.microedition.khronos.egl.EGLConfig +import javax.microedition.khronos.opengles.GL10 +import kotlin.concurrent.read +import kotlin.concurrent.write + +class RendererWrapper(private var _renderer: Renderer) : Renderer { + private val _lock = ReentrantReadWriteLock() + private var _shouldInit = false + private var _lastWidth: Int = 0 + private var _lastHeight: Int = 0 + + override fun destroy() { + _lock.write { _renderer.destroy() } + } + + override fun onModeChanged(enable3d: Boolean) { + _lock.read { _renderer.onModeChanged(enable3d) } + } + + override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) { + _lock.read { _renderer.onSurfaceCreated(gl, config) } + } + + override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) { + _lock.read { + _lastWidth = width + _lastHeight = height + _shouldInit = false + _renderer.onSurfaceChanged(gl, width, height) + } + } + + override fun onDrawFrame(gl: GL10?) { + _lock.read { + if (_shouldInit) { + synchronized(this) { + if (_shouldInit) { + _shouldInit = false + _renderer.onSurfaceCreated(gl, null) + _renderer.onSurfaceChanged(gl, _lastWidth, _lastHeight) + } + } + } + _renderer.onDrawFrame(gl) + } + } + + fun swapRenderer(newRenderer: Renderer) { + _shouldInit = true + val oldRenderer = _lock.write { + val oldRenderer = _renderer + _renderer = newRenderer + oldRenderer + } + oldRenderer.destroy() + } +} diff --git a/src/video/renderers/common.rs b/src/video/renderers/common.rs index dafce4d6..b0186556 100644 --- a/src/video/renderers/common.rs +++ b/src/video/renderers/common.rs @@ -6,8 +6,10 @@ pub trait RenderLogic { fn resize(&mut self, screen_size: (i32, i32)) -> Result<()>; fn update(&mut self, eye: Eye, buffer: &[u8]) -> Result<()>; fn draw(&self) -> Result<()>; + fn request_change_mode(&mut self, _enable3d: bool) -> Result<()> {Ok(())} } + pub struct Renderer { frame_buffers: FrameBufferConsumers, pub logic: TLogic, @@ -42,4 +44,9 @@ impl Renderer { } }); } + + pub fn on_mode_changed(&mut self, enable3d: bool) -> Result<()> { + self.logic.request_change_mode(enable3d)?; + Ok(()) + } } diff --git a/src/video/renderers/gl/program.rs b/src/video/renderers/gl/program.rs index 99c2f93e..e9ffe708 100644 --- a/src/video/renderers/gl/program.rs +++ b/src/video/renderers/gl/program.rs @@ -4,7 +4,7 @@ use crate::video::gl::types::{ }; use crate::video::gl::utils::{check_error, temp_array, AsVoidptr}; use anyhow::Result; -use log::error; +use log::{error}; use std::ffi::{CStr, CString}; const GL_TRUE: GLboolean = 1; @@ -66,10 +66,21 @@ unsafe fn check_shader(type_: GLenum, shader_id: GLuint) -> Result<()> { if length < 0 { return Err(anyhow::anyhow!("Invalid shader info log length")); } + let mut buf = vec![0; length as usize]; let buf_ptr = buf.as_mut_ptr() as *mut GLchar; gl::GetShaderInfoLog(shader_id, length, std::ptr::null_mut(), buf_ptr); - let cstr = CStr::from_bytes_with_nul(buf.as_slice())?; + + let last_ch = buf.as_slice().last().unwrap(); + let buff_slice = buf.as_slice(); + let null_ptr_slice = [0u8; 1].as_slice(); + let concatenated = [&buff_slice,null_ptr_slice].concat(); + let buff_slice_with_null = concatenated.as_slice(); + let cstr = if last_ch != &0u8 { + CStr::from_bytes_with_nul(buff_slice)? + } else { + CStr::from_bytes_with_nul(buff_slice_with_null)? + }; let log = cstr.to_str()?; Err(anyhow::anyhow!( @@ -98,14 +109,13 @@ impl Program { self.id = gl::CreateProgram(); check_error("create a program")?; + let vertex_shader = make_shader(gl::VERTEX_SHADER, self.vertex_shader)?; gl::AttachShader(self.id, vertex_shader); check_error("attach the vertex shader")?; - let fragment_shader = make_shader(gl::FRAGMENT_SHADER, self.fragment_shader)?; gl::AttachShader(self.id, fragment_shader); check_error("attach the fragment shader")?; - gl::LinkProgram(self.id); check_error("link a program")?; gl::UseProgram(self.id); @@ -172,6 +182,12 @@ impl Program { } } + pub fn set_uniform_int(&self, location: GLint, value: GLint) { + unsafe { + gl::Uniform1i(location, value); + } + } + pub fn start_render(&self) -> Result<()> { unsafe { gl::ClearColor(0.0, 0.0, 0.0, 1.0); @@ -218,7 +234,7 @@ impl Program { check_error("render a texture") } } - fn cleanup(&mut self) -> Result<()> { + pub fn cleanup(&mut self) -> Result<()> { unsafe { if gl::IsProgram(self.id) == GL_TRUE { gl::DeleteProgram(self.id) diff --git a/src/video/renderers/leia.rs b/src/video/renderers/leia.rs index 1d800b0b..8e9cee80 100644 --- a/src/video/renderers/leia.rs +++ b/src/video/renderers/leia.rs @@ -7,6 +7,7 @@ use super::gl::{ }; use crate::video::gl::types::{GLfloat, GLint, GLuint}; use anyhow::Result; +// use log::{info}; use cgmath::{vec3, Matrix4}; const VERTEX_SHADER: &str = "\ @@ -20,14 +21,20 @@ void main() { } "; -const FRAGMENT_SHADER: &str = "\ +const FRAGMENT_SHADER_4V: &str = "\ precision mediump float; uniform vec4 u_Colors[2]; uniform sampler2D u_Textures[2]; varying vec2 v_TexCoord; +uniform int u_enable_3d; void main() { + if (u_enable_3d==0){ + gl_FragColor = texture2D(u_Textures[0], v_TexCoord); + gl_FragColor = mix(u_Colors[1], u_Colors[0], gl_FragColor.g); + return; + } // + alignment_offset float view_id = mod(floor(gl_FragCoord.x), 4.0); if (view_id < 0.5) { gl_FragColor = texture2D(u_Textures[0], v_TexCoord); } @@ -48,11 +55,19 @@ pub struct LeiaRenderLogic { program: Program, textures: Textures, + texture_ids_2d: Vec, + + // mailbox? + requested_enable3d: bool, + enable3d: bool, + + position_location: GLuint, tex_coord_location: GLuint, modelview_location: GLint, textures_location: GLint, colors_location: GLint, + enable3d_location: GLint, texture_colors: [[GLfloat; 4]; 2], aspect_ratio: AspectRatio, @@ -63,14 +78,20 @@ impl LeiaRenderLogic { let scale = settings.screen_zoom; let offset = -settings.vertical_offset; Self { - program: Program::new(VERTEX_SHADER, FRAGMENT_SHADER), + program: Program::new(VERTEX_SHADER, FRAGMENT_SHADER_4V), textures: Textures::new(2, (VB_WIDTH, VB_HEIGHT)), + texture_ids_2d: vec![0,0], + + requested_enable3d: false, + enable3d: false, + position_location: 0, tex_coord_location: 0, modelview_location: -1, textures_location: -1, colors_location: -1, + enable3d_location: 0, texture_colors: [ utils::color_as_vector(settings.colors[0]), @@ -81,6 +102,21 @@ impl LeiaRenderLogic { * Matrix4::from_scale(scale), } } + + fn maybe_change_mode(&mut self){ + // already in the requested mode + if self.enable3d == self.requested_enable3d { + return + } + + self.enable3d = self.requested_enable3d; + + if self.enable3d { + self.program.set_uniform_int(self.enable3d_location, 1 as GLint); + } else { + self.program.set_uniform_int(self.enable3d_location, 0 as GLint); + } + } } impl RenderLogic for LeiaRenderLogic { fn init(&mut self) -> Result<()> { @@ -92,6 +128,12 @@ impl RenderLogic for LeiaRenderLogic { self.modelview_location = self.program.get_uniform_location("u_MV"); self.textures_location = self.program.get_uniform_location("u_Textures"); self.colors_location = self.program.get_uniform_location("u_Colors"); + self.enable3d_location = self.program.get_uniform_location("u_enable_3d"); + + // repeated left eye texture pointer in 2d mode (not working :G) + self.texture_ids_2d = vec![self.textures.ids[0],self.textures.ids[0]]; + + self.program.set_uniform_int(self.enable3d_location, 1 as GLint); // textures and colors don't change, set them here self.program @@ -119,6 +161,7 @@ impl RenderLogic for LeiaRenderLogic { } fn update(&mut self, eye: Eye, buffer: &[u8]) -> Result<()> { + self.maybe_change_mode(); self.textures.update(eye as usize, buffer) } fn draw(&self) -> Result<()> { @@ -126,6 +169,11 @@ impl RenderLogic for LeiaRenderLogic { self.program .draw_square(self.position_location, self.tex_coord_location) } + + fn request_change_mode(&mut self, enable3d: bool) -> Result<()> { + self.requested_enable3d = enable3d; // flag mailbox + Ok(()) + } } pub struct Settings { @@ -143,7 +191,7 @@ pub mod jni { use crate::video::renderers::common::Renderer; use crate::{jni_func, jni_helpers}; use anyhow::Result; - use jni::sys::{jint, jobject}; + use jni::sys::{jboolean, jint, jobject}; use jni::JNIEnv; type LeiaRenderer = Renderer; @@ -209,4 +257,10 @@ pub mod jni { let mut this = get_renderer(env, this)?; this.on_draw_frame() } + + jni_func!(LeiaRenderer_nativeOnModeChanged, on_mode_changed, jboolean); + fn on_mode_changed(env: &JNIEnv, this: jobject, enable3d: jboolean) -> Result<()> { + let mut this = get_renderer(env, this)?; + this.on_mode_changed(enable3d != 0) + } }