use std::convert::TryInto; use std::ffi::{c_void, CStr}; use std::sync::mpsc::Receiver; use glfw::{Context, Glfw, Window, WindowEvent}; use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint}; use librashader_common::Size; use crate::filter_chain::FilterChain; use crate::framebuffer::{Framebuffer, GlImage, Viewport}; const WIDTH: u32 = 900; const HEIGHT: u32 = 700; const TITLE: &str = "Hello From OpenGL World!"; pub fn compile_program(vertex: &str, fragment: &str) -> GLuint { let vertex_shader = unsafe { gl::CreateShader(gl::VERTEX_SHADER) }; unsafe { gl::ShaderSource( vertex_shader, 1, &vertex.as_bytes().as_ptr().cast(), &vertex.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 = 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 = 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, &fragment.as_bytes().as_ptr().cast(), &fragment.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 = 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 = 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); } shader_program } extern "system" fn debug_callback( _source: GLenum, _err_type: GLenum, _id: GLuint, _severity: GLenum, _length: GLsizei, message: *const GLchar, _user: *mut c_void, ) { unsafe { let message = CStr::from_ptr(message); println!("[gl] {message:?}"); } } pub fn setup() -> (Glfw, Window, Receiver<(f64, WindowEvent)>, GLuint, GLuint) { let mut glfw = glfw::init(glfw::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)); glfw.window_hint(glfw::WindowHint::OpenGlDebugContext(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); gl::load_with(|ptr| window.get_proc_address(ptr) as *const _); unsafe { gl::Enable(gl::DEBUG_OUTPUT); gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS); gl::DebugMessageCallback(Some(debug_callback), std::ptr::null_mut()); gl::DebugMessageControl( gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE, 0, std::ptr::null(), gl::TRUE, ); } 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; layout (location = 1) in vec3 Color; out VS_OUTPUT { vec3 Color; } OUT; void main() { gl_Position = vec4(Position, 1.0); OUT.Color = Color; }"; const FRAG_SHADER: &str = "#version 330 core in VS_OUTPUT { vec3 Color; } IN; layout(location = 0) out vec4 Color; void main() { Color = vec4(IN.Color, 1.0f); }"; let shader_program = compile_program(VERT_SHADER, FRAG_SHADER); unsafe { gl::ObjectLabel( gl::SHADER, shader_program, -1, b"color_shader\0".as_ptr().cast(), ); } let vertices = &[ // positions // colors 0.5f32, -0.5, 0.0, 1.0, 0.0, 0.0, // bottom right -0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // bottom left 0.0, 0.5, 0.0, 0.0, 0.0, 1.0, // top ]; let mut vbo: gl::types::GLuint = 0; unsafe { gl::GenBuffers(1, &mut vbo); gl::ObjectLabel(gl::BUFFER, vbo, -1, b"triangle_vbo\0".as_ptr().cast()); } unsafe { gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::BufferData( gl::ARRAY_BUFFER, // target (vertices.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, // size of data in bytes vertices.as_ptr() as *const gl::types::GLvoid, // pointer to data gl::STATIC_DRAW, // usage ); gl::BindBuffer(gl::ARRAY_BUFFER, 0); } // set up vertex array object let mut vao: gl::types::GLuint = 0; unsafe { gl::GenVertexArrays(1, &mut vao); gl::ObjectLabel(gl::VERTEX_ARRAY, vao, -1, b"triangle_vao\0".as_ptr().cast()); } unsafe { gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::EnableVertexAttribArray(0); // this is "layout (location = 0)" in vertex shader gl::VertexAttribPointer( 0, // index of the generic vertex attribute ("layout (location = 0)") 3, // the number of components per generic vertex attribute gl::FLOAT, // data type gl::FALSE, // normalized (int-to-float conversion) (6 * std::mem::size_of::()) as gl::types::GLint, // stride (byte offset between consecutive attributes) std::ptr::null(), // offset of the first component ); gl::EnableVertexAttribArray(1); // this is "layout (location = 0)" in vertex shader gl::VertexAttribPointer( 1, // index of the generic vertex attribute ("layout (location = 0)") 3, // the number of components per generic vertex attribute gl::FLOAT, // data type gl::FALSE, // normalized (int-to-float conversion) (6 * std::mem::size_of::()) as gl::types::GLint, // stride (byte offset between consecutive attributes) (3 * std::mem::size_of::()) as *const gl::types::GLvoid, // offset of the first component ); gl::BindBuffer(gl::ARRAY_BUFFER, 0); gl::BindVertexArray(0); } // set up shared state for window unsafe { gl::Viewport(0, 0, 900, 700); gl::ClearColor(0.3, 0.3, 0.5, 1.0); } // ------------------------------------------- println!("OpenGL version: {}", gl_get_string(gl::VERSION)); println!( "GLSL version: {}", gl_get_string(gl::SHADING_LANGUAGE_VERSION) ); (glfw, window, events, shader_program, vao) } pub fn do_loop( mut glfw: Glfw, mut window: Window, events: Receiver<(f64, WindowEvent)>, triangle_program: GLuint, triangle_vao: GLuint, filter: &mut FilterChain, ) { let mut framecount = 0; let mut rendered_framebuffer = 0; let mut rendered_texture = 0; let mut quad_vbuf = 0; let mut output_texture = 0; let mut output_framebuffer_handle = 0; let mut output_quad_vbuf = 0; unsafe { // do frmaebuffer gl::GenFramebuffers(1, &mut rendered_framebuffer); gl::BindFramebuffer(gl::FRAMEBUFFER, rendered_framebuffer); gl::ObjectLabel( gl::FRAMEBUFFER, rendered_framebuffer, -1, b"rendered_framebuffer\0".as_ptr().cast(), ); // make tetxure gl::GenTextures(1, &mut rendered_texture); gl::BindTexture(gl::TEXTURE_2D, rendered_texture); gl::ObjectLabel( gl::TEXTURE, rendered_texture, -1, b"rendered_texture\0".as_ptr().cast(), ); // empty image gl::TexStorage2D( gl::TEXTURE_2D, 1, gl::RGBA8, WIDTH as GLsizei, HEIGHT as GLsizei, ); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); gl::TexParameteri( gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint, ); gl::TexParameteri( gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint, ); // set color attachment gl::FramebufferTexture2D( gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, rendered_texture, 0, ); let buffers = [gl::COLOR_ATTACHMENT0]; gl::DrawBuffers(1, buffers.as_ptr()); if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE { panic!("failed to create fbo") } let fullscreen_fbo = [ -1.0f32, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, ]; gl::GenBuffers(1, &mut quad_vbuf); gl::BindBuffer(gl::ARRAY_BUFFER, quad_vbuf); gl::BufferData( gl::ARRAY_BUFFER, // target (fullscreen_fbo.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, // size of data in bytes fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data gl::STATIC_DRAW, // usage ); } unsafe { // do frmaebuffer gl::GenFramebuffers(1, &mut output_framebuffer_handle); gl::BindFramebuffer(gl::FRAMEBUFFER, output_framebuffer_handle); gl::ObjectLabel( gl::FRAMEBUFFER, output_framebuffer_handle, -1, b"output_framebuffer\0".as_ptr().cast(), ); // make tetxure gl::GenTextures(1, &mut output_texture); gl::BindTexture(gl::TEXTURE_2D, output_texture); gl::ObjectLabel( gl::TEXTURE, output_texture, -1, b"output_texture\0".as_ptr().cast(), ); // empty image gl::TexStorage2D( gl::TEXTURE_2D, 1, gl::RGBA8, WIDTH as GLsizei, HEIGHT as GLsizei, ); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); gl::TexParameteri( gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint, ); gl::TexParameteri( gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint, ); // set color attachment gl::FramebufferTexture2D( gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, output_texture, 0, ); let buffers = [gl::COLOR_ATTACHMENT0]; gl::DrawBuffers(1, buffers.as_ptr()); if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE { panic!("failed to create fbo") } let fullscreen_fbo = [ -1.0f32, -1.0, 0.0, 1.0, -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, ]; gl::GenBuffers(1, &mut output_quad_vbuf); gl::BindBuffer(gl::ARRAY_BUFFER, output_quad_vbuf); gl::BufferData( gl::ARRAY_BUFFER, // target (fullscreen_fbo.len() * std::mem::size_of::()) as gl::types::GLsizeiptr, // size of data in bytes fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data gl::STATIC_DRAW, // usage ); } const VERT_SHADER: &str = r"#version 150 core out vec2 v_tex; const vec2 pos[4]=vec2[4](vec2(-1.0, 1.0), vec2(-1.0,-1.0), vec2( 1.0, 1.0), vec2( 1.0,-1.0)); void main() { v_tex=0.5*pos[gl_VertexID] + vec2(0.5); gl_Position=vec4(pos[gl_VertexID], 0.0, 1.0); } "; const FRAG_SHADER: &str = r" #version 150 core in vec2 v_tex; uniform sampler2D texSampler; out vec4 color; void main() { color=texture(texSampler, v_tex); }"; let quad_programid = compile_program(VERT_SHADER, FRAG_SHADER); let mut quad_vao = 0; unsafe { gl::GenVertexArrays(1, &mut quad_vao); } let (fb_width, fb_height) = window.get_framebuffer_size(); let (vp_width, vp_height) = window.get_size(); let output = Framebuffer::new_from_raw( output_texture, output_framebuffer_handle, gl::RGBA8, Size::new(vp_width as u32, vp_height as u32), 1, ); while !window.should_close() { glfw.poll_events(); for (_, event) in glfw::flush_messages(&events) { glfw_handle_event(&mut window, event); } unsafe { // render to fb gl::BindFramebuffer(gl::FRAMEBUFFER, rendered_framebuffer); gl::Viewport(0, 0, vp_width, vp_height); // clear color clear_color(Color(0.3, 0.4, 0.6, 1.0)); gl::Clear(gl::COLOR_BUFFER_BIT); // do the drawing gl::UseProgram(triangle_program); // select vertices gl::BindVertexArray(triangle_vao); // draw to bound target gl::DrawArrays(gl::TRIANGLES, 0, 3); // unselect vertices gl::BindVertexArray(0); // unselect fbo gl::BindFramebuffer(gl::FRAMEBUFFER, 0); } let viewport = Viewport { x: 0, y: 0, output: &output, mvp: None, }; let rendered = GlImage { handle: rendered_texture, format: gl::RGBA8, size: Size { width: fb_width as u32, height: fb_height as u32, }, padded_size: Default::default(), }; filter.frame(framecount, &viewport, &rendered, false) .unwrap(); unsafe { // texture is done now. // draw quad to screen gl::BindFramebuffer(gl::FRAMEBUFFER, 0); gl::UseProgram(quad_programid); gl::ActiveTexture(gl::TEXTURE0); gl::BindTexture(gl::TEXTURE_2D, output_texture); gl::BindVertexArray(quad_vao); gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4); } framecount += 1; window.swap_buffers(); } } 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, event: glfw::WindowEvent) { use glfw::Action; use glfw::Key; use glfw::WindowEvent as Event; match event { Event::Key(Key::Escape, _, Action::Press, _) => { window.set_should_close(true); } Event::Size(width, height) => window.set_size(width, height), _ => {} } }