Friday, May 20, 2016

Minimal Code for Creating An OpenGL Context

Background

Anyone who does any work with OpenGL in a Windows environment knows it is necessary to create an OpenGL context before OpenGL extensions can be loaded. The typical instruction is first create a temporary window, then a temporary OpenGL context which must be made current.  Once that is done, initialize the OpenGL extensions using your favorite extension loading library like GLEW or GLEE or whatever. If that succeeds, you will have the necessary functions to take control of the context creation parameters using the newer wglChoosePixelFormatARB and similar functions. Attach the desired context to the application window, make it current and then initialize the extensions loader again.  At that point the temporary window may be destroyed. It seems like an odd series of steps to be sure, but if you think about it, extensions are associated with a context and the "wgl" functions in particular are associated with a Windows device context (HDC) which means all of these things need to be setup in advance.

Lately, I have been playing with a homegrown extension loader written in "C". In order to test the functionality of my loader, I needed to create a Window and OpenGL context. Those who create windows from scratch understand, that while straight-forward, it is not necessarily trivial. I learned several years ago the temporary window does not need to be made visible, for the process of context creation to work. Also, for my purposes of checking my extension loading library, it was not necessary for me do anything but the bare minimum in order to create an OpenGL context. Here is how I did it.

The 'main' Function

I rarely use the expected WinMain function when creating a windows application. It doesn't seem to give me any real advantages and it isn't necessary in order create a main application Window.  The only possible advantage is suppression of the console window.  But, for me, the console window provides a mechanism for entering commands and outputting messages, so I prefer to have one for much of what I do.

int main(int argc, char* argv[])
{
 if (!CreateContext())
  return -1;
 int retv = 0;
 if (!initOpengl()) // should succeed if context exists
  retv = 1;
 RemoveContext();

 return retv;
}


The key functions for context management are the calls to CreateContext, which creates the temporary window and OpenGL context, and RemoveContext which destroys them in the text-book fashion.

The Context Functions

The CreateContext function performs three tasks.  It registers a new window class, creates a window of the class, and then attaches an OpenGL context to it.

HWND      hwnd;
HINSTANCE hinst;
HDC       hdc;
HGLRC     hglrc;

bool32 CreateContext()
{
 hinst = GetModuleHandle(0);
 WNDCLASS wclass      = { 0 };
 wclass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
 wclass.hInstance     = hinst;
 wclass.lpszClassName = "TEST_CLASS";
 wclass.lpfnWndProc   = (WNDPROC)MsgHandler;
 if (!RegisterClass(&wclass))
  return false;
 hwnd = CreateWindow("TEST_CLASS", "TEST", 0, 0, 0, 256, 256, 0, 0, hinst, 0);
 if (!hwnd)
  return false;

 hdc = GetDC(hwnd);

 PIXELFORMATDESCRIPTOR pfd = { 0 };
 pfd.nSize        = sizeof(PIXELFORMATDESCRIPTOR);
 pfd.nVersion     = 1;
 pfd.dwFlags      = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW;
 pfd.iPixelType   = PFD_TYPE_RGBA;
 pfd.iLayerType   = PFD_MAIN_PLANE;
 pfd.cColorBits   = 32;
 pfd.cDepthBits   = 24;
 pfd.cStencilBits = 8;
 int pf = ChoosePixelFormat(hdc, &pfd);
 if (!pf) {
  return false; // returns null
 }
 if (SetPixelFormat(hdc, pf, &pfd)) {
  hglrc = wglCreateContext(hdc);
 }
 if (hglrc) {
  if (!wglMakeCurrent(hdc, hglrc))
   return false;
 }
 return true;
}

Notice that the WNDCLASS struct requires a pointer to a message handler.  This function only requires a few lines of code.

int32 MsgHandler(HWND hwnd, uint32 uMsg, uint32 wParam, uint32 lParam)
{
 switch (uMsg)
 {
 case WM_DESTROY:
  return 0;
 }
 return DefWindowProc((HWND)hwnd, uMsg, wParam, lParam);
}


Finally, when the temporary context is no longer required, we clean up after ourselves.

void RemoveContext()
{
 wglMakeCurrent(0, 0);
 wglDeleteContext(hglrc);
 DestroyWindow(hwnd);
 UnregisterClass("TEST_CLASS", hinst);
}

Next Steps

In the 'main' function, between CreateContext and RemoveContext, the call is made to initialize the OpenGL extensions.  If this succeeds, this would be the time to take the next important step of using the extended functions to create the final desired context. Depending on your purposes and how the final context is created, you may need to alter the RemoveContext function since it calls wglMakeCurrent(0, 0) which may remove your newly created context from scope.


Wednesday, May 18, 2016

Supporting Opengl Through Version 3.3

Introduction

I love to write code (both C++ and C) which utilizes Opengl for graphics. However, many of the computers I target are not high-end game machines. Most of the time, they are common, lower-cost systems or business machines which rarely support Opengl versions above 3.2. Today, it seems most of the online documentation and advice assumes you are targeting the most recent versions of Opengl and often I have seen the advice, "download the latest drivers". But it simply doesn't work that way. Many computers are incapable of supporting state-of-the-art graphics without sinking a sizable chunk of money into new cards.


Loading Extensions

Usually, when writing Opengl code, I use GLEW in order to avoid the pain and possible errors of trying to create my own Opengl extension loader.  Most of the projects I create use static linking and in order to statically link GLEW, the best way to avoid certain problems with Visual Studio is to build GLEW from source.  This works well once the proper "make" system is used to create the necessary project files. Recently, I have decided to fore go GLEW and write my own extension loader as an educational exercise.  I thought it would be convenient to target a limited subset of the Opengl functions and definitions aimed at supporting low-end graphics.  I was able to figure out the process looking at various online sources and within a few hours, had a working extension loader which allowed me to create a version 3.0 context.


Opengl Hell

As I tried to expand the extension loading library I began the process of digging much deeper under the hood of Opengl, trying to understand which functions and enum values are required for various versions of Opengl. Once you dive-in, you soon discover the documentation may be scattered around and not always clear. For example, consider the extensions required to support shaders in Open version 2.0. You will need to derive the functions and definitions from extension, #30, GL_ARB_shader_objects; #31, GL_ARB_vertex_shader; #32 GL_ARB_fragment_shader; and #33, GL_ARB_shading_language_100.  However, you will soon discover the final function names are not the same as the onces defined in the extension specifications (see: https://www.opengl.org/registry/). So which functions are supported in "official" Opengl 2.0?  How do I know which functions and definitions are considered "core profile", especially considering certain enum values and functions are deprecated in version 3.1?  Sure, as they say, deprecated does not mean deleted but that statement is usually followed by "but...it is best not to write code using deprecated functions because you never know when the functions and values will be deleted". So if you want your legacy code to be viable in the next decade, avoid deprecated functions.  No problem...so which enumerants are deprecated? Ah! Welcome to Opengl hell, at least it was for me.

Shedding Some Light

Nevertheless, despite the lack of clarity I decided to begin assembling the information I needed to build an extension loading library. So, over the course of a few days, I rummaged through various specifications and sources and assembled a spreadsheet which went a long to way toward giving me the information I needed. On the spreadsheet I listed all known Opengl functions and enumerants and recorded which are considered core, which are deprecated, and in which extensions they are declared. Because, it was not easy to put this together, I decided to make the next person's workload a little easier and have posted it online here.  Hopefully you will find if of value. Later I will expand the information to include versions 4.0 and greater.




Monday, December 2, 2013

Win32 Console Issues

Console Shutdown Issues

When using a console with your Windows application, it is possible for the user to close the console window using the system menu or clicking the 'x' button in the upper right corner of the console frame.  Doing this will cause the Windows application to terminate.  As far as I can tell, this functions like an application abort and does not give you the opportunity to clean up or gracefully shut down your application.  Worse yet, terminating the application may not be what the user expects. No warning is given or chance to cancel the impending shutdown.

The CtrlHandler Function

Windows does provide a way to hook a control handler chain which theoretically allows you to detect and act upon the close, ctrl-c, ctrl-break, and logoff events.  Unfortunately, there is no way to prevent the events.  Your event handling function can be registered when the Console object init function is run.

SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE );

The event processing function is declared as a static member of the Console object.

static bool CtrlHandler(DWORD fdwCtrlType);

Finally the event processing function is coded as part of the Console definition in Console.cpp.

bool Console::CtrlHandler(DWORD fdwCtrlType)
{
switch(fdwCtrlType) {
case CTRL_CLOSE_EVENT:
//
// do some cleanup
//
return false;
case CTRL_SHUTDOWN_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_LOGOFF_EVENT:
return false;

default:
return false;
}
}

Since the event handler is coded as a static member, it cannot access any non-static function or member unless it first acquires an instance of the console object.  This is a limitation of how the Console object is currently coded.  This is not a problem if a static member is created which is a pointer to the current Console instance.

Console.h

class Console
{
    ...

public:
    static Console* m_pCons;
};

You must create an instance of m_pCons which can then be initialized in the console init function.

Console * m_pCons = 0;

bool Console::init()
{
   m_pcons = this;

   ...
}

Now inside the event handler you can use the Console instance pointer m_pCons to access any of the non-static members of the console object.

Doing this makes m_pCons a global pointer accessible anywhere in the code.  A better way to do this would be to create the console object as a singleton.  Use of the singleton pattern is not without its fair share of controversy in the C++ community. Personally, I see use of singletons as a benefit for amateur or hobbyist type applications, although in the long run, it is probably better to learn from the pros and emulate their methods.

Preventing the Close

Creating the event handler, really accomplishes very little in my opinion.  After you do what you can do accessing the various functions and methods of the Console object or any other global objects, the application will still terminate.  There seems to be no practical way to avoid it once it gets that far.  An alternative would be to prevent the user from accessing the close function in the first place.  As it turns out this is very simple to do.  Simply get the console window handle and use it to get the handle of the system menu.  Then delete the "close" menu.  This eliminates the close selection from the system menu and disables the 'x' button, preventing the user from inadvertently shutting down the application.

bool Console::init()
{
   ...

   if (!AllocConsole())
       return false;

   ...

    m_hCons = GetConsoleWindow();
    if (m_hCons <= 0)
        return false;
    HMENU hmenu = GetSystemMenu(m_hCons, FALSE);
    DeleteMenu(hmenu, SC_CLOSE, MF_BYCOMMAND);

    return true;
}

This works quite well at eliminating the accidental 'close' but it will not prevent the user from shutting down the application using a log off or other kind of termination.  That's okay because it is probably not a good idea to take all control away from the user.

A Better Way?

Considering the programmer has limited control over the console window without hooking deeply into its messaging system, the programmer must understand the vulnerabilities of using a console window and weigh the advantages with the disadvantages.  The console window is a ready-made device built to handle character-mode streams with minimal code.  On the other hand it does provide a way for the user to abort the application with almost no way to prevent it.  Disabling the close menu does provide a method to prevent an inadvertent termination of the application.  If the goal is to use the console window as a logging and messaging device, it makes sense to create it once when the application is started and not shut it down until the application has finished running.  With the console's window handle, it is easy to hide or show the console when necessary from your application without freeing it using the FreeConsole method.

A safer way, perhaps, to deal with logging and messaging is to create your own window designed to display logging text.  Since a window is a graphical device rather than a character-mode device, much more can be done, including adding controls like buttons and checkboxes.  Of course, then the console window is essentially a dialog box but you will have full control over how it is created and when it is destroyed.  I will no doubt revisit this topic in the future.

Sunday, December 1, 2013

Create A Win32 Console

Creating a Win32 Console
Windows applications can often take advantage of a Win32 Console to provide simple character-mode I/O which is useful for displaying debugging information or log messages, and provides a simple way to input commands for the application.  When you create a console process under Visual Studio, the console is created for you.  This happens when your compiled program entry point is a main function.  If the compiled application is a Win32 application with a WinMain entry function, you must create your own console in order to use one.  It is not difficult to do, and once done, provides a convenient way to work with the stdin, stdout and stderr character streams.

The example code Main.cpp is a typical WinMain function.  The function is very simple.  It creates an application object and tells it to run.  When the application is finished running, the WindMain function exits and your program ends.

Main.cpp

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "Application.h"


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
 Application* app = new Application;
 if( app->init(hInstance))
  app->run();
 return 0;
}

The program Application is the master object which creates the functioning parts of the program. It consists of an init function which creates the objects and a shutdown function which deletes the objects. It also has an update function which updates each object.  The run method is an endless loop which continues to repeat as long the update function returns true.  If update returns false, the shutdown function is called and the application exits the run method which returns to WinMain and the program ends.

Application.h

#ifndef __APPLICATION_H__
#define __APPLICATION_H__

#include <windows.h>
#include "Console.h"

class Application
{
public:
 bool init(HINSTANCE hInst);
 void run();
 void shutDown();
 bool update();
 void render();

private:
 void setAppDefaults();

private:
 HINSTANCE m_hInstance;
 Console* con;

private:
};
#endif

For this demo, the application init function creates the application's one and only object; the console.  If console creation is successful, the console will be repeatedly updated by invoking the update function in the application run method.

Application.cpp

#include "Application.h"

bool Application::init(HINSTANCE hInst)
{
 // create application framework
 m_hInstance = hInst;

 // create a console for logging
 con = 0;
 con = new Console;
 if (!con->init())
  return false;
 con->printBanner();

 return true;
}

void Application::shutDown()
{
 if (con) delete con;
}

bool Application::update()
{
 con->update();
 return true;
}

void Application::render()
{
}

void Application::run()
{
 for(;;)
 {
  if ( !update() )
   break;
  render();
 }
 shutDown();
}

Console.h defines the console object, which is really a manager of sorts, responsible for creating the command console, providing methods for interacting with the console and ultimately, cleanly deleting it when it is no longer needed.

Console.h

#ifndef __CONSOLE_H__
#define __CONSOLE_H__

#include <windows.h>
#include <string>
#include <iostream>
#include <stdio.h>


class Console
{
public:
 bool init();
 void shutDown();
 void update();
 void print(char * text, ...);
 void printBanner();

 bool isConsole() {return m_bCons;}

 ~Console() {shutDown();}

protected:
 void command();
    bool errorExit (std::string s);
 void keyEventProc(KEY_EVENT_RECORD ker);
    void mouseEventProc(MOUSE_EVENT_RECORD mer);
 void resizeEventProc(WINDOW_BUFFER_SIZE_RECORD wbsr);

private:
 HWND  m_hCons;    // window handle of console
 bool  m_bCons;    // console created if true
 DWORD  m_dwOldMode;   // old I/O state
    DWORD  m_dwMode;    // current I/O state
 HANDLE  m_hStdIn;    // standard handles
 HANDLE  m_hStdOut;
 HANDLE  m_hStdErr;
 std::string m_CmdBuf;    // buffer for input strings
 FILE*  m_ConOut;    // file stream pointer
};

#endif

The init function creates a new console with Windows AllocConsole function. After this, gathers the standard I/O handles and then redirects stdout and stderr to the console.  The freopen_s function can also redirect stdout or stderr to a file by replacing "CON" with the file specification, such as "app.log".

Console.cpp

#include "console.h"

bool Console::init()
{
 m_bCons = false;

 if ( !AllocConsole() )
  return false;

 m_hStdIn  = GetStdHandle(STD_INPUT_HANDLE);
 m_hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
 m_hStdErr = GetStdHandle(STD_ERROR_HANDLE);

 errno_t err = 0;
 
 err = freopen_s( &m_ConOut, "CON", "w", stdout);
 if (err)
  return errorExit("Redir stdout");
 err = freopen_s( &m_ConOut, "CON", "w", stderr);
 if (err)
  return errorExit("Redir stderr");
 

 //CONSOLE_SCREEN_BUFFER_INFO lpSBI;
 //GetConsoleScreenBufferInfo(m_hStdOut, &lpSBI);
 COORD sz;
 sz.X = 80;
 sz.Y = 1024;
 SetConsoleScreenBufferSize(m_hStdOut, sz);

 if (m_hStdIn == INVALID_HANDLE_VALUE)
        return errorExit((LPSTR)"GetStdHandle");

    m_CmdBuf = "";  // create an empty command buffer

    // Save the current input mode, to be restored on exit.
    if (! GetConsoleMode(m_hStdIn, &m_dwOldMode) )
        return errorExit((LPSTR)"GetConsoleMode");

    m_dwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
    if (! SetConsoleMode(m_hStdIn, m_dwMode) )
        return errorExit((LPSTR)"SetConsoleMode");

 m_hCons = GetConsoleWindow();
 if (m_hCons <= 0)
  return false;
 m_pCons = this;
 return true;
}

Finally, the screen buffer is set to 8 characters by 1024 lines, we get the current input mode, set the console input mode to suit our application, then gather other miscellaneous info about the console we may or may not need later.  The rest of the demo code for the console object is shown below, including the printBanner functions illustrates several different ways, text can be output to the console.

Console.cpp (cont)

bool Console::errorExit(std::string s)
{
 std::cerr<<"**CONSOLE_init FAIL** "<< s << std::endl;
 return false;
}

void Console::print(char *text, ...)
{
 va_list argList;
 va_start(argList, text);

 //Write the text
 vfprintf(stdout, text, argList);
 putc('\n', stdout);

 va_end(argList);
}

void Console::shutDown()
{
 fflush(stdout);
 fflush(stderr);
 ////fclose(m_ConOut);
    SetConsoleMode(m_hStdIn, m_dwOldMode);
 FreeConsole();
}

void Console::update()
{
 // read console events
 //LPDWORD nEvents = 0;
    DWORD nEvents;

    //std::cout << m_hStdIn << std::endl;

 GetNumberOfConsoleInputEvents(m_hStdIn, &nEvents);
 if ( !nEvents )
  return;

 INPUT_RECORD irInBuf[128];
 DWORD   cNumRead;
    if (! ReadConsoleInput(
            m_hStdIn,    // input buffer handle
            irInBuf,     // buffer to read into
            128,         // size of read buffer
            &cNumRead) ) // number of records read
        errorExit("ReadConsoleInput");

    for (DWORD i = 0; i < cNumRead; i++)
    {
        switch(irInBuf[i].EventType)
        {
            case KEY_EVENT: // keyboard input
                keyEventProc(irInBuf[i].Event.KeyEvent);
                break;

            case MOUSE_EVENT: // mouse input
                mouseEventProc(irInBuf[i].Event.MouseEvent);
                break;

            case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
                resizeEventProc( irInBuf[i].Event.WindowBufferSizeEvent );
                break;

            case FOCUS_EVENT:  // disregard focus events

            case MENU_EVENT:   // disregard menu events
                break;

            default:
                errorExit("Unknown event type");
                break;
        }
    }
}
void Console::keyEventProc(KEY_EVENT_RECORD ker)
{
    char t = '\0';
    if (ker.bKeyDown)
    {
        char t = ker.uChar.AsciiChar;

        if (t == 13)
        {
            std::cout << std::endl;
            // process the command buffer
            command();
            // reset the command buffer
            m_CmdBuf = "";
            return;
        }
        if (t == 8)
        {
            if (!m_CmdBuf.empty())
            {
                m_CmdBuf.resize (m_CmdBuf.size () - 1);
            }
            std::cout<<t;
            std::cout<<" ";
        }
        std::cout<<t;
        if (t>31)
            m_CmdBuf += t;
    }
}

void Console::mouseEventProc(MOUSE_EVENT_RECORD mer)
{
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
    return;

    printf("Mouse event: ");

    switch(mer.dwEventFlags)
    {
        case 0:

            if (mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
            {
            }
            else if (mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
            {
            }
            else
            {
            }
            break;
        case DOUBLE_CLICK:
            break;
        case MOUSE_HWHEELED:
            break;
        case MOUSE_MOVED:
            break;
        case MOUSE_WHEELED:
            break;
        default:
            break;
    }
}

void Console::resizeEventProc(WINDOW_BUFFER_SIZE_RECORD wbsr)
{
}

void Console::printBanner()
{
 printf("+==============================================+\n");
 printf("+           E10 Graphics Framework             +\n");
 printf("+              (c) 2012 - 2013                 +\n");
 printf("+                 J. Kellams                   +\n");
 printf("+==============================================+\n");
 std::cerr << "testing" << std::endl;
}

The command function demonstrates a method for processing user input in the console window.  Entering the command "exit" will close the console window and terminate the application.

void Console::command()
{
 bool result = false;
 
 if (m_CmdBuf == "exit"){
  SendMessage(m_hCons, WM_CLOSE, 0, 0);
  result = true;
 }
 if (result)
  print("Command processed: %s", m_CmdBuf.c_str());
 else
  print("Unknown command: %s", m_CmdBuf.c_str()); 
}


Now, while this code works (or at least appears to) it is far from perfect. In fact, there is a major flaw in the shutdown.  If the user closes the console window from the system menu (clicking the 'X" button, for example) the shutdown method is bypassed and the application is not shutdown in a controlled way.  I will discuss this in more detail in the next post and show some ways to prevent this problem.