Внезапно решил попробовать использовать SDL2 и OpenGL в Rust. В сети есть соответствующие библиотеки и примеры к ним, но оказалось что все примеры устарели и больше не компилируются. Пришлось решить эту проблему.

Я взял за основу примеры от SDL2, OpenGL биндинга для Rust и адаптировал их для Rust 1.18.0. Так как я только начал изучать Rust, мог где-нибудь напортачить. Тем не менее, этот пример компилируются и работает.
Файл main.rs:
extern crate gl; extern crate sdl2; use sdl2::EventPump; use sdl2::event::Event; use sdl2::keyboard::Keycode; use sdl2::video::GLProfile; use gl::types::*; use std::mem; use std::ptr; use std::str; use std::ffi::CStr; use std::ffi::CString; // Vertex data static VERTEX_DATA: [GLfloat; 6] = [0.0, 0.5, 0.5, -0.5, -0.5, -0.5]; // Shader sources static VS_SRC: &'static str = "#version 150\n\ in vec2 position;\n\ void main() {\n\ gl_Position = vec4(position, 0.0, 1.0);\n\ }"; static FS_SRC: &'static str = "#version 150\n\ out vec4 out_color;\n\ void main() {\n\ out_color = vec4(1.0, 1.0, 1.0, 1.0);\n\ }"; // compile shader fn compile_shader(src: &str, ty: GLenum) -> GLuint { let shader; unsafe { shader = gl::CreateShader(ty); // Attempt to compile the shader let c_str = CString::new(src.as_bytes()).unwrap(); gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null()); gl::CompileShader(shader); // Get the compile status let mut status = gl::FALSE as GLint; gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); // Fail on error if status != (gl::TRUE as GLint) { let mut len = 0; gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); let mut buf = Vec::with_capacity(len as usize); buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar); panic!("{}", str::from_utf8(&buf) .ok() .expect("ShaderInfoLog not valid utf8")); } } shader } // link fragment and vertex shader fn link_program(vs: GLuint, fs: GLuint) -> GLuint { unsafe { let program = gl::CreateProgram(); gl::AttachShader(program, vs); gl::AttachShader(program, fs); gl::LinkProgram(program); // Get the link status let mut status = gl::FALSE as GLint; gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); // Fail on error if status != (gl::TRUE as GLint) { let mut len: GLint = 0; gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); let mut buf = Vec::with_capacity(len as usize); buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar); panic!("{}", str::from_utf8(&buf) .ok() .expect("ProgramInfoLog not valid utf8")); } program } } // convert OpenGL native string to string unsafe fn gl_to_str(c_str: *const u8) -> &'static str { mem::transmute(str::from_utf8(CStr::from_ptr(c_str as *const i8).to_bytes()).unwrap()) } // simulate SDL_WaitEvent(NULL) function because it is not implemented in rust-sdl2 fn wait_for_event(events: &sdl2::EventSubsystem, pump: &mut EventPump) { let event = pump.wait_event(); events.push_event(event).ok(); } fn main() { // initialize SDL and video subsystem let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); let gl_attr = video_subsystem.gl_attr(); // Don't use deprecated OpenGL functions gl_attr.set_context_profile(GLProfile::Core); // Set the context into debug mode gl_attr.set_context_flags().debug().forward_compatible().set(); // Set the OpenGL context version (OpenGL 3.2) gl_attr.set_context_version(3, 2); // Enable anti-aliasing gl_attr.set_multisample_buffers(1); gl_attr.set_multisample_samples(4); let window = video_subsystem.window("Triangle", 640, 480) .position_centered() .opengl() .build() .unwrap(); // Yes, we're still using the Core profile assert_eq!(gl_attr.context_profile(), GLProfile::Core); // ... and we're still using OpenGL 3.2 assert_eq!(gl_attr.context_version(), (3, 2)); // init OpenGL function pointers gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _); // create OpenGL context and make it current let gl_context = window.gl_create_context().unwrap(); window.gl_make_current(&gl_context).ok(); // print drivers information unsafe { println!("Vendor: {}", gl_to_str(gl::GetString(gl::VENDOR))); println!("Renderer: {}", gl_to_str(gl::GetString(gl::RENDERER))); println!("GLSL version: {}", gl_to_str(gl::GetString(gl::SHADING_LANGUAGE_VERSION))); println!("GL version: {}", gl_to_str(gl::GetString(gl::VERSION))); } // load shaders let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER); let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER); let program = link_program(vs, fs); // prepare vertex array object and vertex buffer object let mut vao = 0; let mut vbo = 0; unsafe { // Create Vertex Array Object gl::GenVertexArrays(1, &mut vao); gl::BindVertexArray(vao); // Create a Vertex Buffer Object and copy the vertex data to it gl::GenBuffers(1, &mut vbo); gl::BindBuffer(gl::ARRAY_BUFFER, vbo); gl::BufferData(gl::ARRAY_BUFFER, (VERTEX_DATA.len() * mem::size_of::()) as GLsizeiptr, mem::transmute(&VERTEX_DATA[0]), gl::STATIC_DRAW); // Use shader program gl::UseProgram(program); gl::BindFragDataLocation(program, 0, CString::new("out_color").unwrap().as_ptr()); // Specify the layout of the vertex data let pos_attr = gl::GetAttribLocation(program, CString::new("position").unwrap().as_ptr()); gl::EnableVertexAttribArray(pos_attr as GLuint); gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT, gl::FALSE as GLboolean, 0, ptr::null()); } // main event loop let mut event_pump = sdl_context.event_pump().unwrap(); 'running: loop { // do rendering unsafe { // Clear the screen to black gl::ClearColor(0.3, 0.3, 0.3, 1.0); gl::Clear(gl::COLOR_BUFFER_BIT); // Draw a triangle from the 3 vertices gl::DrawArrays(gl::TRIANGLES, 0, 3); } // copy buffer to screen window.gl_swap_window(); // wait for event wait_for_event(&sdl_context.event().unwrap(), &mut event_pump); // handle all arrived events for event in event_pump.poll_iter() { match event { Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { break 'running }, _ => {} } } } // Cleanup unsafe { gl::DeleteProgram(program); gl::DeleteShader(fs); gl::DeleteShader(vs); gl::DeleteBuffers(1, &vbo); gl::DeleteVertexArrays(1, &vao); } }
Теперь содержимое файла Cargo.toml:
[package] name = "triangle" version = "0.1.0" authors = [ "Alexander" ] [dependencies] gl = "0.6.3" [dependencies.sdl2] features = [ "use_mac_framework" ] version = "0.30.0"
Этот пример рассчитан на компиляцию в MacOS X. Так как SDL2 у меня установлен как фреймворк, потребовалось указать дополнительную опцию “use_mac_framework”. На других платформах она не нужна.
Как ни странно, в оболочке для Rust не оказалось аналога функции SDL_WaitEvent(NULL), которая ждет прихода следующего события, но не удаляет его из очереди. Пришлось реализовать её подручными средствами. Надеюсь, в будущих версиях rust-sdl2 это исправят.