GLFW Framebuffer Code Works, WGL Code Fails On glBlitFramebuffer()

Hi Folks:

I’ve enjoyed Joey DeVries amazing Learn OpenGL tutorials, but I’ve been hung on the “Off-screen MSAA” section of this lesson.

I’m applying these lessons to a Blender model I’ve made. His examples are GLFW oriented. I’m developing for Windows right now so I’ve been rendering my model with code from the samples, altered to use WGL.

This particular tutorial involves the use of anti aliasing when using a frame buffer.

I can get the GLFW code from the sample to work fine. My sample using WGL blows up after rendering the image to the buffer, in the glBlitFramebuffer() call. I bracketed that statement with glGetError() calls.

I can separate the GLFW code from the example into a setup portion, called from the Windows message loop’s WM_CREATE clause and the sample’s game loop code called from code that services the message loop’s WM_TIMER message. This works too, as long as I’m using GLFW.

I comment out the following GLFW code from the function that sets up OpenGL:

    // Init GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

These statements are replaced by a call WGL setup functions I lifted from this tutorial.

These WGL setup functions have been working for a few of my versions of the tutorial’s sample code.

As I use this WGL startup code, I’ve found that I’ve needed to add things to the context attributes, like WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, as I work through the tutorials.

Here is the game loop code, mostly a straight lift from the tutorial’s samples. A few lines of GLFW code are commented out, replaced with WGL code.

void loop_from_tutorial_with_wgl(HWND hdlg, GRAPHICS_PARAMETER *graphics_parameter_ptr)
{
#if 0
    // Set frame time
    GLfloat currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;
#endif

    // Check and call events
    // glfwPollEvents();
    // Do_Movement();      
        
    // 1. Draw scene as normal in multisampled buffers
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);   
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Set transformation matrices        
    shader_ptr->Use();

    glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)screenWidth/(GLfloat)screenHeight, 0.1f, 1000.0f);
    glUniformMatrix4fv(glGetUniformLocation(shader_ptr->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
    glUniformMatrix4fv(glGetUniformLocation(shader_ptr->Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));    
    glUniformMatrix4fv(glGetUniformLocation(shader_ptr->Program, "model"), 1, GL_FALSE, glm::value_ptr(glm::mat4()));
        
    glBindVertexArray(cubeVAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    glBindVertexArray(0);

    // 2. Now blit multisampled buffer(s) to default framebuffers
    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);                

    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

    glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); 

    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       
    // Swap the buffers
    //  glfwSwapBuffers(window);
    SwapBuffers(graphics_parameter_ptr->get_hdc());
}

GLuint generateMultiSampleTexture(GLuint samples)
{
    GLuint texture;
    glGenTextures(1, &texture);

    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, screenWidth, screenHeight, GL_TRUE);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

    return texture; 
}

I can start flooding this thread with many lines of code. Perhaps someone can see something I’m overlooking before that happens.

  Thanks
  Larry

[QUOTE=larryl;1287175]…I’ve been hung on the “Off-screen MSAA” section of this lesson. …altered to use WGL.

This particular tutorial involves the use of anti aliasing when using a frame buffer. I can get the GLFW code from the sample to work fine. My sample using WGL blows up after rendering the image to the buffer, in the glBlitFramebuffer() call. I bracketed that statement with glGetError() calls. [/QUOTE]

What GL error is being thrown? GL_INVALID_OPERATION?

Though you do show the texture used for the off-screen FBO, you don’t really show the code for creating your two framebuffers (the system framebuffer and your off-screen framebuffer), I think the key lies there. Let’s see your WGL framebuffer and off-screen FBO setup.

I suspect what you want is to have your source (off-screen) framebuffer to be multisampled and your destination (WGL system) framebuffer to be single-sampled. If both are multisampled, and the number of samples is different between them, that is one reason why glBlitFramebuffer could throw an error.

Also on your glTexImage2DMultisample() call… GL_RGB is not a specific internal format. Consider using GL_RGB8 or GL_RGBA8. For more on this, see:

Thanks Again Dark Photon:

Yes, GL_INVALID_OPERATION.

Oh yea, leaving the buffer creation out of the post wasn’t smart.

I don’t usually slavishly copy and paste code from tutorials when trying to learn something. I was doing that here in order to isolate that elusive difference that broke the WGL version of the code.

This was cut and pasted from the sample code and inserted at the end of the OpenGL setup function, for both the GLFW and the WGL code:

    // Framebuffers
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);  
    // Create a multisampled color attachment texture
    GLuint textureColorBufferMultiSampled = generateMultiSampleTexture(4);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);
    // Create a renderbuffer object for depth and stencil attachments
    GLuint rbo;
    glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo); 
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, screenWidth, screenHeight); 
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); 

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

Here is generateMultiSampleTexture(), called from the code above:

GLuint generateMultiSampleTexture(GLuint samples)
{
    GLuint texture;
    glGenTextures(1, &texture);

    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, screenWidth, screenHeight, GL_TRUE);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

    return texture;
}

It looks like both buffers are doing four points of multi sampling.

[QUOTE=Dark Photon;1287180]Also on your glTexImage2DMultisample() call… GL_RGB is not a specific internal format. Consider using GL_RGB8 or GL_RGBA8. For more on this, see:

  • Common Mistakes#Image precision (OpenGL wiki)[/QUOTE]

    Attempts to pass GL_RGB8 and GL_RGBA8 to glTexImage2DMultisample() didn’t seem to have any effect.

    Thanks for your suggestions.

    Larry
    

Ok, now how about the creation of your system framebuffer (e.g. WGL window)?

It looks like both buffers are doing four points of multi sampling.

That’s fine. The buffers in a framebuffer should have the same sampling rate. What I was referrering to was the number of samples in your source framebuffer (your FBO) and your destination framebuffer (e.g. WGL window).

thte usual way to approch it is:
– find out what function call provokes an error
– find out why that function has thrown that error by reading the registy description of that function

example:

if (glGetError() != GL_NO_ERROR)
{
std::cout << "uuuppss! GL error occured before line " << __LINE__ << std::endl;
std::cin.get(); /* just stop the app */
}

past this piece of code at several locations in your code, until you know what function has thrown the error. then click here, find that function: OpenGL 4 Reference Pages
each function description has a “Errors” section, there you can find out why that type of GL_ERROR was thrown. if it was GHL_INVALID_OPERATION and glBlitFramebuffer() has thrown it:

one reason, (for example) could be that the “samples count” of the default framebuffer and your FBOs drawbuffer dont match

Thanks again Dark Photon:

  This is my prime suspect.  

As I stated in this thread’s original post, the code in my init_opengl() was lifted from this tutorial. I’d read about the need to create a fake window to initialize GLEW before creating the context used by the application. It got me started and I’m grateful for the way it laid out the issues.

I don’t have a solid understanding of the pixel format and context attributes. I’ve needed to alter the context attributes.

Here’s the OpenGL initialization code:

#include "stdafx.h"

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

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/wglew.h>

#include <opengl_utilities.h>

#include "initializing_and_terminating_openglP.h"

const char *FAKE_OPENGL_CLASS_NAME = "FAKE_OPENGL_CLASS";

// This code was lifted, with a little reorganization, from 
// http://www.mbsoftworks.sk/index.php?page=tutorials&series=1&tutorial=2
// The OpenGL groupdescribes what's happening here:
// https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL)#Pixel_Format
HGLRC init_opengl(HWND hwnd, HDC *hdc_ptr, HINSTANCE hinstance, int major_version,
    int minor_version, const int *PixelFormatAttribList_ptr, std::string *message_string_ptr)
{
    int gl_errors_found_count = 0;
    std::string gl_error_message;
    HGLRC return_val = NULL;
    bool error_found = false;
    std::string local_message_string;
    PIXELFORMATDESCRIPTOR pfd;
    int pixel_format = 0;
    int number_of_formats_found = 0;
    HDC hdc = GetDC(hwnd);
    HWND fake_window = NULL;
    std::string text_buffer;
    DWORD callers_error = GetLastError();
    DWORD error_code = 0;
    std::string string_buffer;

    SetLastError(0);

    if (!init_glew(hinstance, &local_message_string))
    {
        gl_errors_found_count = gl_error_to_text(&gl_error_message);

        error_found = true;

        if (message_string_ptr != NULL)
        {
            *message_string_ptr += "init_opengl()
";
            *message_string_ptr += "   Failed to initialize glew.
";
            *message_string_ptr += local_message_string.c_str();
        }
    }
    else
    {
        if (major_version <= 2)
        {
            memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
            pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
            pfd.nVersion = 1;
            pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
            pfd.iPixelType = PFD_TYPE_RGBA;
            pfd.cColorBits = 32;
            pfd.cDepthBits = 32;
            pfd.iLayerType = PFD_MAIN_PLANE;

            pixel_format = ChoosePixelFormat(hdc, &pfd);

            if (pixel_format == 0)
            {
                error_found = true;

                if (message_string_ptr != NULL)
                {
                    *message_string_ptr += "init_opengl()
";
                    *message_string_ptr += "   Failed to choose pixel format.
";
                    error_code = GetLastError();

                    if (error_code == 0)
                    {
                        *message_string_ptr += "Error code is 0
";
                    }
                    else
                    {
                        *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                        *message_string_ptr += "
";
                    }
                }
            }
            else
            {
                if (!SetPixelFormat(hdc, pixel_format, &pfd))
                {
                    error_found = true;

                    if (message_string_ptr != NULL)
                    {
                        *message_string_ptr += "init_opengl()
";
                        *message_string_ptr += "   Failed to set pixel format.
";
                        error_code = GetLastError();

                        if (error_code == 0)
                        {
                            *message_string_ptr += "Error code is 0
";
                        }
                        else
                        {
                            *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                            *message_string_ptr += "
";
                        }
                    }
                }
                else
                {
                    // Create the old style context (OpenGL 2.1 and before)
                    return_val = wglCreateContext(hdc);

                    if (return_val == NULL)
                    {
                        error_found = true;

                        if (message_string_ptr != NULL)
                        {
                            *message_string_ptr += "init_opengl()
";
                            *message_string_ptr += "   Failed to create context.
";
                            error_code = GetLastError();

                            if (error_code == 0)
                            {
                                *message_string_ptr += "Error code is 0
";
                            }
                            else
                            {
                                *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                                *message_string_ptr += "
";
                            }
                        }
                    }
                    else
                    {
                        if(!wglMakeCurrent(hdc, return_val))
                        {
                            error_found = true;

                            if (message_string_ptr != NULL)
                            {
                                *message_string_ptr += "init_opengl()
";
                                *message_string_ptr += "   wglMakeCurrent() failed.
";
                                error_code = GetLastError();

                                if (error_code == 0)
                                {
                                    *message_string_ptr += "Error code is 0
";
                                }
                                else
                                {
                                    *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                                    *message_string_ptr += "
";
                                }
                            }
                        }
                    }
                }
            }
        }
        else
        {
            // Version number > 2
            if (WGLEW_ARB_create_context && WGLEW_ARB_pixel_format)
            {
                const int iPixelFormatAttribList[] =
                {
                    WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
                    WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
                    WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
                    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
                    WGL_COLOR_BITS_ARB, 32,
                    WGL_DEPTH_BITS_ARB, 24,
                    WGL_STENCIL_BITS_ARB, 8,
                    WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
                    WGL_SAMPLES_ARB, 16,
                    0 // End of attributes list
                };

                if (PixelFormatAttribList_ptr == NULL)
                {
                    PixelFormatAttribList_ptr = iPixelFormatAttribList;
                }

                int iContextAttribs[] =
                {
                    WGL_CONTEXT_MAJOR_VERSION_ARB, major_version,
                    WGL_CONTEXT_MINOR_VERSION_ARB, minor_version,
                    // WGL_CONTEXT_FLAGS_ARB,
                    WGL_CONTEXT_PROFILE_MASK_ARB,
                    WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
                    // WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
                    0 // End of attributes list
                };

                if (!wglChoosePixelFormatARB(hdc,
                    PixelFormatAttribList_ptr,
                    // iPixelFormatAttribList, // Integer attribute pairs
                    NULL,					// Floating attribute pairs
                    1,	// Max num of formats that can be stored next argument.
                    &pixel_format,	// List of acceptable formats go here.
                    (UINT*)&number_of_formats_found))	// Number of formats saved in list.
                {
                    error_found = true;

                    if (message_string_ptr != NULL)
                    {
                        *message_string_ptr += "init_opengl()
";
                        *message_string_ptr += "   Failed to choose pixel format.
";
                        error_code = GetLastError();

                        if (error_code == 0)
                        {
                            *message_string_ptr += "Error code is 0
";
                        }
                        else
                        {
                            *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                            *message_string_ptr += "
";
                        }
                    }
                }
                else
                {
                    // PFD seems to be only redundant parameter now
                    // Apparently the PIXELFORMATDESCRIPTOR isn't necessary for SetPixelFormat():
                    // https://msdn.microsoft.com/en-us/library/windows/desktop/dd369049%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
                    if (!SetPixelFormat(hdc, pixel_format, NULL))
                    {
                        error_found = true;

                        if (message_string_ptr != NULL)
                        {
                            *message_string_ptr += "init_opengl()
";
                            *message_string_ptr += "   Failed to set pixel format.
";
                            error_code = GetLastError();

                            if (error_code == 0)
                            {
                                *message_string_ptr += "Error code is 0
";
                            }
                            else
                            {
                                *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                                *message_string_ptr += "
";
                            }
                        }
                    }
                    else
                    {
                        return_val = wglCreateContextAttribsARB(hdc, 0, iContextAttribs);

                        // If everything went OK
                        if (return_val == NULL)
                        {
                            error_found = true;

                            if (message_string_ptr != NULL)
                            {
                                *message_string_ptr += "init_opengl()
";
                                *message_string_ptr += "   Faild to create OpenGL context.
";
                                *message_string_ptr += "   OpelGL version:";
                                totext_s(major_version, &text_buffer, 10);
                                *message_string_ptr += text_buffer.c_str();
                                *message_string_ptr += ".";
                                totext_s(minor_version, &text_buffer, 10);
                                *message_string_ptr += text_buffer.c_str();
                                *message_string_ptr += "
";
                                error_code = GetLastError();

                                if (error_code == 0)
                                {
                                    *message_string_ptr += "Error code is 0
";
                                }
                                else
                                {
                                    *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                                    *message_string_ptr += "
";
                                }
                            }
                        }
                        else
                        {
                            if(!wglMakeCurrent(hdc, return_val))
                            {
                                error_found = true;

                                if (message_string_ptr != NULL)
                                {
                                    *message_string_ptr += "init_opengl()
";
                                    *message_string_ptr += "   wglMakeCurrent() failed.
";
                                    error_code = GetLastError();

                                    if (error_code == 0)
                                    {
                                        *message_string_ptr += "Error code is 0
";
                                    }
                                    else
                                    {
                                        *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                                        *message_string_ptr += "
";
                                    }
                                }
                            }

                            gl_errors_found_count = gl_error_to_text(&gl_error_message);
                        }
                    }
                }
            }
        }
    }

    gl_errors_found_count = gl_error_to_text(&gl_error_message);

    if (hdc_ptr != NULL)
    {
        *hdc_ptr = hdc;
    }

    // Restore caller's error code, if any.
    SetLastError(callers_error);

    gl_errors_found_count = gl_error_to_text(&gl_error_message);

    return return_val;
}

bool init_glew(HINSTANCE hinstance, std::string *message_string_ptr)
{
    // This code is inspired by http://www.mbsoftworks.sk/index.php?page=tutorials&series=1&tutorial=2

    // From https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL)
    // The key problem is this: the function you use to get WGL extensions is, 
    // itself, an OpenGL extension. Thus like any OpenGL function, it requires an 
    // OpenGL context to call it. So in order to get the functions we need to 
    // create a context, we have to... create a context. 
    bool error_found = false;
    WNDCLASS fake_window_class;
    HDC hdc = NULL;
    HGLRC fake_hglrc = NULL;
    DWORD error_code = 0;
    std::string string_buffer;

    memset(&fake_window_class, 0x00, sizeof fake_window_class);

    fake_window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;		// Redraw On Move, And Own DC For Window
    fake_window_class.lpfnWndProc = fake_WndProc;
    fake_window_class.cbClsExtra = 0;						// No Extra Window Data
    fake_window_class.cbWndExtra = 0;						// No Extra Window Data
    fake_window_class.hInstance = hinstance;	// Set The Instance
    fake_window_class.hIcon = NULL;			// Load The Default Icon
    fake_window_class.hCursor = NULL;		// Load The Arrow Pointer
    fake_window_class.hbrBackground = HBRUSH(BLACK_BRUSH);			// No Background Required For GL
    fake_window_class.lpszMenuName = NULL;
    fake_window_class.lpszClassName = FAKE_OPENGL_CLASS_NAME;		// Set The Class Name

    if (!RegisterClass(&fake_window_class))					// Attempt To Register The Window Class
    {
        error_found = true;

        if (message_string_ptr != NULL)
        {
            *message_string_ptr += "init_glew()
";
            *message_string_ptr += "   Failed To Register The Window Class ";
            *message_string_ptr += FAKE_OPENGL_CLASS_NAME;
            *message_string_ptr += ".
";
            error_code = GetLastError();

            if (error_code == 0)
            {
                *message_string_ptr += "Error code is 0
";
            }
            else
            {
                *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                *message_string_ptr += "
";
            }
        }
    }

    HWND fake_window_handle = CreateWindow(FAKE_OPENGL_CLASS_NAME,
        "FAKE",
        WS_OVERLAPPEDWINDOW | WS_MAXIMIZE | WS_CLIPCHILDREN,
        0,
        0,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hinstance,
        NULL);

    if (fake_window_handle == NULL)
    {
        error_found = true;

        if (message_string_ptr != NULL)
        {
            *message_string_ptr += "init_glew()
";
            *message_string_ptr += "   Failed To create ";
            *message_string_ptr += FAKE_OPENGL_CLASS_NAME;
            *message_string_ptr += " class window.
";
            *message_string_ptr += "   Hinstance ";
            *message_string_ptr += totext_s(int(hinstance), &string_buffer, 16, 8, '0')->c_str();
            *message_string_ptr += ".
";
            error_code = GetLastError();

            if (error_code == 0)
            {
                *message_string_ptr += "Error code is 0
";
            }
            else
            {
                *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                *message_string_ptr += "
";
            }
        }
    }
    else
    {
        hdc = GetDC(fake_window_handle);

        if(hdc == NULL)
        {
            error_found = true;

            if (message_string_ptr != NULL)
            {
                *message_string_ptr += "init_glew()
";
                *message_string_ptr += "   GetDC() returned NULL.
";
            }
        }
        else
        {
            // Choose a pixel format
            PIXELFORMATDESCRIPTOR pfd;
            memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
            pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
            pfd.nVersion = 1;
            pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
            pfd.iPixelType = PFD_TYPE_RGBA;
            pfd.cColorBits = 32;
            pfd.cDepthBits = 32;
            pfd.iLayerType = PFD_MAIN_PLANE;

            int pixel_format = ChoosePixelFormat(hdc, &pfd);

            if (pixel_format == 0)
            {
                error_found = true;

                if (message_string_ptr != NULL)
                {
                    *message_string_ptr += "init_glew()
";
                    *message_string_ptr += "   ChoosePixelFormat() failed.
";
                    error_code = GetLastError();

                    if (error_code == 0)
                    {
                        *message_string_ptr += "Error code is 0
";
                    }
                    else
                    {
                        *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                        *message_string_ptr += "
";
                    }
                }
            }
            else
            {
                if (!SetPixelFormat(hdc, pixel_format, &pfd))
                {
                    error_found = true;

                    if (message_string_ptr != NULL)
                    {
                        *message_string_ptr += "init_glew()
";
                        *message_string_ptr += "   SetPixelFormat() failed.
";
                        error_code = GetLastError();

                        if (error_code == 0)
                        {
                            *message_string_ptr += "Error code is 0
";
                        }
                        else
                        {
                            *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                            *message_string_ptr += "
";
                        }
                    }
                }
                else
                {
                    // Create the false, old style context (OpenGL 2.1 and before)
                    fake_hglrc = wglCreateContext(hdc);
                    if(fake_hglrc == NULL)
                    {
                        error_found = true;

                        if (message_string_ptr != NULL)
                        {
                            *message_string_ptr += "init_glew()
";
                            *message_string_ptr += "   wglCreateContext() failed.
";
                            error_code = GetLastError();

                            if (error_code == 0)
                            {
                                *message_string_ptr += "Error code is 0
";
                            }
                            else
                            {
                                *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                                *message_string_ptr += "
";
                            }
                        }
                    }
                    else
                    {
                        wglMakeCurrent(hdc, fake_hglrc);

                        if (glewInit() != GLEW_OK)
                        {
                            error_found = true;

                            if (message_string_ptr != NULL)
                            {
                                *message_string_ptr += "init_glew()
";
                                *message_string_ptr += "   Couldn't initialize GLEW!
";
                                error_code = GetLastError();

                                if (error_code == 0)
                                {
                                    *message_string_ptr += "Error code is 0
";
                                }
                                else
                                {
                                    *message_string_ptr += error_code_to_string(error_code, &string_buffer)->c_str();
                                    *message_string_ptr += "
";
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(fake_hglrc);
    DestroyWindow(fake_window_handle);

    return !error_found;
}

LRESULT CALLBACK fake_WndProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:

        return 0;
    }

    return DefWindowProc(hdlg, message, wParam, lParam);
}

I see where, because of the cut and paste, I did pass a different window size to glViewport() before setting up the buffers. That doesn’t seem to be the source of my problem. glRenderbufferStorageMultisample() and glTexImage2DMultisample() are being passed the same width and height.

Here’s the graphic C++ module. It in no way represents coding practices I use. Most notably, I have a strong aversion to passing global arguments.

For reference I’ve preserved, in commented out form, the original GLFW code.

This is basically a cut and paste of the tutorial’s sample code which can be found here, altered to use WGL instead of GLFW and broken into a function, main_from_tutorial_with_wgl(), called by the window procedure’s message loop’s WM_CREATE clause. The sample’s game loop code is loop_from_tutorial_with_wgl(), which is called when the message loop’s WM_TIMER is serviced.

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
// #include <GLFW/glfw3.h>

// GL includes
#include "Shader.h"
#include "Camera.h"

// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <opengl_utilities.h>

#include "learn_opengl_common.h"

// Properties
GLuint screenWidth = 800, screenHeight = 600;

#if 0
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
#endif

void Do_Movement();
GLuint generateMultiSampleTexture(GLuint samples);

// Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
bool keys[1024];
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;

// New globals for seperate loop function
GLuint framebuffer;
// GLFWwindow* window;
GLuint cubeVAO;
Shader *shader_ptr = NULL;

// The MAIN function, from here we start our application and run our Game loop
GRAPHICS_PARAMETER *main_from_tutorial_with_wgl(HWND hdlg, HINSTANCE hinstance,
                                                                std::string *message_string_ptr)
{

#if 0
    // Init GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
#endif

    int gl_errors_found_count = 0;
    std::string gl_error_message;
    bool error_found = false;
    GRAPHICS_PARAMETER *graphics_parameter_ptr = new GRAPHICS_PARAMETER();
    windows_camera_wrapper_basic::CAMERA_WRAPPER *free_camera_wrapper_ptr = NULL;
    opengl_camera::CAMERA *camera_ptr = NULL;
    vector<Mesh> *meshes_vector_ptr = NULL;

    HDC hdc = NULL;
    std::string local_message_string;
    RECT client_rect;
    int width = 0;
    int height = 0;

    graphics_parameter_ptr->set_hwnd(hdlg);

    graphics_parameter_ptr->set_hrc(init_opengl(hdlg, &hdc, hinstance, 3, 3,
        NULL,
        &local_message_string));

    if (graphics_parameter_ptr->get_hrc() == NULL)
    {
        error_found = true;

        if (message_string_ptr != NULL)
        {
            *message_string_ptr += "setup_splash_screen()
";
            *message_string_ptr += "   HRC is NULL.
";
            *message_string_ptr += local_message_string.c_str();
            local_message_string.erase();
        }
    }
    else
    {
        graphics_parameter_ptr->set_hdc(hdc);

        if (graphics_parameter_ptr->get_hrc() == NULL)
        {
            error_found = true;

            if (message_string_ptr != NULL)
            {
                *message_string_ptr += "setup_splash_screen()
";
                *message_string_ptr += "   OpenGL failed to open.
";
                *message_string_ptr += local_message_string.c_str();
                local_message_string.erase();
            }
        }
        else
        {
            GetClientRect(hdlg, &client_rect);
            width = client_rect.right - client_rect.left;
            height = client_rect.bottom - client_rect.top;

            graphics_parameter_ptr->set_client_rect(&client_rect);

            glViewport(0, 0, width, height);

            initialize_cameras(graphics_parameter_ptr, width, height, VK_TAB, VK_TAB, 0.001);
        }
    }

    screenWidth = width;
    screenHeight = height;

    // Initialize GLEW to setup the OpenGL Function pointers
    glewExperimental = GL_TRUE;
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, screenWidth, screenHeight);

    // Setup OpenGL options
    glEnable(GL_MULTISAMPLE); // Enabled by default on some drivers, but not all so always enable to make sure
    glEnable(GL_DEPTH_TEST);

    // Setup and compile our shaders
   // Shader shader("advanced.shv", "advanced.shf");
   // shader_ptr = &shader;
   shader_ptr = new Shader("advanced.shv", "advanced.shf");

    #pragma region "object_initialization"
    // Set the object data (buffers, vertex attributes)
    GLfloat cubeVertices[] = {
        // Positions       
        -0.5f, -0.5f, -0.5f, 
         0.5f, -0.5f, -0.5f, 
         0.5f,  0.5f, -0.5f, 
         0.5f,  0.5f, -0.5f,  
        -0.5f,  0.5f, -0.5f,  
        -0.5f, -0.5f, -0.5f, 

        -0.5f, -0.5f,  0.5f, 
         0.5f, -0.5f,  0.5f, 
         0.5f,  0.5f,  0.5f, 
         0.5f,  0.5f,  0.5f,  
        -0.5f,  0.5f,  0.5f, 
        -0.5f, -0.5f,  0.5f, 

        -0.5f,  0.5f,  0.5f, 
        -0.5f,  0.5f, -0.5f,  
        -0.5f, -0.5f, -0.5f,  
        -0.5f, -0.5f, -0.5f, 
        -0.5f, -0.5f,  0.5f, 
        -0.5f,  0.5f,  0.5f, 

         0.5f,  0.5f,  0.5f,  
         0.5f,  0.5f, -0.5f, 
         0.5f, -0.5f, -0.5f, 
         0.5f, -0.5f, -0.5f, 
         0.5f, -0.5f,  0.5f,  
         0.5f,  0.5f,  0.5f,  

        -0.5f, -0.5f, -0.5f, 
         0.5f, -0.5f, -0.5f,  
         0.5f, -0.5f,  0.5f, 
         0.5f, -0.5f,  0.5f, 
        -0.5f, -0.5f,  0.5f,  
        -0.5f, -0.5f, -0.5f,  

        -0.5f,  0.5f, -0.5f,  
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,  
         0.5f,  0.5f,  0.5f,  
        -0.5f,  0.5f,  0.5f,  
        -0.5f,  0.5f, -0.5f
    };
    // Setup cube VAO
    GLuint cubeVBO;
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &cubeVBO);
    glBindVertexArray(cubeVAO);
    glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glBindVertexArray(0);
    #pragma endregion


    // Framebuffers
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);  
    // Create a multisampled color attachment texture
    GLuint textureColorBufferMultiSampled = generateMultiSampleTexture(4);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);
    // Create a renderbuffer object for depth and stencil attachments
    GLuint rbo;
    glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo); 
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, screenWidth, screenHeight); 
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); 

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    return graphics_parameter_ptr;
}

void loop_from_tutorial_with_wgl(HWND hdlg, GRAPHICS_PARAMETER *graphics_parameter_ptr)
{
#if 0
    // Set frame time
    GLfloat currentFrame = glfwGetTime();
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;
#endif

    // Check and call events
    // glfwPollEvents();
    // Do_Movement();      

    report_opengl_error_in_popup(hdlg, "After glfwPollEvents()");
        
    // 1. Draw scene as normal in multisampled buffers
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);   
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    report_opengl_error_in_popup(hdlg, "After glClear()");

    // Set transformation matrices		
    shader_ptr->Use();
 
    report_opengl_error_in_popup(hdlg, "After shader_ptr->Use()");

    glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)screenWidth/(GLfloat)screenHeight, 0.1f, 1000.0f);
    glUniformMatrix4fv(glGetUniformLocation(shader_ptr->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
    glUniformMatrix4fv(glGetUniformLocation(shader_ptr->Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));	
    glUniformMatrix4fv(glGetUniformLocation(shader_ptr->Program, "model"), 1, GL_FALSE, glm::value_ptr(glm::mat4()));
        
    glBindVertexArray(cubeVAO);
    glDrawArrays(GL_TRIANGLES, 0, 36);
  
    report_opengl_error_in_popup(hdlg, "After glDrawArrays()");

    glBindVertexArray(0);

    // 2. Now blit multisampled buffer(s) to default framebuffers
    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);				
   
    report_opengl_error_in_popup(hdlg, "After glBindFramebuffer(GL_READ_FRAMEBUFFER) and glBindFramebuffer(GL_DRAW_FRAMEBUFFER)");

    glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);     
    
    report_opengl_error_in_popup(hdlg, "After glBlitFramebuffer()");
       
    // Swap the buffers
    //  glfwSwapBuffers(window);
    SwapBuffers(graphics_parameter_ptr->get_hdc());

    report_opengl_error_in_popup(hdlg, "At end of loop, after SwapBuffers()");
}

GLuint generateMultiSampleTexture(GLuint samples)
{
    GLuint texture;
    glGenTextures(1, &texture);

    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB8, screenWidth, screenHeight, GL_TRUE);
    glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

    return texture;
}

As always Dark Photon, I appreciate your assistance.

  Thanks
  Larry

Thanks again John:

I have an inline function that calls glGetError(). It has text arguments that describe where it was called from.

I’m trying to use WGL code that’s identical to the GLFW code to setup, draw and blit the frame buffer, with a call to glBlitFramebuffer(). I haven’t been able to find a difference that would cause the problem.

I don’t understand why OpenGL can’t provide more descriptive error messages.

I used to be a Unix programmer using Solaris. After a decade and a half of Microsoft’s abuse of developers I’ve come to accept it as a price for the massive user base. But I am looking at the Android platform.

What’s the reason OpenGL uses GL_INVALID_OPERATION to describe 10 different problems. The OpenGL code that detected the problem knows why it failed. Why not tell the developer?

  Thanks once more for your help.
  Larry

[QUOTE=larryl;1287187]I’m trying to use WGL code that’s identical to the GLFW code to setup, draw and blit the frame buffer, with a call to glBlitFramebuffer(). I haven’t been able to find a difference that would cause the problem.

I don’t understand why OpenGL can’t provide more descriptive error messages.[/QUOTE]

why not ? you know how glGetError() worls and you know a GL_INVALID_OPERATION is thrown somewhere, its a matter of minutes to find the function throwing that error. you said you have a “inliner” for that, try something like this:

#define CheckForGLErrors      CheckForGLErrors__(__FILE__, __LINE__);

void CheckForGLErrors__(const char* file, unsigned int line)
{
    string errorstring;
    for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)
    {
        if (error == GL_INVALID_ENUM)
            errorstring += " GL_INVALID_ENUM";
        if (error == GL_INVALID_VALUE)
            errorstring += " GL_INVALID_VALUE";
        if (error == GL_INVALID_OPERATION)
            errorstring += " GL_INVALID_OPERATION";
        if (error == GL_STACK_OVERFLOW)
            errorstring += " GL_STACK_OVERFLOW";
        if (error == GL_STACK_UNDERFLOW)
            errorstring += " GL_STACK_UNDERFLOW";
        if (error == GL_OUT_OF_MEMORY)
            errorstring += " GL_OUT_OF_MEMORY";
        if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
            errorstring += " GL_INVALID_FRAMEBUFFER_OPERATION";
        if (error == GL_CONTEXT_LOST)
            errorstring += " GL_CONTEXT_LOST";
    }
    if (!errorstring.empty())
    {
        cout
            << (char)7
            << "OpenGL Error: " << endl
            << "	Line: 	" << line << endl
            << "	File: 	" << file << endl
            << "	Errors: 	" << errorstring << endl << endl;
        cin.get();
    }
}

that gives you the file + line where the error was detected, with that anyone should be able to track the error throwing functiion in a few minutes. if it actually is glBlitFramebuffer(for example), you also know the reasons for the error. the next thing is to exclude each possible reason until 1 is left over. but without figuring out what function has thrown that error, you can guess all day long without much success …

a “more descriptive” message is what you get if you enable a debug message output, but that requires a debug context

https://www.khronos.org/opengl/wiki/Debug_Output

What code? In all probability, the original glGetError() simply read, cleared and returned the graphics hardware’s status flags. GL_INVALID_OPERATION means that the “invalid operation” flag was set.

[QUOTE=larryl;1287186]Here’s the OpenGL initialization code:


HGLRC init_opengl(HWND hwnd, HDC *hdc_ptr, HINSTANCE hinstance, int major_version,
    int minor_version, const int *PixelFormatAttribList_ptr, std::string *message_string_ptr)
{
    ...
    init_glew(hinstance, &local_message_string);

    if (major_version <= 2)
    {
        ...
        pixel_format = ChoosePixelFormat(hdc, &pfd);
        ...
        SetPixelFormat(hdc, pixel_format, &pfd);
        ...
        // Create the old style context (OpenGL 2.1 and before)
        return_val = wglCreateContext(hdc);
        ...
        wglMakeCurrent(hdc, return_val);
    }
    else
    {
        // Version number > 2
        if (WGLEW_ARB_create_context && WGLEW_ARB_pixel_format)
        {
            const int iPixelFormatAttribList[] =
            {
                ...
                WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
                WGL_SAMPLES_ARB, 16,
                0 // End of attributes list
            };

            if (PixelFormatAttribList_ptr == NULL)
            {
                PixelFormatAttribList_ptr = iPixelFormatAttribList;
            }

            ...
            wglChoosePixelFormatARB(hdc,
                PixelFormatAttribList_ptr,
                NULL,
                1,
                &pixel_format,
                (UINT*)&number_of_formats_found);
            ...
            SetPixelFormat(hdc, pixel_format, NULL);
                return_val = wglCreateContextAttribsARB(hdc, 0, iContextAttribs);
            ...
            wglMakeCurrent(hdc, return_val);
        }
    }

    ...
}

bool init_glew(HINSTANCE hinstance, std::string *message_string_ptr)
{
    // This code is inspired by http://www.mbsoftworks.sk/index.php?page=tutorials&series=1&tutorial=2

    // From https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL)
    // The key problem is this: the function you use to get WGL extensions is,
    // itself, an OpenGL extension. Thus like any OpenGL function, it requires an
    // OpenGL context to call it. So in order to get the functions we need to
    // create a context, we have to... create a context.
    bool error_found = false;
    WNDCLASS fake_window_class;
    HDC hdc = NULL;
    HGLRC fake_hglrc = NULL;
    DWORD error_code = 0;
    std::string string_buffer;

    ...
    HWND fake_window_handle = CreateWindow(FAKE_OPENGL_CLASS_NAME, ...);
    ...
    hdc = GetDC(fake_window_handle);

    ...
    int pixel_format = ChoosePixelFormat(hdc, &pfd);
    ...
    SetPixelFormat(hdc, pixel_format, &pfd);
    ...
    fake_hglrc = wglCreateContext(hdc);
    ...
    wglMakeCurrent(hdc, fake_hglrc);
    ...
    glewInit();
    ...
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(fake_hglrc);
    DestroyWindow(fake_window_handle);

    return !error_found;
}

[/QUOTE]

Ok, there two context creation paths here. The question is which one is your code taking.

I’ve excised all the error handling from your code snippet above in this post so we can see what the code is actually trying to do.

Notice that one path (the path you’re likely taking, where “major_version > 2”) is using WGL_ARB_pixel_format to create a context using a “multisample” pixel format, where the number of samples is 16 (notice the values set for WGL_SAMPLE_BUFFERS_ARB and WGL_SAMPLES_ARB). That means that, if it succeeds, your system framebuffer will be a 16x MSAA framebuffer.

This number of samples (16) is different than the number of samples you were using for your off-screen FBO textures (4). If your code is taking this path, then this could very well be the cause of your GL_INVALID_OPERATION when blitting from the off-screen FBO to the system framebuffer.

Try WGL_SAMPLE_BUFFERS_ARB = 0 (GL_FALSE) instead and just delete the WGL_SAMPLES_ARB = 16 line. That should create a single-sampled system framebuffer, which ideally will clear up your problem.

Thanks again Dark Photon:

I doubt that I'll ever write code for old OpenGL again, I did one long ago, so I'll change that and just test for versions 3.3 and above.  

[QUOTE=Dark Photon;1287196]Notice that one path (the path you’re likely taking, where “major_version > 2”) is using WGL_ARB_pixel_format to create a context using a “multisample” pixel format, where the number of samples is 16 (notice the values set for WGL_SAMPLE_BUFFERS_ARB and WGL_SAMPLES_ARB). That means that, if it succeeds, your system framebuffer will be a 16x MSAA framebuffer.

This number of samples (16) is different than the number of samples you were using for your off-screen FBO textures (4). If your code is taking this path, then this could very well be the cause of your GL_INVALID_OPERATION when blitting from the off-screen FBO to the system framebuffer.

Try WGL_SAMPLE_BUFFERS_ARB = 0 (GL_FALSE) instead and just delete the WGL_SAMPLES_ARB = 16 line. That should create a single-sampled system framebuffer, which ideally will clear up your problem.[/QUOTE]

That was stupid.

I’ve been so focused on what the GLFW code was setting that I hadn’t noticed the sample size I’d set. I added those to the pixel format attributes several weeks ago and it dropped off of my radar.

I’ve tried WGL_SAMPLE_BUFFERS_ARB of 0 and then WGL_SAMPLE_BUFFERS_ARB of GL_TRUE with WGL_SAMPLES_ARB of 4.

Both cases stopped the error, but the display is still blank.

I have another problem I need to deal with right now. I’ll work on this tomorrow.

  Thanks again
  Larry