因此,我正在我的Win32应用程序中设置我的OpenGL上下文。我正在设置一个多线程环境,一个线程处理窗口消息,另一个线程处理OpenGL渲染调用。到目前为止,我的工作流程如下:
线程A:
然后,在线程B中:
问题是,线程B中的wglMakeCurrent返回false,getLastError返回170(资源正在使用中)。我所读到的一切都暗示这意味着渲染上下文已经在线程A中使用了,但是我甚至在线程B被创建之前就已经在线程A中显式地调用了“wglMakeCurrent(NULL,NULL)”。
可能出了什么问题?
PS. 为了清楚起见,我并不是试图从不同的并发线程运行 OpenGL 调用。每个 OpenGL 调用都将从线程 B 进行,但在线程 B 存在之前创建渲染上下文除外。
编辑:这是一些源代码。这是在线程 A 中初始化窗口的函数:
Window* Window::init(void)
{
/* If the singleton exists already, just return a pointer to it */
if (singleton)
return singleton;
/* Allocate the singleton and check for errors */
singleton = new Window();
if (singleton == NULL)
{
fatalerror("In Window::init():\n");
fatalmore("Memory allocation failure.\n");
fatalmore("Could not allocate singleton.\n");
_getch();
exit(1);
}
/* Register window class */
WNDCLASSEX wc;
memset(&wc, 0, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = wndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = APP_NAME;
if (!RegisterClassEx(&wc))
{
fatalerror("In Window::init():\n");
fatalmore("Failed to register window class.\n");
fatalmore("Error code: '%i'\n", GetLastError());
clean();
_getch();
exit(1);
}
/* Read settings */
int width = Settings::get()->screenWidth;
int height = Settings::get()->screenHeight;
DWORD exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD style = WS_OVERLAPPEDWINDOW;
/* Are we using fullscreen mode? */
if (Settings::get()->fullscreen)
{
width = Settings::get()->fullscreenWidth;
height = Settings::get()->fullscreenHeight;
DEVMODE dm;
memset(&dm, 0, sizeof(DEVMODE));
dm.dmSize = sizeof(DEVMODE);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmBitsPerPel = 32;
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
warning("in Window::init():\n");
warnmore("The requested fullscreen mode is not supported.\n");
warnmore("Will continue in windowed mode.\n");
width = Settings::get()->screenWidth;
height = Settings::get()->screenHeight;
Settings::get()->fullscreen = false;
}
else
{
exStyle = WS_EX_APPWINDOW;
style = WS_POPUP;
}
}
/* Create the window */
singleton->wnd = CreateWindowEx( exStyle,
APP_NAME, APP_NAME,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | style,
CW_USEDEFAULT, CW_USEDEFAULT,
width, height,
NULL,
NULL,
GetModuleHandle(NULL),
NULL );
if (!singleton->wnd)
{
fatalerror("In Window::init():\n");
fatalmore("Could not create window.\n");
fatalmore("Error code: '%i'\n", GetLastError());
clean();
_getch();
exit(1);
}
/* Get the DC */
singleton->dc = GetDC(singleton->wnd);
if (!singleton->dc)
{
fatalerror("In Window::init():\n");
fatalmore("Failed to get display context.\n");
fatalmore("Error code: '%i'\n", GetLastError());
clean();
_getch();
exit(1);
}
/* Choose a pixel format */
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pf = ChoosePixelFormat(singleton->dc, &pfd);
if (!pf)
{
fatalerror("In Window::init():\n");
fatalmore("Failed to choose a pixel format.\n");
fatalmore("Error code: '%i'\n", GetLastError());
clean();
_getch();
exit(1);
}
/* Set the pixel format */
if (!SetPixelFormat(singleton->dc, pf, &pfd))
{
fatalerror("In Window::init():\n");
fatalmore("Failed to set pixel format '%i'.\n", pf);
fatalmore("Error code: '%i'\n", GetLastError());
clean();
_getch();
exit(1);
}
/* Create our fake, temporary OpenGL context */
HGLRC temprc = wglCreateContext(singleton->dc);
if (!temprc)
{
fatalerror("In Window::init():\n");
fatalmore("Failed to create a temporary context.\n");
fatalmore("Error code: '%i'\n", GetLastError());
clean();
_getch();
exit(1);
}
wglMakeCurrent(singleton->dc, temprc);
/* Initialize the OpenGL extensions we need */
if (!initGLCreationExtensions())
{
fatalerror("In Window::init():\n");
fatalmore("Failed to initialize OpenGL context creation extensions.\n");
clean();
_getch();
exit(1);
}
/* Check for OpenGL version */
int majorver = 0, minorver = 0;
glGetIntegerv(GL_MAJOR_VERSION, &majorver);
glGetIntegerv(GL_MINOR_VERSION, &minorver);
if (majorver < 3 || (majorver == 3 && minorver < 1))
{
fatalerror("In Window::init():\n");
fatalmore("OpenGL version 3.1 or higher is required.\n");
clean();
_getch();
exit(1);
}
/* Define context attributes */
int contextAttribs[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, majorver,
WGL_CONTEXT_MINOR_VERSION_ARB, minorver,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
0
};
/* Create the real OpenGL context */
singleton->rc = wglCreateContextAttribsARB(singleton->dc, NULL, contextAttribs);
if (!singleton->rc)
{
fatalerror("In Window::init():\n");
fatalmore("Failed to create OpenGL context.\n");
fatalmore("Error code: '%i'\n", GetLastError());
clean();
_getch();
exit(1);
}
ShowWindow(singleton->wnd, SW_SHOW);
/* Delete the fake context */
wglMakeCurrent(NULL, NULL);
wglDeleteContext(temprc);
info("Successfully created window.\n");
printf("\n");
return singleton;
}
这是在线程B中运行的启动函数:
bool Window::renderThreadStartupFunc(void)
{
if (!singleton)
return false;
if (!singleton->dc)
return false;
if (!wglMakeCurrent(singleton->dc, singleton->rc))
{
error("In Window::renderThreadStartupFunc():\n");
errormore("Could not make context current.\n");
errormore("Error code: '%i'\n", GetLastError());
return false;
}
if (!initGLExtensions())
{
error("In Window::renderThreadStartupFunc():\n");
errormore("Failed to initialize OpenGL extensions.\n");
return false;
}
return true;
}
下面是负责启动和关闭线程b的代码。它在一个单独的类中运行,并使用c 11的std::thread库。
Renderer* Renderer::init(void)
{
/* If the singleton exists already, just return a pointer to it */
if (singleton)
return singleton;
/* Allocate the singleton and check for errors */
singleton = new Renderer();
if (singleton == NULL)
{
fatalerror("In Renderer::init():\n");
fatalmore("Memory allocation failure.\n");
fatalmore("Could not allocate singleton.\n");
_getch();
exit(1);
}
Window::get();
singleton->running = true;
renderThread = new thread(main);
if (renderThread == NULL)
{
fatalerror("In Renderer::init():\n");
fatalmore("Memory allocation failure.\n");
fatalmore("Could not allocate rendering thread.\n");
_getch();
exit(1);
}
Message startup;
startup.type = MT_STARTUP;
singleton->queue.push(startup);
return singleton;
}
void Renderer::clean(void)
{
if (!singleton)
return;
Message shutdown;
shutdown.type = MT_SHUTDOWN;
singleton->queue.push(shutdown);
if (renderThread)
{
renderThread->join();
delete renderThread;
}
}
一些建议:
>
创建适当的上下文,临时助手上下文为当前上下文(扩展函数指针与Windows中的活动上下文绑定),即在创建适当上下文之前,不要释放当前上下文中的助手上下文,也不要释放助手上下文。
在启动线程B之前,在适当的上下文和hdc上调用wglMakeCurrent(…),调用glFinish(),然后调用wglMakeCurrent(NULL,NULL)并删除临时上下文。对于一些小车司机来说,这是一个肮脏的变通方法。
我一直在网上搜索,但找不到这个问题的可靠答案。 我有一个程序,它旋转第二个线程。从这第二个线程(不是主线程)它打开一个GLFW窗口并执行所有OpenGL调用。没有其他线程执行单个OpenGL调用。 在Mac OS X上可以吗?有消息称,苹果操作系统只能在主线程中运行OpenGL,也有消息称情况并非如此,所以我很好奇。(我自己没有Mac电脑来测试)。 非常感谢。
问题内容: 我想使用Mockito测试下面的(简化)代码。我不知道如何告诉Mockito第一次失败,然后第二次成功。 我可以通过以下方式设置成功测试: 以及失败测试: 但是,如何测试一次失败(或两次)然后成功,就可以了吗? 问题答案: 从文档: 有时,对于同一方法调用,我们需要对不同的返回值/异常进行存根。典型的用例可能是模拟迭代器。Mockito的原始版本没有此功能来促进简单的模拟。例如,可以使
先尝试用 D3 写第一个 HelloWorld 程序。学编程入门的第一个程序都是在屏幕上输出 HelloWorld,本课稍微有些不同,不是单纯的输出。 HTML 是怎么输出 HelloWorld 的 都知道 HTML 吧,如果不知道请下百度一下吧。在 HTML 中输出 HelloWorld 是怎样的呢,先看下面的代码。 <html> <head> <meta charset="
我将Mockito与JUnit一起使用,为Android项目中的类实现单元测试。问题是,我在随后的两个测试中调用了,而这两个测试完全相同(以确保我正确使用了Mockito),但有趣的是,在第二个测试中的verify总是失败。我怀疑,在使用注释的每次测试之前需要执行一些操作,而我忽略了这些操作。 我使用Android Studio 3.4.1、Mockito 2.7.22和JUnit 4.12。 下
3. 第二个汇编程序 例 18.2. 求一组数的最大值的汇编程序 #PURPOSE: This program finds the maximum number of a # set of data items. # #VARIABLES: The registers have the following uses: # # %edi - Holds the index of the data
问题内容: 我有Thrad和Handler: 当应用启动时,第一次使用 thread.start(); 一切正常。但是当我尝试启动 thread.start(); 从按钮第二次我有: E / MessageQueue-JNI:java.lang.IllegalThreadStateException:线程已启动。 问题答案: 您应该在启动之前检查该线程的状态。