OpenGL 版本在使用 ifstream 时太低
OpenGL version too low when using ifstream
我正在为2D Roguelike编写引擎的一些元素。我正处于我希望能够打开 .pngs 的部分。(我手动做事,因为我喜欢了解这些事情。所以我创建了一个PngLoader
类,并开始用它做一些基本的事情,比如......打开文件。出于某种原因,这会破坏 OpenGL GLFunctionFinder
类,该类执行类似于 GLEW 的操作,但手动操作除外。
当OpenGL版本太低时,GLFF基本上会使程序崩溃;这是预期的行为。(可能是未设置函数指针上的段错误。我可以通过让它更优雅地崩溃来"修复"这个问题,但谁在乎呢?GLFF通常运行良好,因为我的显卡运行OpenGL 4.3左右,但是几天前当驱动程序切换到集成图形驱动程序(仅执行OpenGL版本1.1)时,我确实中断了它。这是通过更改图形仪表板中的某些设置来修复的。
因此,当我写这样的东西时,我今天出现的问题出现了:
class ifcontainerclass {
std::ifstream fs;
};
/* other code */
int WINAPI WinMain(/* ... */) {
GLFunctionFinder ff;
ff.interrogateWindows();
ifcontainerclass ifcc;
/* GL code and main loop */
return 0;
}
。OpenGL 上下文卡在 1.1 版。如果我ifstream
更改为fstream
,我会得到我期望的更高版本的上下文,问题就会消失。
我还在测试中发现,如果我注释掉GL code and main loop
区域,问题会再次消失。"版本太低"检查是在 GLFunctionFinder::interrogateWindows()
中完成的,而不是在后来的 GL 代码中完成的,因此仍在检查条件。(经过一些测试,我发现注释掉MSG
结构是使问题消失的原因。
我目前的信念是编译器正在做一些魔术,导致Windows/Intel/NVidia仅在...我真的不知道什么时候。这个问题看起来真的很武断。
我可能会考虑摆脱我出于懒惰而使用的全局HDC
和全局HGLRC
,因为我认为问题与事物的初始化方式/编译器如何安排初始化这些东西有关,将它们拉出全局范围将让我更有效地检查和控制该过程。我在GLFunctionFinder
中通过使用一个static void * GlobalAddr = this
文件范围的指针来做到这一点,将其转换为虚拟窗口WndProc
中的GLFunctionFinder
,并让HDC
和HGLRC
成为GLFunctionFinder
的成员变量,可通过指针访问。我可能会在我的主窗口中尝试类似的东西;无论如何,我一直需要清理全局范围的东西。我可以做的另一件事是在调试器中运行每个版本并查看它的差异,尽管我不愿意这样做,因为调试在我的 IDE 中并没有真正正确设置,而且我不期待修复它。
我想我可以通过使用 fstream
而不是 ifstream
来度过这一关,但我对不理解如此奇怪的问题感到不舒服,因为它表明在我有 10k 行代码之前,我应该意识到某种不稳定性任意停止运行,只能通过更改其他地方看似完全不相关的东西来修复。
问题:
- 到底发生了什么?这里的核心问题是什么?
- 为什么将
ifstream
更改为fstream
解决问题? - 为什么注释掉
MSG
结构可以解决问题?
PS:NvOptimusEnablement = 0x00000001
没有解决问题。
PPS:Qt中的MinGW 4.9.2(作为IDE,没有Qt库)和CMake
编辑:在确定Qt的调试器在-ggdb
传递给g++时工作后,我逐步浏览了代码,发现GLFunctionFinder
中的PIXELFORMATDESCRIPTOR
没有被分配;我将属性分配给某个随机临时变量而不是成员变量,而ChoosePixelFormat
正在使用成员变量。由于您获得的上下文取决于您指定的像素类型,因此我实际上是从 Windows 请求不确定的设备上下文。编译的细节决定了在PIXELFORMATDESCRIPTOR
中放入了哪些随机垃圾,而声明一个ifstream
而不是一个fstream
恰好将错误的随机垃圾放在那个区域。
这个问题是通过在定义临时pfd
后向GLFunctionFinder
的构造函数添加一些具有this->pfd_ = pfd;
效果的东西来解决的。
编辑2:为了满足我对"题外话"标志含义的理解,我将提供核心问题的最小示例:
主.cpp:
#include <windows.h>
#include <sstream>
#include <GL/gl.h>
HDC h_dc;
HGLRC h_context;
LRESULT CALLBACK MainWndProc(_In_ HWND h_wnd,
_In_ UINT u_msg,
_In_ WPARAM w_param,
_In_ LPARAM l_param) {
switch(u_msg) {
case WM_CREATE: {
PIXELFORMATDESCRIPTOR pfd; // <-- This was the error source, (pfd not set to an
// accelerated format, but only sometimes)
// except in my code it was harder to see than
// this.
h_dc = GetDC(h_wnd);
int pfint = ChoosePixelFormat(h_dc, &pfd);
SetPixelFormat(h_dc, pfint, &pfd);
h_context = wglCreateContext(h_dc);
wglMakeCurrent(h_dc, h_context);
const unsigned char * version_string =
static_cast<const unsigned char *>(glGetString(GL_VERSION));
if(version_string[0] == '1' || version_string[0] == '2') {
std::stringstream ss;
ss << "OpenGL version (" << version_string << ") is too low";
MessageBox(NULL, ss.str().c_str(), "Error", MB_OK | MB_ICONERROR);
}
break;
}
case WM_DESTROY:
PostQuitMessage(EXIT_SUCCESS);
break;
default:
return DefWindowProc(h_wnd, u_msg, w_param, l_param);
}
return 1;
}
int WINAPI WinMain( HINSTANCE h_inst,
HINSTANCE h_previnst,
LPSTR cmd_str_in,
int cmd_show_opt) {
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_OWNDC;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = h_inst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "MAINWIN";
wc.hIconSm = NULL;
RegisterClassEx(&wc);
HWND h_wnd = CreateWindowEx(0,
"MAINWIN",
"MCVE Program",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
640,
480,
NULL,
NULL,
h_inst,
NULL);
return EXIT_SUCCESS;
}
CMakeLists.txt:
project(mcve_pfd_problem)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} WIN32 ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} opengl32)
万一有人跳到最后,问题就解决了,但我不知道我应该如何表明这一点。
因此,我实际看到的是由于默认初始化结构而导致的未定义行为的影响,其中包含的值未初始化:
class GLFunctionFinder {
PIXELFORMATDESCRIPTOR pfdarr_;
/* other code */
GLFunctionFinder();
setupContext();
/* other code */
}
GLFunctionFinder::GLFunctionFinder() {
/* other code */
PIXELFORMATDESCRIPTOR pfd = { /* things */ };
// Missing: pfdarr_ = pfd;
// pfdarr_ never gets set
}
GLFunctionFinder::setupContext() {
// Undefined behavior:
int px_format_default = ChoosePixelFormat(this->h_cd, &(this->pfdarr_));
/* other code */
}
这给了ChoosePixelFormat
pfdarr_
中的任何垃圾.当我最初写这篇文章时,它表现得好像没有问题,因为显然垃圾数据"看起来像"加速像素格式类型,ChoosePixelFormat
会给我一个产生我所追求的OpenGL上下文的int format
。它就这样呆了一段时间,因为它一直在工作。
从fstream
切换到ifstream
改变了编译器布局/优化程序方式的一些细节,pfdarr_
中的垃圾数据更改为"看起来像"未加速的格式。这导致获得错误的上下文,从而导致OpenGL版本检查失败。注释掉MSG
结构和部分事件循环的故事基本相同:碰巧编译器发出的东西会产生我想要的 OpenGL 上下文。
我昨晚正在编译我在 Edit 2 中给出的代码,它给了我一个 1.1 的上下文。今天早上,完全相同的代码,没有错误;移动了MessageBox
,发现我得到了 4.3 上下文。有趣的错误。
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- ifstream什么都没读
- 导入库可以跨dll版本工作吗
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 在clang++预处理器中确定gcc工具链版本
- 码头化的C++应用程序是否向后兼容早期的内核版本
- 不同的Visual Studio版本中缺少.dll
- 用符号版本替换对函数的所有调用
- luaL_dofile在已知良好的字节码上失败,可以使用未编译的版本
- 正在解码MSVC 32位版本的程序集(作业).没有手术做什么
- 我需要分发哪些版本的可再分发文件
- CV_OCL_RUN宏如何在OpenCV(版本3.4.5)的goodFeaturesToTrack实现中工作?
- ifstream文件在从行中读取时被清除
- 在运行时读取 libstdc++ 版本
- 如何声明一个可以在整个程序中使用的全局 2d 3d 4d .. 数组(堆版本)变量?
- FindPackageHandleStandardArgs.cmake:137 的 CMake 错误(消息):找不到 Boost (缺少:正则表达式)(找到合适的版本"1.72.0",
- Vulkan SDK 版本 1.1.85.0 在 Kubuntu 18.10 上链接
- 如何正确实现与基类不同的版本?
- 从预处理器获取 Windows 版本(C++ Win32)
- OpenGL 版本在使用 ifstream 时太低