use super::error::*;
use super::opengl::*;
use super::offscreen_trait::*;
use flo_render_gl_offscreen::wgl;
use flo_render_gl_offscreen::winapi::um::winuser::{CreateWindowExW, RegisterClassExW, GetDC, DefWindowProcW, DestroyWindow, WNDCLASSEXW, CS_OWNDC, WS_EX_APPWINDOW, WS_OVERLAPPEDWINDOW};
use flo_render_gl_offscreen::winapi::um::wingdi::{ChoosePixelFormat, SetPixelFormat, PIXELFORMATDESCRIPTOR, PFD_DRAW_TO_WINDOW, PFD_SUPPORT_OPENGL, PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, PFD_MAIN_PLANE};
use flo_render_gl_offscreen::winapi::um::libloaderapi::{LoadLibraryW, FreeLibrary, GetModuleHandleW, GetProcAddress};
use flo_render_gl_offscreen::winapi::um::errhandlingapi::{GetLastError};
use flo_render_gl_offscreen::winapi::shared::windef::{HWND, HGLRC};
use flo_render_gl_offscreen::winapi::shared::minwindef::{HINSTANCE};
use flo_render_gl_offscreen::winapi::shared::winerror::{ERROR_CLASS_ALREADY_EXISTS};
use std::mem;
use std::ptr;
use std::ffi;
use std::ffi::{CString, OsStr};
use std::os::windows::ffi::{OsStrExt};
struct WglOffscreenRenderContext {
window: HWND,
context: HGLRC,
opengl_library: HINSTANCE
}
pub fn initialize_offscreen_rendering() -> Result<impl OffscreenRenderContext, RenderInitError> {
unsafe {
let class_name = OsStr::new("FloRender Class").encode_wide().chain(Some(0).into_iter()).collect::<Vec<_>>();
let window_name = OsStr::new("flo_draw OpenGL window").encode_wide().chain(Some(0).into_iter()).collect::<Vec<_>>();
let mut window_class = mem::zeroed::<WNDCLASSEXW>();
window_class.cbSize = mem::size_of::<WNDCLASSEXW>() as u32;
window_class.lpszClassName = class_name.as_ptr();
window_class.style = CS_OWNDC;
window_class.hInstance = GetModuleHandleW(ptr::null());
window_class.lpfnWndProc = Some(DefWindowProcW);
if RegisterClassExW(&window_class) == 0 {
if GetLastError() != ERROR_CLASS_ALREADY_EXISTS {
panic!("Unable to register window class for offscreen rendering: {}", GetLastError());
}
}
let window = CreateWindowExW(WS_EX_APPWINDOW, class_name.as_ptr(), window_name.as_ptr(), WS_OVERLAPPEDWINDOW, 0, 0, 1024, 768, ptr::null_mut(), ptr::null_mut(), GetModuleHandleW(ptr::null()), ptr::null_mut());
if window.is_null() {
panic!("Unable to create window for offscreen rendering: {}", GetLastError());
}
let dc = GetDC(window);
if dc.is_null() {
panic!("Unable to fetch device context for offscreen window: {}", GetLastError());
}
let pixel_format = PIXELFORMATDESCRIPTOR {
nSize: mem::size_of::<PIXELFORMATDESCRIPTOR>() as u16,
nVersion: 1,
dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
iPixelType: PFD_TYPE_RGBA,
cColorBits: 32,
cRedBits: 0,
cRedShift: 0,
cGreenBits: 0,
cGreenShift: 0,
cBlueBits: 0,
cBlueShift: 0,
cAlphaBits: 0,
cAlphaShift: 0,
cAccumBits: 0,
cAccumRedBits: 0,
cAccumGreenBits: 0,
cAccumBlueBits: 0,
cAccumAlphaBits: 0,
cDepthBits: 24,
cStencilBits: 8,
cAuxBuffers: 0,
iLayerType: PFD_MAIN_PLANE,
bReserved: 0,
dwLayerMask: 0,
dwVisibleMask: 0,
dwDamageMask: 0
};
let chosen_format = ChoosePixelFormat(dc, &pixel_format);
SetPixelFormat(dc, chosen_format, &pixel_format);
let initial_ctxt = wgl::wgl::CreateContext(dc as *const _);
if initial_ctxt.is_null() {
panic!("Could not create initial OpenGL context: {}", GetLastError());
}
if wgl::wgl::MakeCurrent(dc as *const _, initial_ctxt) == 0 {
panic!("Could not make initial context current: {}", GetLastError());
}
let extra_functions = wgl::wgl_extra::Wgl::load_with(|addr| {
let addr = CString::new(addr.as_bytes()).unwrap();
let addr = addr.as_ptr();
wgl::wgl::GetProcAddress(addr) as *const _
});
wgl::wgl::MakeCurrent(dc as *const _, ptr::null());
wgl::wgl::DeleteContext(initial_ctxt);
let mut opengl33_attribs = vec![];
opengl33_attribs.push(wgl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as i32);
opengl33_attribs.push(3);
opengl33_attribs.push(wgl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as i32);
opengl33_attribs.push(3);
opengl33_attribs.push(0);
let ctxt = extra_functions.CreateContextAttribsARB(dc as *const _, ptr::null(), opengl33_attribs.as_ptr());
if ctxt.is_null() {
panic!("Could not create OpenGL 3.3 context: {}", GetLastError());
}
if wgl::wgl::MakeCurrent(dc as *const _, ctxt) == 0 {
panic!("Could not make OpenGL 3.3 context current: {}", GetLastError());
}
let opengl_name = OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter()).collect::<Vec<_>>();
let opengl_library = LoadLibraryW(opengl_name.as_ptr());
gl::load_with(|name| {
let name = CString::new(name.as_bytes()).unwrap();
let name = name.as_ptr();
let proc_address = wgl::wgl::GetProcAddress(name) as *const ffi::c_void;
if !proc_address.is_null() {
proc_address
} else {
GetProcAddress(opengl_library, name) as *const _
}
});
wgl::wgl::MakeCurrent(dc as *const _, ptr::null());
Ok(WglOffscreenRenderContext {
window: window,
context: ctxt as HGLRC,
opengl_library: opengl_library
})
}
}
impl Drop for WglOffscreenRenderContext {
fn drop(&mut self) {
unsafe {
wgl::wgl::DeleteContext(self.context as *const _);
DestroyWindow(self.window);
FreeLibrary(self.opengl_library);
}
}
}
impl OffscreenRenderContext for WglOffscreenRenderContext {
type RenderTarget = OpenGlOffscreenRenderer;
fn create_render_target(&mut self, width: usize, height: usize) -> Self::RenderTarget {
unsafe {
let dc = GetDC(self.window);
if dc.is_null() {
panic!("Unable to fetch device context for offscreen window: {}", GetLastError());
}
if wgl::wgl::MakeCurrent(dc as *const _, self.context as *const _) == 0 {
panic!("Could not make OpenGL context current: {}", GetLastError());
}
OpenGlOffscreenRenderer::new(width, height)
}
}
}