switch to glutin maybe
This commit is contained in:
parent
c152f57167
commit
6cf91935a0
4 changed files with 484 additions and 285 deletions
21
Cargo.toml
21
Cargo.toml
|
@ -9,11 +9,18 @@ panic = "abort"
|
|||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
|
||||
[features]
|
||||
default = ["wayland","egl"]
|
||||
egl = ["glutin-winit/egl"]
|
||||
wayland = ["glutin-winit/wayland", "winit/wayland-dlopen"]
|
||||
|
||||
[dependencies]
|
||||
gl = "0.14.0"
|
||||
glfw = { version = "0.57.0", features = ["wayland"] }
|
||||
image = { version = "0.25.2", default-features = false, features = ["rayon", "avif-native"] }
|
||||
#jxl-oxide = { version = "0.8.1" }
|
||||
#bytemuck = "1"
|
||||
#ogl33 = { version = "0.2.0", features = ["debug_error_checks"]}
|
||||
#beryllium = "0.13.3"
|
||||
jxl-oxide = { version = "0.8.1" }
|
||||
glutin = { version = "0.32.0", default-features = false}
|
||||
glutin-winit = { version = "0.5.0", default-features = false}
|
||||
raw-window-handle = "0.6"
|
||||
winit = { version = "0.30.0", default-features = false, features = ["rwh_06"] }
|
||||
|
||||
[build-dependencies]
|
||||
gl_generator = "0.14"
|
||||
cfg_aliases = "0.2.1"
|
||||
|
|
40
build.rs
Normal file
40
build.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use cfg_aliases::cfg_aliases;
|
||||
use gl_generator::{Api, Fallbacks, Profile, Registry, StructGenerator};
|
||||
|
||||
fn main() {
|
||||
// XXX this is taken from glutin/build.rs.
|
||||
|
||||
// Setup alias to reduce `cfg` boilerplate.
|
||||
cfg_aliases! {
|
||||
// Systems.
|
||||
android_platform: { target_os = "android" },
|
||||
wasm_platform: { target_family = "wasm" },
|
||||
macos_platform: { target_os = "macos" },
|
||||
ios_platform: { target_os = "ios" },
|
||||
apple: { any(ios_platform, macos_platform) },
|
||||
free_unix: { all(unix, not(apple), not(android_platform)) },
|
||||
|
||||
// Native displays.
|
||||
x11_platform: { all(feature = "x11", free_unix, not(wasm_platform)) },
|
||||
wayland_platform: { all(feature = "wayland", free_unix, not(wasm_platform)) },
|
||||
|
||||
// Backends.
|
||||
egl_backend: { all(feature = "egl", any(windows, unix), not(apple), not(wasm_platform)) },
|
||||
glx_backend: { all(feature = "glx", x11_platform, not(wasm_platform)) },
|
||||
wgl_backend: { all(feature = "wgl", windows, not(wasm_platform)) },
|
||||
cgl_backend: { all(macos_platform, not(wasm_platform)) },
|
||||
}
|
||||
|
||||
let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
let mut file = File::create(dest.join("gl_bindings.rs")).unwrap();
|
||||
Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, [])
|
||||
.write_bindings(StructGenerator, &mut file)
|
||||
.unwrap();
|
||||
}
|
286
src/main.rs
286
src/main.rs
|
@ -1,280 +1,10 @@
|
|||
use std::convert::TryInto;
|
||||
// use jxl_oxide::JxlImage;
|
||||
use image::ImageReader;
|
||||
use glfw;
|
||||
use glfw::Context;
|
||||
use gl;
|
||||
use std::error::Error;
|
||||
mod mywin;
|
||||
use winit::event_loop::EventLoop;
|
||||
use jxl_oxide::JxlImage;
|
||||
|
||||
const WIDTH: u32 = 480;
|
||||
const HEIGHT: u32 = 320;
|
||||
const TITLE: &str = "Hello From OpenGL World!";
|
||||
|
||||
fn main() {
|
||||
let img = ImageReader::open("combined.avif").unwrap().decode();
|
||||
println!("{:?}", img);
|
||||
// let image = JxlImage::builder().open("combined.jxl").expect("Failed to read image header");
|
||||
// println!("{:?}", image.image_header()); // Prints the image header
|
||||
//let image = dav1d::rav1d_open("combined.avif");
|
||||
|
||||
use glfw::fail_on_errors;
|
||||
let mut glfw = glfw::init(fail_on_errors!()).unwrap();
|
||||
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
|
||||
glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
|
||||
glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
|
||||
glfw.window_hint(glfw::WindowHint::Resizable(true));
|
||||
|
||||
let (mut window, events) = glfw.create_window(WIDTH, HEIGHT, TITLE, glfw::WindowMode::Windowed).unwrap();
|
||||
let (screen_width, screen_height) = window.get_framebuffer_size();
|
||||
|
||||
window.make_current();
|
||||
window.set_key_polling(true);
|
||||
window.set_pos_polling(true);
|
||||
window.set_all_polling(true);
|
||||
window.set_size_polling(true);
|
||||
window.set_close_polling(true);
|
||||
window.set_refresh_polling(true);
|
||||
window.set_focus_polling(true);
|
||||
window.set_iconify_polling(true);
|
||||
window.set_framebuffer_size_polling(true);
|
||||
window.set_char_polling(true);
|
||||
window.set_char_mods_polling(true);
|
||||
window.set_mouse_button_polling(true);
|
||||
window.set_cursor_pos_polling(true);
|
||||
window.set_cursor_enter_polling(true);
|
||||
window.set_scroll_polling(true);
|
||||
window.set_maximize_polling(true);
|
||||
window.set_content_scale_polling(true);
|
||||
gl::load_with(|ptr| window.get_proc_address(ptr) as *const _);
|
||||
|
||||
unsafe {
|
||||
gl::Viewport(0, 0, screen_width, screen_height);
|
||||
clear_color(Color(0.4, 0.4, 0.4, 1.0));
|
||||
}
|
||||
// -------------------------------------------
|
||||
|
||||
const VERT_SHADER: &str = "#version 330 core
|
||||
layout (location = 0) in vec3 position;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(position, 1.0);
|
||||
// gl_Position = vec4(position.xyz, 1.0);
|
||||
// gl_Position = vec4(position.x, position.y, position.z, 1.0);
|
||||
}";
|
||||
|
||||
const FRAG_SHADER: &str = "#version 330 core
|
||||
out vec4 Color;
|
||||
void main()
|
||||
{
|
||||
Color = vec4(0.9, 0.5, 0.2, 1.0);
|
||||
}";
|
||||
|
||||
let vertex_shader = unsafe { gl::CreateShader(gl::VERTEX_SHADER) };
|
||||
unsafe {
|
||||
gl::ShaderSource(vertex_shader, 1, &VERT_SHADER.as_bytes().as_ptr().cast(), &VERT_SHADER.len().try_into().unwrap());
|
||||
gl::CompileShader(vertex_shader);
|
||||
|
||||
let mut success = 0;
|
||||
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
|
||||
if success == 0 {
|
||||
let mut log_len = 0_i32;
|
||||
// gl::GetShaderiv(vertex_shader, gl::INFO_LOG_LENGTH, &mut log_len);
|
||||
// let mut v: Vec<u8> = Vec::with_capacity(log_len as usize);
|
||||
// gl::GetShaderInfoLog(vertex_shader, log_len, &mut log_len, v.as_mut_ptr().cast());
|
||||
let mut v: Vec<u8> = Vec::with_capacity(1024);
|
||||
gl::GetShaderInfoLog(vertex_shader, 1024, &mut log_len, v.as_mut_ptr().cast());
|
||||
v.set_len(log_len.try_into().unwrap());
|
||||
panic!("Vertex Shader Compile Error: {}", String::from_utf8_lossy(&v));
|
||||
}
|
||||
}
|
||||
|
||||
let fragment_shader = unsafe { gl::CreateShader(gl::FRAGMENT_SHADER) };
|
||||
unsafe {
|
||||
gl::ShaderSource(fragment_shader, 1, &FRAG_SHADER.as_bytes().as_ptr().cast(), &FRAG_SHADER.len().try_into().unwrap());
|
||||
gl::CompileShader(fragment_shader);
|
||||
|
||||
let mut success = 0;
|
||||
gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
|
||||
if success == 0 {
|
||||
let mut v: Vec<u8> = Vec::with_capacity(1024);
|
||||
let mut log_len = 0_i32;
|
||||
gl::GetShaderInfoLog(fragment_shader, 1024, &mut log_len, v.as_mut_ptr().cast());
|
||||
v.set_len(log_len.try_into().unwrap());
|
||||
panic!("Fragment Shader Compile Error: {}", String::from_utf8_lossy(&v));
|
||||
}
|
||||
}
|
||||
|
||||
let shader_program = unsafe { gl::CreateProgram() };
|
||||
unsafe {
|
||||
gl::AttachShader(shader_program, vertex_shader);
|
||||
gl::AttachShader(shader_program, fragment_shader);
|
||||
gl::LinkProgram(shader_program);
|
||||
|
||||
let mut success = 0;
|
||||
gl::GetProgramiv(shader_program, gl::LINK_STATUS, &mut success);
|
||||
if success == 0 {
|
||||
let mut v: Vec<u8> = Vec::with_capacity(1024);
|
||||
let mut log_len = 0_i32;
|
||||
gl::GetProgramInfoLog(shader_program, 1024, &mut log_len, v.as_mut_ptr().cast());
|
||||
v.set_len(log_len.try_into().unwrap());
|
||||
panic!("Program Link Error: {}", String::from_utf8_lossy(&v));
|
||||
}
|
||||
|
||||
gl::DetachShader(shader_program, vertex_shader);
|
||||
gl::DetachShader(shader_program, fragment_shader);
|
||||
gl::DeleteShader(vertex_shader);
|
||||
gl::DeleteShader(fragment_shader);
|
||||
}
|
||||
|
||||
let vertecies = [
|
||||
-0.5f32, -0.5, 0.0,
|
||||
0.5, -0.5, 0.0,
|
||||
0.0, 0.5, 0.0,
|
||||
];
|
||||
|
||||
let mut vao = 0;
|
||||
unsafe { gl::GenVertexArrays(1, &mut vao) };
|
||||
|
||||
let mut vbo = 0;
|
||||
unsafe { gl::GenBuffers(1, &mut vbo) };
|
||||
|
||||
unsafe {
|
||||
gl::BindVertexArray(vao);
|
||||
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
|
||||
gl::BufferData(gl::ARRAY_BUFFER, std::mem::size_of_val(&vertecies) as isize, vertecies.as_ptr().cast(), gl::STATIC_DRAW);
|
||||
|
||||
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, 3 * std::mem::size_of::<f32>() as i32, 0 as *const _);
|
||||
gl::EnableVertexAttribArray(0);
|
||||
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
|
||||
gl::BindVertexArray(0);
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
println!("OpenGL version: {}", gl_get_string(gl::VERSION));
|
||||
println!("GLSL version: {}", gl_get_string(gl::SHADING_LANGUAGE_VERSION));
|
||||
|
||||
|
||||
while !window.should_close() {
|
||||
glfw.poll_events();
|
||||
for (time, event) in glfw::flush_messages(&events) {
|
||||
glfw_handle_event(&mut window, time, event);
|
||||
}
|
||||
|
||||
clear_color(Color(0.3, 0.4, 0.6, 1.0));
|
||||
|
||||
unsafe {
|
||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
gl::UseProgram(shader_program);
|
||||
gl::BindVertexArray(vao);
|
||||
|
||||
gl::DrawArrays(gl::TRIANGLES, 0, 3);
|
||||
|
||||
gl::BindVertexArray(0);
|
||||
}
|
||||
|
||||
window.swap_buffers();
|
||||
}
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let image = JxlImage::builder().open("combined.jxl").expect("Failed to read image header");
|
||||
println!("{:?}", image.image_header()); // Prints the image header
|
||||
mywin::main(EventLoop::new().unwrap())
|
||||
}
|
||||
|
||||
pub struct Color(f32, f32, f32, f32);
|
||||
|
||||
pub fn clear_color(c: Color) {
|
||||
unsafe { gl::ClearColor(c.0, c.1, c.2, c.3) }
|
||||
}
|
||||
|
||||
pub fn gl_get_string<'a>(name: gl::types::GLenum) -> &'a str {
|
||||
let v = unsafe { gl::GetString(name) };
|
||||
let v: &std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(v as *const i8) };
|
||||
v.to_str().unwrap()
|
||||
}
|
||||
|
||||
fn glfw_handle_event(window: &mut glfw::Window, time: f64, event: glfw::WindowEvent) {
|
||||
use glfw::WindowEvent as Event;
|
||||
use glfw::Key;
|
||||
use glfw::Action;
|
||||
|
||||
match event {
|
||||
Event::Pos(x, y) => {
|
||||
window.set_title(&format!("Time: {:?}, Window pos: ({:?}, {:?})", time, x, y))
|
||||
}
|
||||
Event::Size(w, h) => window.set_title(&format!(
|
||||
"Time: {:?}, Window size: ({:?}, {:?})",
|
||||
time, w, h
|
||||
)),
|
||||
Event::Close => println!("Time: {:?}, Window close requested.", time),
|
||||
Event::Refresh => {
|
||||
println!("Time: {:?}, Window refresh callback triggered.", time)
|
||||
}
|
||||
Event::Focus(true) => println!("Time: {:?}, Window focus gained.", time),
|
||||
Event::Focus(false) => println!("Time: {:?}, Window focus lost.", time),
|
||||
Event::Iconify(true) => println!("Time: {:?}, Window was minimised", time),
|
||||
Event::Iconify(false) => println!("Time: {:?}, Window was maximised.", time),
|
||||
Event::FramebufferSize(w, h) => {
|
||||
unsafe {
|
||||
gl::Viewport(0, 0, w, h);
|
||||
}
|
||||
println!("Time: {:?}, Framebuffer size: ({:?}, {:?})", time, w, h)
|
||||
}
|
||||
Event::Char(character) => {
|
||||
println!("Time: {:?}, Character: {:?}", time, character)
|
||||
}
|
||||
Event::CharModifiers(character, mods) => println!(
|
||||
"Time: {:?}, Character: {:?}, Modifiers: [{:?}]",
|
||||
time, character, mods
|
||||
),
|
||||
Event::MouseButton(btn, action, mods) => println!(
|
||||
"Time: {:?}, Button: {:?}, Action: {:?}, Modifiers: [{:?}]",
|
||||
time,
|
||||
glfw::DebugAliases(btn),
|
||||
action,
|
||||
mods
|
||||
),
|
||||
Event::CursorPos(xpos, ypos) => window.set_title(&format!(
|
||||
"Time: {:?}, Cursor position: ({:?}, {:?})",
|
||||
time, xpos, ypos
|
||||
)),
|
||||
Event::CursorEnter(true) => {
|
||||
println!("Time: {:?}, Cursor entered window.", time)
|
||||
}
|
||||
Event::CursorEnter(false) => println!("Time: {:?}, Cursor left window.", time),
|
||||
Event::Scroll(x, y) => window.set_title(&format!(
|
||||
"Time: {:?}, Scroll offset: ({:?}, {:?})",
|
||||
time, x, y
|
||||
)),
|
||||
/*Event::Key(key, scancode, action, mods) => {
|
||||
println!(
|
||||
"Time: {:?}, Key: {:?}, ScanCode: {:?}, Action: {:?}, Modifiers: [{:?}]",
|
||||
time, key, scancode, action, mods
|
||||
);
|
||||
match (key, action) {
|
||||
(Key::Escape, Action::Press) => window.set_should_close(true),
|
||||
(Key::R, Action::Press) => {
|
||||
// Resize should cause the window to "refresh"
|
||||
let (window_width, window_height) = window.get_size();
|
||||
window.set_size(window_width + 1, window_height);
|
||||
window.set_size(window_width, window_height);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}*/
|
||||
Event::FileDrop(paths) => {
|
||||
println!("Time: {:?}, Files dropped: {:?}", time, paths)
|
||||
}
|
||||
Event::Maximize(maximized) => {
|
||||
println!("Time: {:?}, Window maximized: {:?}.", time, maximized)
|
||||
}
|
||||
Event::ContentScale(xscale, yscale) => println!(
|
||||
"Time: {:?}, Content scale x: {:?}, Content scale y: {:?}",
|
||||
time, xscale, yscale
|
||||
),
|
||||
Event::Key(Key::Escape, _, Action::Press, _) => {
|
||||
window.set_should_close(true);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
422
src/mywin.rs
Normal file
422
src/mywin.rs
Normal file
|
@ -0,0 +1,422 @@
|
|||
use std::error::Error;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::Deref;
|
||||
|
||||
use gl::types::GLfloat;
|
||||
use raw_window_handle::HasWindowHandle;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{KeyEvent, WindowEvent};
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::Window;
|
||||
|
||||
use glutin::config::{Config, ConfigTemplateBuilder};
|
||||
use glutin::context::{
|
||||
ContextApi, ContextAttributesBuilder, NotCurrentContext, PossiblyCurrentContext, Version,
|
||||
};
|
||||
use glutin::display::GetGlDisplay;
|
||||
use glutin::prelude::*;
|
||||
use glutin::surface::{Surface, SwapInterval, WindowSurface};
|
||||
|
||||
use glutin_winit::{DisplayBuilder, GlWindow};
|
||||
|
||||
pub mod gl {
|
||||
#![allow(clippy::all)]
|
||||
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||
|
||||
pub use Gles2 as Gl;
|
||||
}
|
||||
|
||||
pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box<dyn Error>> {
|
||||
let window_attributes = Window::default_attributes()
|
||||
.with_transparent(true)
|
||||
.with_title("Glutin triangle gradient example (press Escape to exit)");
|
||||
|
||||
// The template will match only the configurations supporting rendering
|
||||
// to windows.
|
||||
//
|
||||
// XXX We force transparency only on macOS, given that EGL on X11 doesn't
|
||||
// have it, but we still want to show window. The macOS situation is like
|
||||
// that, because we can query only one config at a time on it, but all
|
||||
// normal platforms will return multiple configs, so we can find the config
|
||||
// with transparency ourselves inside the `reduce`.
|
||||
let template =
|
||||
ConfigTemplateBuilder::new().with_alpha_size(8).with_transparency(cfg!(cgl_backend));
|
||||
|
||||
let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes));
|
||||
|
||||
let mut app = App::new(template, display_builder);
|
||||
event_loop.run_app(&mut app)?;
|
||||
|
||||
app.exit_state
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
let (mut window, gl_config) = match self.display_builder.clone().build(
|
||||
event_loop,
|
||||
self.template.clone(),
|
||||
gl_config_picker,
|
||||
) {
|
||||
Ok(ok) => ok,
|
||||
Err(e) => {
|
||||
self.exit_state = Err(e);
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
println!("Picked a config with {} samples", gl_config.num_samples());
|
||||
|
||||
let raw_window_handle = window
|
||||
.as_ref()
|
||||
.and_then(|window| window.window_handle().ok())
|
||||
.map(|handle| handle.as_raw());
|
||||
|
||||
// XXX The display could be obtained from any object created by it, so we can
|
||||
// query it from the config.
|
||||
let gl_display = gl_config.display();
|
||||
|
||||
// The context creation part.
|
||||
let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
|
||||
|
||||
// Since glutin by default tries to create OpenGL core context, which may not be
|
||||
// present we should try gles.
|
||||
let fallback_context_attributes = ContextAttributesBuilder::new()
|
||||
.with_context_api(ContextApi::Gles(None))
|
||||
.build(raw_window_handle);
|
||||
|
||||
// There are also some old devices that support neither modern OpenGL nor GLES.
|
||||
// To support these we can try and create a 2.1 context.
|
||||
let legacy_context_attributes = ContextAttributesBuilder::new()
|
||||
.with_context_api(ContextApi::OpenGl(Some(Version::new(2, 1))))
|
||||
.build(raw_window_handle);
|
||||
|
||||
// Reuse the uncurrented context from a suspended() call if it exists, otherwise
|
||||
// this is the first time resumed() is called, where the context still
|
||||
// has to be created.
|
||||
let not_current_gl_context = self.not_current_gl_context.take().unwrap_or_else(|| unsafe {
|
||||
gl_display.create_context(&gl_config, &context_attributes).unwrap_or_else(|_| {
|
||||
gl_display.create_context(&gl_config, &fallback_context_attributes).unwrap_or_else(
|
||||
|_| {
|
||||
gl_display
|
||||
.create_context(&gl_config, &legacy_context_attributes)
|
||||
.expect("failed to create context")
|
||||
},
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
let window = window.take().unwrap_or_else(|| {
|
||||
let window_attributes = Window::default_attributes()
|
||||
.with_transparent(true)
|
||||
.with_title("Glutin triangle gradient example (press Escape to exit)");
|
||||
glutin_winit::finalize_window(event_loop, window_attributes, &gl_config).unwrap()
|
||||
});
|
||||
|
||||
let attrs = window
|
||||
.build_surface_attributes(Default::default())
|
||||
.expect("Failed to build surface attributes");
|
||||
let gl_surface =
|
||||
unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };
|
||||
|
||||
// Make it current.
|
||||
let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap();
|
||||
|
||||
// The context needs to be current for the Renderer to set up shaders and
|
||||
// buffers. It also performs function loading, which needs a current context on
|
||||
// WGL.
|
||||
self.renderer.get_or_insert_with(|| Renderer::new(&gl_display));
|
||||
|
||||
// Try setting vsync.
|
||||
if let Err(res) = gl_surface
|
||||
.set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
|
||||
{
|
||||
eprintln!("Error setting vsync: {res:?}");
|
||||
}
|
||||
|
||||
assert!(self.state.replace(AppState { gl_context, gl_surface, window }).is_none());
|
||||
}
|
||||
|
||||
fn suspended(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
// This event is only raised on Android, where the backing NativeWindow for a GL
|
||||
// Surface can appear and disappear at any moment.
|
||||
println!("Android window removed");
|
||||
|
||||
// Destroy the GL Surface and un-current the GL Context before ndk-glue releases
|
||||
// the window back to the system.
|
||||
let gl_context = self.state.take().unwrap().gl_context;
|
||||
assert!(self
|
||||
.not_current_gl_context
|
||||
.replace(gl_context.make_not_current().unwrap())
|
||||
.is_none());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
_window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
WindowEvent::Resized(size) if size.width != 0 && size.height != 0 => {
|
||||
// Some platforms like EGL require resizing GL surface to update the size
|
||||
// Notable platforms here are Wayland and macOS, other don't require it
|
||||
// and the function is no-op, but it's wise to resize it for portability
|
||||
// reasons.
|
||||
if let Some(AppState { gl_context, gl_surface, window: _ }) = self.state.as_ref() {
|
||||
gl_surface.resize(
|
||||
gl_context,
|
||||
NonZeroU32::new(size.width).unwrap(),
|
||||
NonZeroU32::new(size.height).unwrap(),
|
||||
);
|
||||
let renderer = self.renderer.as_ref().unwrap();
|
||||
renderer.resize(size.width as i32, size.height as i32);
|
||||
}
|
||||
},
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
event: KeyEvent { logical_key: Key::Named(NamedKey::Escape), .. },
|
||||
..
|
||||
} => event_loop.exit(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
if let Some(AppState { gl_context, gl_surface, window }) = self.state.as_ref() {
|
||||
let renderer = self.renderer.as_ref().unwrap();
|
||||
renderer.draw();
|
||||
window.request_redraw();
|
||||
|
||||
gl_surface.swap_buffers(gl_context).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct App {
|
||||
template: ConfigTemplateBuilder,
|
||||
display_builder: DisplayBuilder,
|
||||
exit_state: Result<(), Box<dyn Error>>,
|
||||
not_current_gl_context: Option<NotCurrentContext>,
|
||||
renderer: Option<Renderer>,
|
||||
// NOTE: `AppState` carries the `Window`, thus it should be dropped after everything else.
|
||||
state: Option<AppState>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new(template: ConfigTemplateBuilder, display_builder: DisplayBuilder) -> Self {
|
||||
Self {
|
||||
template,
|
||||
display_builder,
|
||||
exit_state: Ok(()),
|
||||
not_current_gl_context: None,
|
||||
state: None,
|
||||
renderer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
gl_context: PossiblyCurrentContext,
|
||||
gl_surface: Surface<WindowSurface>,
|
||||
// NOTE: Window should be dropped after all resources created using its
|
||||
// raw-window-handle.
|
||||
window: Window,
|
||||
}
|
||||
|
||||
// Find the config with the maximum number of samples, so our triangle will be
|
||||
// smooth.
|
||||
pub fn gl_config_picker(configs: Box<dyn Iterator<Item = Config> + '_>) -> Config {
|
||||
configs
|
||||
.reduce(|accum, config| {
|
||||
let transparency_check = config.supports_transparency().unwrap_or(false)
|
||||
& !accum.supports_transparency().unwrap_or(false);
|
||||
|
||||
if transparency_check || config.num_samples() > accum.num_samples() {
|
||||
config
|
||||
} else {
|
||||
accum
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
program: gl::types::GLuint,
|
||||
vao: gl::types::GLuint,
|
||||
vbo: gl::types::GLuint,
|
||||
gl: gl::Gl,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new<D: GlDisplay>(gl_display: &D) -> Self {
|
||||
unsafe {
|
||||
let gl = gl::Gl::load_with(|symbol| {
|
||||
let symbol = CString::new(symbol).unwrap();
|
||||
gl_display.get_proc_address(symbol.as_c_str()).cast()
|
||||
});
|
||||
|
||||
if let Some(renderer) = get_gl_string(&gl, gl::RENDERER) {
|
||||
println!("Running on {}", renderer.to_string_lossy());
|
||||
}
|
||||
if let Some(version) = get_gl_string(&gl, gl::VERSION) {
|
||||
println!("OpenGL Version {}", version.to_string_lossy());
|
||||
}
|
||||
|
||||
if let Some(shaders_version) = get_gl_string(&gl, gl::SHADING_LANGUAGE_VERSION) {
|
||||
println!("Shaders version on {}", shaders_version.to_string_lossy());
|
||||
}
|
||||
|
||||
let vertex_shader = create_shader(&gl, gl::VERTEX_SHADER, VERTEX_SHADER_SOURCE);
|
||||
let fragment_shader = create_shader(&gl, gl::FRAGMENT_SHADER, FRAGMENT_SHADER_SOURCE);
|
||||
|
||||
let program = gl.CreateProgram();
|
||||
|
||||
gl.AttachShader(program, vertex_shader);
|
||||
gl.AttachShader(program, fragment_shader);
|
||||
|
||||
gl.LinkProgram(program);
|
||||
|
||||
gl.UseProgram(program);
|
||||
|
||||
gl.DeleteShader(vertex_shader);
|
||||
gl.DeleteShader(fragment_shader);
|
||||
|
||||
let mut vao = std::mem::zeroed();
|
||||
gl.GenVertexArrays(1, &mut vao);
|
||||
gl.BindVertexArray(vao);
|
||||
|
||||
let mut vbo = std::mem::zeroed();
|
||||
gl.GenBuffers(1, &mut vbo);
|
||||
gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
|
||||
gl.BufferData(
|
||||
gl::ARRAY_BUFFER,
|
||||
(VERTEX_DATA.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr,
|
||||
VERTEX_DATA.as_ptr() as *const _,
|
||||
gl::STATIC_DRAW,
|
||||
);
|
||||
|
||||
let pos_attrib = gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _);
|
||||
let color_attrib = gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _);
|
||||
gl.VertexAttribPointer(
|
||||
pos_attrib as gl::types::GLuint,
|
||||
2,
|
||||
gl::FLOAT,
|
||||
0,
|
||||
5 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
std::ptr::null(),
|
||||
);
|
||||
gl.VertexAttribPointer(
|
||||
color_attrib as gl::types::GLuint,
|
||||
3,
|
||||
gl::FLOAT,
|
||||
0,
|
||||
5 * std::mem::size_of::<f32>() as gl::types::GLsizei,
|
||||
(2 * std::mem::size_of::<f32>()) as *const () as *const _,
|
||||
);
|
||||
gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint);
|
||||
gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint);
|
||||
|
||||
Self { program, vao, vbo, gl }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self) {
|
||||
self.draw_with_clear_color(0.1, 0.1, 0.1, 0.9)
|
||||
}
|
||||
|
||||
pub fn draw_with_clear_color(
|
||||
&self,
|
||||
red: GLfloat,
|
||||
green: GLfloat,
|
||||
blue: GLfloat,
|
||||
alpha: GLfloat,
|
||||
) {
|
||||
unsafe {
|
||||
self.gl.UseProgram(self.program);
|
||||
|
||||
self.gl.BindVertexArray(self.vao);
|
||||
self.gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo);
|
||||
|
||||
self.gl.ClearColor(red, green, blue, alpha);
|
||||
self.gl.Clear(gl::COLOR_BUFFER_BIT);
|
||||
self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&self, width: i32, height: i32) {
|
||||
unsafe {
|
||||
self.gl.Viewport(0, 0, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Renderer {
|
||||
type Target = gl::Gl;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.gl
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Renderer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.gl.DeleteProgram(self.program);
|
||||
self.gl.DeleteBuffers(1, &self.vbo);
|
||||
self.gl.DeleteVertexArrays(1, &self.vao);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_shader(
|
||||
gl: &gl::Gl,
|
||||
shader: gl::types::GLenum,
|
||||
source: &[u8],
|
||||
) -> gl::types::GLuint {
|
||||
let shader = gl.CreateShader(shader);
|
||||
gl.ShaderSource(shader, 1, [source.as_ptr().cast()].as_ptr(), std::ptr::null());
|
||||
gl.CompileShader(shader);
|
||||
shader
|
||||
}
|
||||
|
||||
fn get_gl_string(gl: &gl::Gl, variant: gl::types::GLenum) -> Option<&'static CStr> {
|
||||
unsafe {
|
||||
let s = gl.GetString(variant);
|
||||
(!s.is_null()).then(|| CStr::from_ptr(s.cast()))
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
static VERTEX_DATA: [f32; 15] = [
|
||||
-0.5, -0.5, 1.0, 0.0, 0.0,
|
||||
0.0, 0.5, 0.0, 1.0, 0.0,
|
||||
0.5, -0.5, 0.0, 0.0, 1.0,
|
||||
];
|
||||
|
||||
const VERTEX_SHADER_SOURCE: &[u8] = b"
|
||||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
attribute vec2 position;
|
||||
attribute vec3 color;
|
||||
|
||||
varying vec3 v_color;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
v_color = color;
|
||||
}
|
||||
\0";
|
||||
|
||||
const FRAGMENT_SHADER_SOURCE: &[u8] = b"
|
||||
#version 100
|
||||
precision mediump float;
|
||||
|
||||
varying vec3 v_color;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(v_color, 1.0);
|
||||
}
|
||||
\0";
|
Loading…
Reference in a new issue