winapi - Win32 OpenGL Window in rust: unable to load certain function pointers


Keywords:winapi 


Question: 

#[cfg(windows)] extern crate winapi;

#[cfg(windows)] use winapi::shared::windef::HWND;
#[cfg(windows)] use winapi::shared::windef::HMENU;
#[cfg(windows)] use winapi::shared::windef::HBRUSH;
#[cfg(windows)] use winapi::shared::minwindef::HINSTANCE;

#[cfg(windows)] use winapi::shared::minwindef::UINT;
#[cfg(windows)] use winapi::shared::minwindef::DWORD;
#[cfg(windows)] use winapi::shared::minwindef::WPARAM;
#[cfg(windows)] use winapi::shared::minwindef::LPARAM;
#[cfg(windows)] use winapi::shared::minwindef::LRESULT;
#[cfg(windows)] use winapi::um::winnt::LPCWSTR;

#[cfg(windows)] use winapi::um::winuser::WS_OVERLAPPEDWINDOW;
#[cfg(windows)] use winapi::um::winuser::WS_VISIBLE;
#[cfg(windows)] use winapi::um::winuser::WNDCLASSW;

#[cfg(windows)] use std::os::windows::ffi::OsStrExt;
#[cfg(windows)] use std::ffi::OsStr;

#[cfg(windows)] use std::os::raw::c_void;
#[cfg(windows)] use winapi::um::libloaderapi::GetProcAddress;
#[cfg(windows)] use gl::types::*;

// 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
in vec2 position;
void main() {
    gl_Position = vec4(position, 0.0, 1.0);
}";

static FS_SRC: &'static str = "
#version 150
out vec4 out_color;
void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
}";

mod gl
{
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

#[cfg(windows)]
fn to_wstring(str : &str) -> Vec<u16>
{
    let v : Vec<u16> =
            OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
    v
}

#[cfg(windows)]
pub unsafe extern "system" fn window_proc(h_wnd :HWND,
    msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT
{
    if msg == winapi::um::winuser::WM_DESTROY {
        winapi::um::winuser::PostQuitMessage(0i32);
    }
    return winapi::um::winuser::DefWindowProcW(h_wnd, msg, w_param, l_param);
}

fn draw_gl_scene(window_width: i32, window_height: i32,
        h_dc: winapi::shared::windef::HDC)
{
    unsafe
    {
        let module = winapi::um::libloaderapi::LoadLibraryW(to_wstring("opengl32.dll")  .as_ptr());

        // gl::load_with(|s| GetProcAddress(module,
        //         s.as_ptr() as *const i8) as *const c_void);

        gl::Viewport::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        if gl::Viewport::is_loaded() {
            gl::Viewport(0, 0, window_width, window_height);
        } else {
            panic!("gl::Viewport was not loaded")
        }

        gl::ClearColor::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        if gl::ClearColor::is_loaded() {
            gl::ClearColor(0.12109375f32, 0.12109375f32, 0.12109375f32, 1.0f32);
        } else {
            panic!("gl::ClearColor was not loaded")
        }

        gl::GetError::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        let error_check_value: GLenum = gl::GetError();

        /*
        gl::CreateShader::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        let mut vertex_shader_id = 0u32;
        if gl::CreateShader::is_loaded() {
            vertex_shader_id = gl::CreateShader(gl::VERTEX_SHADER);
        } else {
            panic!("gl::CreateShader was not loaded")
        }
        */

        /*
        let c_str = std::ffi::CString::new(VS_SRC.as_bytes()).unwrap();
        gl::ShaderSource::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        if gl::ShaderSource::is_loaded() {
            gl::ShaderSource(vertex_shader_id, 1, &c_str.as_ptr(), std::ptr::null_mut());
        } else {
            // panic!("gl::ShaderSource was not loaded")
        }
        gl::CompileShader::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        if gl::CompileShader::is_loaded() {
            gl::CompileShader(vertex_shader_id);
        } else {
            // panic!("gl::CompileShader was not loaded")
        }
        let mut vertex_array_id: GLuint = 0u32;
        gl::GenVertexArrays::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        if gl::GenVertexArrays::is_loaded() {
            gl::GenVertexArrays(1, &mut vertex_array_id as *mut u32);
        } else {
            panic!("gl::GenVertexArrays was not loaded")
        }
        */

        gl::Clear::load_with(|s| GetProcAddress(module,
                s.as_ptr() as *const i8) as *const c_void);
        if gl::Clear::is_loaded() {
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT |     gl::STENCIL_BUFFER_BIT);
        } else {
            panic!("gl::Clear was not loaded")
        }
        winapi::um::wingdi::SwapBuffers(h_dc);
    }
}

fn main()
{
  unsafe
  {
    let class_name = to_wstring("OpenGL");

    let h_instance = winapi::um::libloaderapi::GetModuleHandleW(std::ptr::null_mut());
    let wnd = WNDCLASSW {
        style: 0,
        lpfnWndProc: Some(window_proc),
        cbClsExtra: 0,
        cbWndExtra: 0,
        hInstance: h_instance,
        hIcon: winapi::um::winuser::LoadIconW(0 as HINSTANCE,
                                              winapi::um::winuser::IDI_APPLICATION),
        hCursor: winapi::um::winuser::LoadCursorW(0 as HINSTANCE,
                                                  winapi::um::winuser::IDI_APPLICATION),
        hbrBackground: 16 as HBRUSH,
        lpszMenuName: 0 as LPCWSTR,
        lpszClassName: class_name.as_ptr(),
    };

    winapi::um::winuser::RegisterClassW(&wnd);

    let window_width = 640;
    let window_height = 480;
    let h_wnd_window = winapi::um::winuser::CreateWindowExW(0, class_name.as_ptr(),
                       to_wstring("OpenGL Example").as_ptr(), WS_OVERLAPPEDWINDOW |     WS_VISIBLE,
                       0, 0, window_width, window_height, 0 as HWND, 0 as HMENU,    h_instance,
                       std::ptr::null_mut());

    let mut msg = winapi::um::winuser::MSG {
        hwnd : 0 as HWND,
        message : 0 as UINT,
        wParam : 0 as WPARAM,
        lParam : 0 as LPARAM,
        time : 0 as DWORD,
        pt : winapi::shared::windef::POINT { x: 0, y: 0, },
    };

    let h_dc = winapi::um::winuser::GetDC(h_wnd_window);
    let pfd = winapi::um::wingdi::PIXELFORMATDESCRIPTOR {
        nSize: std::mem::size_of::<winapi::um::wingdi::PIXELFORMATDESCRIPTOR>() as u16,
        nVersion: 1,
        dwFlags: winapi::um::wingdi::PFD_DRAW_TO_WINDOW
                | winapi::um::wingdi::PFD_SUPPORT_OPENGL |  winapi::um::wingdi::PFD_DOUBLEBUFFER,
        iPixelType: winapi::um::wingdi::PFD_TYPE_RGBA,
        cColorBits: 64,
        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: 32,
        cStencilBits: 8,
        cAuxBuffers: 0,
        iLayerType: winapi::um::wingdi::PFD_MAIN_PLANE,
        bReserved: 0,
        dwLayerMask: 0,
        dwVisibleMask: 0,
        dwDamageMask: 0,
    };

    let pixel_format = winapi::um::wingdi::ChoosePixelFormat(h_dc,
            &pfd as *const winapi::um::wingdi::PIXELFORMATDESCRIPTOR);
    winapi::um::wingdi::SetPixelFormat(h_dc, pixel_format,
            &pfd as *const winapi::um::wingdi::PIXELFORMATDESCRIPTOR);
    let h_rc = winapi::um::wingdi::wglCreateContext(h_dc);
    winapi::um::wingdi::wglMakeCurrent(h_dc, h_rc);
    winapi::um::winuser::ShowWindow(h_wnd_window, winapi::um::winuser::SW_SHOW);

    loop
    {
        if winapi::um::winuser::PeekMessageW(&mut msg, 0u32 as HWND, 0u32, 0u32,
                                             winapi::um::winuser::PM_REMOVE) > 0i32 {
            if msg.message == winapi::um::winuser::WM_QUIT {
                break;
            } else {
                winapi::um::winuser::TranslateMessage(&mut msg);
                winapi::um::winuser::DispatchMessageW(&mut msg);
            }

        } else {
            draw_gl_scene(window_width, window_height, h_dc);
        }
    }
  }
}

The above code is the version of my implementation of the outdated project located here:

Using the latest version of winapi and gl_generator as the sole two dependencies, I expanded on the starter code to successfully render an OpenGL context in tandem with the Win32 API as opposed to the glfw as suggested by the contributors of the cargo crate gl-rs. When run on a Windows machine, this project as it is presents a dark screen, as seen in this link.

Any further attempt to render something more substantial has been fruitless thus far, due to an inability to load any function pointers that relate to shaders or vertex buffer objects.

If you comb over the function draw_gl_scene, you'll see that I have a generic "load_with" function commented out and a series of function-specific "load_with" functions alongside execution code or panics depending on whether the function in question "is_loaded". The generic "load_with" I've curbed for the time being until writing function-specific "load-with" functions is no longer tenable.

As for CreateShader, ShaderSource, CompileShader, and GenVertexArrays, none of them successfully load if I uncomment any of them. I'm sure this is a user error, The odds that one of winapi and gl-rs or an anomalistic interaction between winapi and gl_generator is the cause is unlikely. I'm at my wit's end as to how to resolve this. I appreciate your time if you've read this far.


1 Answer: 

Read the wiki and this too about loading OGL pointers.

Briefly, for Windows:

  • GetProcAddress is only valid for OpenGL <= 1.1
  • wglGetProcAddress is only valid for OpenGL > 1.1

Also, the use of shaders requires OpenGL >= 2.0. Better go with OGL >= 3.2 Core Profile. For this context:

  • You need a proper context for OpenGL >= 3.0, which is created with the use of wglCreateContextAttribsARB.
  • You need a valid and current context to get the pointer to wglCreateContextAttribsARB itself by using wglGetProcAddress.
  • This context may be temporary. It can be created with wglCreateContext. You can delete it after you got that pointer; and the create the "real" context.

.