【问题标题】:Is there any reason glUseProgram might crash my program? (C Win32)glUseProgram 是否有任何原因可能会使我的程序崩溃? (C Win32)
【发布时间】:2020-05-29 08:55:38
【问题描述】:

我正在用 C 语言学习带有 Win32 的 OpenGL,并且我已经到了想要实现着色器的地步。我这样做更多的是作为一种学习经验而不是一个项目,所以我决定不使用像 glew 之类的典型扩展牧马人库。这是我的扩展抓取头文件:

glExtensions.h:

#include <windows.h>

#define WGL_DRAW_TO_WINDOW_ARB         0x2001
#define WGL_ACCELERATION_ARB           0x2003
#define WGL_SWAP_METHOD_ARB            0x2007
#define WGL_SUPPORT_OPENGL_ARB         0x2010
#define WGL_DOUBLE_BUFFER_ARB          0x2011
#define WGL_PIXEL_TYPE_ARB             0x2013
#define WGL_COLOR_BITS_ARB             0x2014
#define WGL_DEPTH_BITS_ARB             0x2022
#define WGL_STENCIL_BITS_ARB           0x2023
#define WGL_FULL_ACCELERATION_ARB      0x2027
#define WGL_SWAP_EXCHANGE_ARB          0x2028
#define WGL_TYPE_RGBA_ARB              0x202B
#define WGL_CONTEXT_MAJOR_VERSION_ARB  0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB  0x2092
#define GL_ARRAY_BUFFER                   0x8892
#define GL_STATIC_DRAW                    0x88E4
#define GL_FRAGMENT_SHADER                0x8B30
#define GL_VERTEX_SHADER                  0x8B31
#define GL_COMPILE_STATUS                 0x8B81
#define GL_LINK_STATUS                    0x8B82
#define GL_INFO_LOG_LENGTH                0x8B84
#define GL_TEXTURE0                       0x84C0
#define GL_BGRA                           0x80E1
#define GL_ELEMENT_ARRAY_BUFFER           0x8893

PROC glCreateProgram;
PROC glCreateShader;
PROC glShaderSource;
PROC glCompileShader;
PROC glAttachShader;
PROC glLinkProgram;
PROC glUseProgram;
PROC glGetShaderInfoLog;
PROC glDeleteShader;
PROC glDetachShader;
PROC glIsProgram;
PROC glGenBuffers;
PROC glBindBuffer;
PROC glBufferData;
PROC glEnableVertexAttribArray;
PROC glVertexAttribPointer;
PROC glGenVertexArrays;
PROC glBindVertexArray;

void GLEInit(int);

这里是c文件, glExtionsions.c

#include "glExtensions.h"
#include <windows.h>
#include <gl/gl.h>
#include "consoleUtil.h"

extern PROC glCreateProgram;
extern PROC glCreateShader;
extern PROC glShaderSource;
extern PROC glCompileShader;
extern PROC glAttachShader;
extern PROC glLinkProgram;
extern PROC glUseProgram;
extern PROC glGetShaderInfoLog;
extern PROC glDeleteShader;
extern PROC glDetachShader;
extern PROC glIsProgram;
extern PROC glGenBuffers;
extern PROC glBindBuffer;
extern PROC glBufferData;
extern PROC glEnableVertexAttribArray;
extern PROC glVertexAttribPointer;
extern PROC glGenVertexArrays;
extern PROC glBindVertexArray;

PROC getProc(int hOut, const char* name) {
    PROC p = wglGetProcAddress(name);
    if(p == 0 ||
        (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
        (p == (void*)-1) )
    {
        HMODULE module = LoadLibraryA("opengl32.dll");
        p = GetProcAddress(module, name);
    }

    return p;
}

void GLEInit(int hOut) {
    glCreateProgram = getProc(hOut, "glCreateProgram");
    glCreateShader = getProc(hOut, "glCreateShader");
    glShaderSource = getProc(hOut, "glShaderSource");
    glCompileShader = getProc(hOut, "glCompileShader");
    glAttachShader = getProc(hOut, "glAttachShader");
    glLinkProgram = getProc(hOut, "glLinkProgram");
    glUseProgram = getProc(hOut, "glUseProgram");
    glGetShaderInfoLog = getProc(hOut, "glGetShaderInfoLog");
    glDeleteShader = getProc(hOut, "glDeleteShader");
    glDetachShader = getProc(hOut, "glDetachShader");
    glIsProgram = getProc(hOut, "glIsProgram");
    glGenBuffers = getProc(hOut, "glGenBuffers");
    glBindBuffer = getProc(hOut, "glBindBuffer");
    glBufferData = getProc(hOut, "glBufferData");
    glEnableVertexAttribArray = getProc(hOut, "glEnableVertexAttribArray");
    glVertexAttribPointer = getProc(hOut, "glVertexAttribPointer");
    glGenVertexArrays = getProc(hOut, "glGenVertexArrays");
    glBindVertexArray = getProc(hOut, "glBindVertexArray");
}

GLEInit 在窗口创建后运行,然后我运行这段代码

int createBasicProgram(int hOut) {
    //vertex shader bullcrap
    FILE* basicVSFile = getFile("shaders/basic.vs");
    FILEDATA* vsfd = getFileData(hOut, basicVSFile);
    close(basicVSFile);

    int vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vs, vsfd->numLines, vsfd->lines, vsfd->lengths);
    glCompileShader(vs);

    int actualLength = 0;
    glGetShaderInfoLog(vs, ERROR_BUFFER_SIZE, &actualLength, errorBuffer);
    if (actualLength > 0) {
        print(hOut, "deleting vertex shader\n");
        glDeleteShader(vs);
        print(hOut, errorBuffer);
        return -1;
    }
    freeFileData(vsfd);


    //fragment shader bullcrap
    FILE* basicFSFile = getFile("shaders/basic.fs");
    FILEDATA* fsfd = getFileData(hOut, basicFSFile);
    int fs = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fs, fsfd->numLines, fsfd->lines, fsfd->lengths);
    glCompileShader(fs);

    glGetShaderInfoLog(fs, ERROR_BUFFER_SIZE, &actualLength, errorBuffer);
    if (actualLength > 0) {
        print(hOut, "deleting fragment shader\n");
        glDeleteShader(fs);
        print(hOut, errorBuffer);
        return -1;
    }
    freeFileData(fsfd);

    int progId = glCreateProgram();
    glAttachShader(progId, vs);
    glAttachShader(progId, fs);
    glLinkProgram(progId);
    glGetShaderInfoLog(progId, ERROR_BUFFER_SIZE, &actualLength, errorBuffer);
    if (actualLength > 0) {
        print(hOut, errorBuffer);
    }

    glDetachShader(progId, vs);
    glDetachShader(progId, fs);
    glDeleteShader(vs);
    glDeleteShader(fs);

    return progId;
}

void init(hOut) {
    bp = createBasicProgram(hOut);

    glGenVertexArrays(1, &vaoId);
    glBindVertexArray(vaoId);
    glGenBuffers(1, &vboId);
    glBindBuffer(GL_ARRAY_BUFFER, vboId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
}

此代码不会导致错误,但是当我添加简单语句时:

glUseProgram(bp);

进入显示函数,每帧调用一次,程序运行一秒钟,控制台和程序窗口打开并黑屏大约 3 秒钟,然后崩溃,没有构建错误或输出错误可跟踪。这个 glUseProgram 是否有任何原因会导致我的程序崩溃?

堆栈转储显示:“Exception: STATUS_ACCESS_VIOLATION at rip=001004016E6”,其中包含一堆内存位置和我不理解的内容。

【问题讨论】:

  • 你肯定在链接后查询了错误的错误日志。应该是 glGetProgramInfoLog 而不是 glGetShaderInfoLog
  • 好吧,这些疯狂的东西是干什么用的? if(p == 0 || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || (p == (void*)-1) )。请注意,void 指针和函数指针不能可靠地混合使用。
  • 请在您的上下文中描述“崩溃”的含义。该程序是否只是在没有任何消息的情况下自行关闭?没有“无法执行位置...”错误?
  • @Lundin: "这个疯狂的东西是干什么用的?" WGL 函数指针检索函数不能可靠地为实现未提供的函数返回 nullptr。它可以返回过多的无效指针值,因此您必须根据所有这些值检查指针。现在,这几乎可以肯定是从另一个加载器复制而来的,后者从另一个加载器复制了它,等等,所以目前尚不清楚这在今天的实现中是否仍然需要,但在某些时候需要它。
  • How to debug small programs。如果您正在学习,请花时间学习如何使用调试器。这段时间花得很值。这是您将比任何其他工具使用得更多的工具。

标签: c winapi opengl


【解决方案1】:

我又花了 7 个小时在这个项目上工作,我终于弄明白了。 “它”是指我的旧代码有几处问题。前两个问题是 glExtensions.c 和 glExtensions.h,最初我所做的只是调用使用 getProc 来获取函数指针并将其放入 PROC 宏中,这根本不是它需要的。 PROC 没有使用正确的调用约定或指定正确的参数,而是需要包含 glext.h,它由 khronos 维护,可以在此处找到:https://www.khronos.org/registry/OpenGL/api/GL/glext.h。关于这些函数的作用的描述也可以在这里找到:https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions。需要更改的另一件事是在程序创建之前调用了 glUseProgram。程序创建代码是正确的,但是我在 WM_PAINT 和 WM_SIZE 上调用 display,这两个都被放入消息队列并在调用 GLEInit 之前处理。相反,如果 PeekMessage 找不到任何消息,则应在消息循环中调用 display。

这是自上次以来更改的相关代码:

glExtensions.h:

#include <windows.h>
#include "glext.h"
#include "wglext.h"

PFNGLCREATEPROGRAMPROC glCreateProgram;
PFNGLCREATESHADERPROC glCreateShader;
PFNGLSHADERSOURCEPROC glShaderSource;
PFNGLCOMPILESHADERPROC glCompileShader;
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLLINKPROGRAMPROC glLinkProgram;
PFNGLUSEPROGRAMPROC glUseProgram;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
PFNGLDELETESHADERPROC glDeleteShader;
PFNGLDETACHSHADERPROC glDetachShader;
PFNGLISPROGRAMPROC glIsProgram;
PFNGLGENBUFFERSPROC glGenBuffers;
PFNGLBINDBUFFERPROC glBindBuffer;
PFNGLBUFFERDATAPROC glBufferData;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
PFNGLGETINTEGERI_VPROC glGetIntegeri_v;
PFNGLGETSTRINGIPROC glGetStringi;
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT;

void GLEInit(HANDLE);

void checkGLError(HANDLE, char*);

glExtensions.c:

#include "glExtensions.h"

#include <windows.h>
#include <gl/gl.h>
#include "consoleUtil.h"

void* getProc(HANDLE hOut, const char* name) {
    print(hOut, "loading %s\n", name);
    void* p = wglGetProcAddress(name);
    if (p == NULL) {
        int e = GetLastError();
        print(hOut, "error loading %s: %i\n", name, e);
        return NULL;
    }
    if(p == 0 ||
        (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
        (p == (void*)-1) )
    {
        HMODULE module = LoadLibraryA("opengl32.dll");
        if (module == NULL) {
            int e = GetLastError();
            print(hOut, "error loading library \"opengl32.dll\" for function pointer %s: %i\n", name, e);
            return NULL;
        }
        p = GetProcAddress(module, name);
        if (p == NULL) {
            int e = GetLastError();
            print(hOut, "error loading %s with getProcAddress: %i\n", name, e);
            return NULL;
        }
    }

    print(hOut, "success loading\n");
    return p;
}

void checkGLError(HANDLE hOut, char* desc) {
    GLenum e = glGetError();
    if (e > 0) {
        print(hOut, "Error %i: %s\n", e, desc);
        return;
    }
    print(hOut, "%s completed with no errors\n", desc);
}

void GLEInit(HANDLE hOut) {
    glCreateProgram = (PFNGLCREATEPROGRAMPROC)getProc(hOut, "glCreateProgram");
    glCreateShader = (PFNGLCREATESHADERPROC)getProc(hOut, "glCreateShader");
    glShaderSource = (PFNGLSHADERSOURCEPROC)getProc(hOut, "glShaderSource");
    glCompileShader = (PFNGLCOMPILESHADERPROC)getProc(hOut, "glCompileShader");
    glAttachShader = (PFNGLATTACHSHADERPROC)getProc(hOut, "glAttachShader");
    glLinkProgram = (PFNGLLINKPROGRAMPROC)getProc(hOut, "glLinkProgram");
    glUseProgram = (PFNGLUSEPROGRAMPROC)getProc(hOut, "glUseProgram");
    glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)getProc(hOut, "glGetShaderInfoLog");
    glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)getProc(hOut, "glGetProgramInfoLog");
    glDeleteShader = (PFNGLDELETESHADERPROC)getProc(hOut, "glDeleteShader");
    glDetachShader = (PFNGLDETACHSHADERPROC)getProc(hOut, "glDetachShader");
    glIsProgram = (PFNGLISPROGRAMPROC)getProc(hOut, "glIsProgram");
    glGenBuffers = (PFNGLGENBUFFERSPROC)getProc(hOut, "glGenBuffers");
    glBindBuffer = (PFNGLBINDBUFFERPROC)getProc(hOut, "glBindBuffer");
    glBufferData = (PFNGLBUFFERDATAPROC)getProc(hOut, "glBufferData");
    glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)getProc(hOut, "glEnableVertexAttribArray");
    glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)getProc(hOut, "glVertexAttribPointer");
    glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)getProc(hOut, "glGenVertexArrays");
    glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)getProc(hOut, "glBindVertexArray");
    glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)getProc(hOut, "glGetIntegeri_v");
    glGetStringi = (PFNGLGETSTRINGIPROC)getProc(hOut, "glGetStringi");
    wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)getProc(hOut, "wglSwapIntervalEXT");
    wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)getProc(hOut, "wglGetSwapIntervalEXT");
}

【讨论】:

  • 所以该错误部分存在于未发布的某些 Windows WndProc 消息循环中?
  • 一旦我修复了我的扩展加载器文件,问题是当我调用 ShowWindow(hWnd) 时,它会将 WM_SIZE 和 WM_PAINT 插入到消息队列中,这被称为显示函数。这引发错误的原因是我在显示窗口之前没有调用 GLEInit(),并且着色器程序是在 GLEInit() 中创建的。因此,第一个显示函数是告诉 opengl 使用一个不存在的程序,这给了我一个 seg 错误并导致崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-24
  • 1970-01-01
相关资源
最近更新 更多