DX9 Desktop Window Manager High CPU - c++

Desktop Window Manager Uses A Ton Of CPU When Drawing DX9 Window.
I'm Not Sure Why It Is Using So Much CPU
https://imgur.com/a/Bz7AVro
LRESULT CALLBACK WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam){
Sleep(12);
switch (Message){
case WM_PAINT:
Render();
break;
case WM_CREATE:
DwmExtendFrameIntoClientArea(hWnd, &Margin);
break;
case WM_DESTROY:
PostQuitMessage(1);
return 0;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
break;
}
return 0;
}
ref class CMAIN {
public:
void StartIt() { Main(); }
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hSecInstance, LPSTR nCmdLine, INT nCmdShow){
Thread^ main;
CMAIN^ cMain = gcnew CMAIN();
main = gcnew Thread(gcnew ThreadStart(cMain, &CMAIN::StartIt));
main->Name = "main";
main->Start();
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)SetWindowToTarget, 0, 0, 0);
WNDCLASSEX wClass;
wClass.cbClsExtra = NULL;
wClass.cbSize = sizeof(WNDCLASSEX);
wClass.cbWndExtra = NULL;
wClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
wClass.hCursor = LoadCursor(0, IDC_ARROW);
wClass.hIcon = LoadIcon(0, IDI_APPLICATION);
wClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
wClass.hInstance = hInstance;
wClass.lpfnWndProc = WinProc;
wClass.lpszClassName = lWindowName;
wClass.lpszMenuName = lWindowName;
wClass.style = CS_VREDRAW | CS_HREDRAW;
if(!RegisterClassEx(&wClass))
exit(1);
tWnd = FindWindow(0, tWindowName);
if (tWnd){
GetWindowRect(tWnd, &tSize);
Width = tSize.right - tSize.left;
Height = tSize.bottom - tSize.top;
hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, lWindowName, lWindowName, WS_POPUP, 1, 1, Width, Height, 0, 0, 0, 0);
SetLayeredWindowAttributes(hWnd, 0, 1.0f, LWA_ALPHA);
SetLayeredWindowAttributes(hWnd, 0, RGB(0, 0, 0), LWA_COLORKEY);
ShowWindow( hWnd, SW_SHOW);
}
DirectXInit(hWnd);
while (!directXExit){
Sleep(12);
if(PeekMessage(&Message, hWnd, 0, 0, PM_REMOVE)){
DispatchMessage(&Message);
TranslateMessage(&Message);
}
}
return 0;
}
void SetWindowToTarget(){
while(true){
tWnd = FindWindow(0, tWindowName);
if (tWnd){
GetWindowRect(tWnd, &tSize);
Width = tSize.right - tSize.left;
Height = tSize.bottom - tSize.top;
DWORD dwStyle = GetWindowLong(tWnd, GWL_STYLE);
if(dwStyle & WS_BORDER){
tSize.top += 23;
Height -= 23;
}
MoveWindow(hWnd, tSize.left, tSize.top, Width, Height, true);
}
Sleep(1500);
}
}

It would be useful to get some more information around what you're trying to do, and it's also hard to see exactly what could be going on with the WM_PAINT handler in your code (since you call a function called Render but don't provide that code). But at first glance I think there's at least one thing that's going to force high CPU usage, and that's your message loop. You're basically spinning in that loop due to calling PeekMessage, with a call to Sleep(12) which will basically give you a ~80fps update on that window; since you're using the DWM composition code (DwmExtendFrameIntoClient) my guess is that you're triggering a 80Hz update in DWM because of that. If you're rendering something in realtime in that loop, then that's all to be expected and not avoidable (unless you put your rendering loop on a separate thread). But if you're just responding to WM_PAINT messages then you'd be better served using a message pump that's using GetMessage rather than PeekMessage, since GetMessage will suspend the thread when the Windows message queue is empty, and you'll only trigger CPU work when something needs to be updated.

Related

Unable to resize win 32 window

I'm trying to learn how to create a window in win 32. This is as far as I have got with it. The problem I'm facing is I'm unable to create a window that can be resized by the user. I'm hoping that someone could help me solve this newbie problem.
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::string msg = "";
UINT width = 0;
UINT height = 0;
switch(uMsg)
{
case WM_SIZE:
width = LOWORD(lParam);
height = HIWORD(lParam);
if(width<(height*600)/800) SetWindowPos(hWnd, NULL, 0, 0, width, height, SWP_NOMOVE|SWPNOZORDER);
return true;
case WM_SIZING:
width = LOWORD(lParam);
height = HIWORD(lParam);
if(width<(height*600)/800) SetWindowPos(hWnd, NULL, 0, 0, width, height, SWP_NOMOVE|SWPNOZORDER);
return true;
case WM_DESTROY:
PostQuitMessage(0);
return true;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, INT nCmdShow)
{
WNDCLASSEX wnd = {0};
wnd.lpszClassName = "WindowLearn";
wnd.hInstance = hInstance;
wnd.lpfnWndProc = windowProc;
wnd.cbSize = sizeof(WNDCLASSEX);
wnd.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&wnd);
HWND hWnd = CreateWindowEx(NULL, "WindowLearn", "WindowLearnChild", WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0, 0, 800, 600, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
MSG msg = {0};
float pTime = 0.0f;
BOOL result;
while(msg.message != WM_QUIT)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Window gets created OK, but with this when I try to resize the window the window gets stuck to the mouse.
It appears that you want to to idle processing, meaning having some tasks done for your directX application when no event are present in the event loop.
There are 2 different ways of doing that:
dedicate a separate thread for background processing. It adds the complexity of multiprocessing, but allows to do all the processing as a single piece of code, and let the system affect time slices to the event loop and the background processing.
use a modified event loop that does chunks of background processing when no event are present. PeekMessage is the key here:
...
MSG msg = {0};
for (;;)
{
if (PeekMessage(&msg, NULL, 0, 0, 0, 0) {
if (! GetMessage(&msg, NULL, 0, 0, 0)) break; // msg is WM_QUIT
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
// do some idle processing for a short time
// no event will be processing during that
...
}
}
The nice point is that you do not need any multi-threading, but you have to explicitely split you backgroung processing in short time slices.

Win32 Window Doesn't Draw After minute or so

I am in a sticky situation.
I'm messing around with Bitmaps and windows.
I followed this tutorial: https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image
I am basically taking multiple pictures of my desktop and displaying them into a window.
It works great as intended but after like a minute or 2 the window won't update and I can't find the error or why this is occurring.
Here is the code:
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DESTROY: {
PostQuitMessage(0); // Exit the program if the window gets closed.
} break;
case WM_SIZE: {
} break;
}
return DefWindowProc(hWnd, message, wParam, lParam); // Handle any messages the switch statement didn't
}
HWND SpawnWindow() {
WNDCLASSEX WindowClass;
SecureZeroMemory(&WindowClass, sizeof(WNDCLASSEX));
WindowClass.cbClsExtra = NULL;
WindowClass.cbWndExtra = NULL;
WindowClass.cbSize = sizeof(WNDCLASSEX);
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
WindowClass.lpfnWndProc = WinProc;
WindowClass.hInstance = NULL;
WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WindowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
WindowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
WindowClass.hbrBackground = NULL;
WindowClass.lpszClassName = L"Class NAme";
WindowClass.lpszMenuName = L"Menu Name";
RegisterClassEx(&WindowClass);
return CreateWindowEx(NULL, L"Class Name", L"Window Title", WS_VISIBLE | WS_TILEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
}
void DoMessages() {
MSG msg = {};
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
std::cout << msg.message << "\n";
}
int main() {
HWND WindowToGetPic = SpawnWindow();
HDC DesktopDC = GetDC(0);
HDC WindowDC = GetDC(WindowToGetPic);
if (!DesktopDC)
std::cout << "DC NULL!\n";
HDC TempDC = CreateCompatibleDC(DesktopDC);
HDC TempDCWindow = CreateCompatibleDC(WindowDC);
if (!TempDC)
std::cout << "TempDC NULL!\n";
while (true) {
/*
after calling CreateCompatibleDC you mus then call CreateCompatiableBitmap with the DC recieved
then call SelectObject
*/
HBITMAP hBitmap = CreateCompatibleBitmap(TempDC, 1920, 1080);/* screen res */
SelectObject(TempDC, hBitmap);
SetStretchBltMode(TempDC, HALFTONE);
RECT WindowDimensions;
GetClientRect(WindowToGetPic, &WindowDimensions);
if (!BitBlt(WindowDC, 0, 0, WindowDimensions.right - WindowDimensions.left, WindowDimensions.bottom - WindowDimensions.top, DesktopDC, 0, 0, SRCCOPY))
std::cout << "BitBlt ERROR\n";
DoMessages();
InvalidateRect(WindowHwnd, NULL, TRUE);
}
std::cin.ignore();
return 0;
}
If anyone could test this, or spot why the window doesn't update after a minute or two I would really appreciate it.
Thank you in advance!
I think you have spent all resources.
For example,
You create bitmap every cycle and don't release it.
You call SelectObject and don't return selected object to original state.
You have several resource leaks:
when you are done with an HDC returned by GetDC, call ReleaseDC
when you are done with an HDC returned by CreateCompatibleDC, call DeleteDC
when you are done with hBitmap, select the original bitmap back into the DC and call DeleteObject. The original bitmap is returned by SelectObject.
This is all covered in the documentation (Google will find it).

Initilizer-list cannot convert to const MARGINS*

Today I jumped to Visual Studio (C++) 2013, I was using Codeblocks a lot of time but I realized that codeblocks starts to fail at compiling some codes like this:
//#define _WIN32_WINNT 0x0500
#include "hMain.h"
#include <windows.h>
#include <Uxtheme.h>
int Width = 800;
int Height = 600;
const MARGINS* Margin = { 0, 0, Width , Height };
char lWindowName[256] = "TEEEST";
HWND hWnd;
char tWindowName[256] = "TEEEST";
HWND tWnd;
RECT tSize;
MSG Message;
LRESULT CALLBACK WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_PAINT:
Render ();
break;
case WM_CREATE:
DwmExtendFrameIntoClientArea(hWnd, Margin);
break;
case WM_COMMAND:
if (wParam == VK_ESCAPE) PostQuitMessage(0);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
break;
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hSecInstance, LPSTR nCmdLine, INT nCmdShow)
{
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)SetWindowToTarget, 0, 0, 0);
WNDCLASSEX wClass;
wClass.cbClsExtra = NULL;
wClass.cbSize = sizeof(WNDCLASSEX);
wClass.cbWndExtra = NULL;
wClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
wClass.hCursor = LoadCursor(0, IDC_ARROW);
wClass.hIcon = LoadIcon(0, IDI_APPLICATION);
wClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
wClass.hInstance = hInstance;
wClass.lpfnWndProc = WinProc;
wClass.lpszClassName = (LPCWSTR)lWindowName;
wClass.lpszMenuName = (LPCWSTR)lWindowName;
wClass.style = CS_VREDRAW | CS_HREDRAW;
if(!RegisterClassEx(&wClass))
exit(1);
tWnd = FindWindow(0, (LPCWSTR)tWindowName);
if (tWnd)
{
GetWindowRect(tWnd, &tSize);
Width = tSize.right - tSize.left;
Height = tSize.bottom - tSize.top;
hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, (LPCWSTR)lWindowName, (LPCWSTR)lWindowName, WS_POPUP, 1, 1, Width, Height, 0, 0, 0, 0);
SetLayeredWindowAttributes(hWnd, 0, 1.0f, LWA_ALPHA);
SetLayeredWindowAttributes(hWnd, 0, RGB(0, 0, 0), LWA_COLORKEY);
ShowWindow( hWnd, SW_SHOW);
}
DirectXInit(hWnd);
for (;;)
{
if (GetAsyncKeyState(VK_ESCAPE)) break;
if(PeekMessage(&Message, hWnd, 0, 0, PM_REMOVE))
{
DispatchMessage(&Message);
TranslateMessage(&Message);
}
Sleep(1);
}
return 0;
}
void SetWindowToTarget()
{
while(true)
{
tWnd = FindWindow(0, (LPCWSTR)tWindowName);
if (tWnd)
{
GetWindowRect(tWnd, &tSize);
Width = tSize.right - tSize.left;
Height = tSize.bottom - tSize.top;
DWORD dwStyle = GetWindowLong(tWnd, GWL_STYLE);
if(dwStyle & WS_BORDER)
{
tSize.top += 23;
Height -= 23;
}
MoveWindow(hWnd, tSize.left, tSize.top, Width, Height, true);
}
else
{
char* ErrorMsg[125];
MessageBox(0, L"MAL", (LPCWSTR)L"Error - Cannot find the game!", MB_OK | MB_ICONERROR);
exit(1);
}
Sleep(100);
}
}
So, 2 question. First, how could I fix this compiler error: http://gyazo.com/6d7de9e0f3bad345dbbe7b9b80c90b8d And the second question. I realized that I must put an "L" and (LPCWSTR) before some chars*, this is 100% required? Is there any way to avoid that at least the (LPCWSTR)? Thanks for read.
First,
const MARGINS* Margin = { 0, 0, Width , Height };
is invalid. You probably want to create a new MARGINS object and initialize it with the given values, in which case you don't want Margin to be a pointer:
const MARGINS Margin = { 0, 0, Width , Height };
Second, DwmExtendFrameIntoClientArea() expects a const MARGINS*, which is a pointer to a MARGINS object. Just give it the address of Margin:
DwmExtendFrameIntoClientArea(hWnd, &Margin);
As to the wide character issues, I am assuming that you are compiling this with the "Unicode character set" in the project options. In this case, most strings in the Windows API will be pointers to wide characters (wchar_t*). However, you are allocating narrow character arrays (such as lWindowName) and casting them to a pointer to a wchar_t:
wClass.lpszClassName = (LPCWSTR)lWindowName;
This will give you all sorts of weird behaviours. Make sure your strings really are made of wide characters:
wchar_t lWindowName[256] = L"TEEEST";
This will allow you to drop most casts.

Win32 window - no close message

I am currently making a small win32 window wrapper class, but I have a few problem.
If I hit the close(X) button of the window the window closes immediately without sending a quit or destroy message, so I can't for example prevent the window to close or save something before closing the window.
And the second problem/question is,
If I use this small code to use the window, the computer cpu gets strongly used.
But its only a small window.
How I can change/fix this?
int main()
{
glwCreate();
while(true/*Later here comes a method that checks, wether window close is requested*/)
{
glwUpdate();
}
glwDestroy();
return 0;
}
-
#include "glw.h"
#include <windows.h>
#include <iostream>
HINSTANCE instanceHandle;
WNDCLASSEX windowClass;
HWND windowHandle;
LRESULT CALLBACK WindowMessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::cout<<uMsg<<'\n';
switch(uMsg)
{
case WM_QUIT:
{
std::cout<<"QUIT\n";
return 0;
}
case WM_DESTROY:
{
std::cout<<"DESTROY\n";
return 0;
}
}
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
void glwCreate()
{
instanceHandle = GetModuleHandle(0);
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowMessageHandler;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = instanceHandle;
windowClass.hCursor = LoadCursor(0,IDC_ARROW);
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.lpszClassName = "atomus_window_class";
windowClass.lpszMenuName = "menu_name";
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
RegisterClassEx(&windowClass);
windowHandle = CreateWindowEx( 0,
"atomus_window_class",
"atomus title",
WS_OVERLAPPEDWINDOW,
0,
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
instanceHandle,
0);
ShowWindow(windowHandle, SW_SHOW);
}
void glwDestroy()
{
DestroyWindow(windowHandle);
windowHandle = 0;
UnregisterClass(windowClass.lpszClassName, instanceHandle);
}
void glwUpdate()
{
MSG message;
while (PeekMessage (&message, 0, 0, 0, PM_REMOVE) > 0) //Or use an if statement
{
TranslateMessage (&message);
DispatchMessage (&message);
}
}
If you add handling for WM_CLOSE you get to control whether your window closes or not. By not providing your own handling for that message you get the default from DefWindowProc which is to destroy your window.

Multithreading OpenGL in a child window

I'm trying to build an OpenGL application that is responsive even while the main window is being resized or moved. The most logical solution that I have found is to create a child window and a Message Pump in separate thread which renders the OpenGL. It can resize itself in between frames as necessary. The primary message pump and window frame runs in the main process.
It works great to a point. The window can be moved, menus used and resized without affecting the frame rate of the child window. SwapBuffers() is where it all falls apart.
SwapBuffers() seems to be running in software mode when it is run in this manner. It no longer holds at 60 FPS to match my monitor's VSync, it jumps into the hundreds when the window is around 100x100 and drops to 20 FPS when maximized to 1920x1080. When running in a single thread, everything seems normal.
There were a few issues that I resolved. Like when messages need to pass between parent and child it stalls the entire application. Overriding WM_SETCURSOR and setting WS_EX_NOPARENTNOTIFY resolved those. It still occasionally stutters when I click.
I'm hoping that I'm just not doing something properly and that someone experienced with OpenGL can help me out. Something to do with my initialization or cleanup may be off since this interferes with other OpenGL applications running on my PC even after I close it.
Here is a simplified test case that exhibits the issues that I'm experiencing. It should compile in about any modern Visual Studio.
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <wchar.h>
#pragma comment(lib, "opengl32.lib")
typedef signed int s32;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef float f32;
typedef double f64;
bool run = true;
// Window procedure for the main application window
LRESULT CALLBACK AppWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_DESTROY && (GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_APPWINDOW))
PostQuitMessage(0);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
// Window procedure for the OpenGL rendering window
LRESULT CALLBACK RenderWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_SETCURSOR)
{
SetCursor(LoadCursor(NULL, IDC_CROSS));
return TRUE;
}
if (msg == WM_SIZE)
glViewport(0, 0, LOWORD(lParam)-2, HIWORD(lParam)-2);
return AppWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI ThreadMain(HWND parent)
{
HINSTANCE hInstance = GetModuleHandle(0);
// Depending on if this is running as a child or a overlap window, set up the window styles
UINT ClassStyle, Style, ExStyle;
if (parent)
{
ClassStyle = 0;
Style = WS_CHILD;
ExStyle = WS_EX_NOPARENTNOTIFY;
} else {
ClassStyle = 0 | CS_VREDRAW | CS_HREDRAW;
Style = WS_OVERLAPPEDWINDOW;
ExStyle = WS_EX_APPWINDOW;
}
// Create the child window class
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = ClassStyle;
wc.hInstance = hInstance;
wc.lpfnWndProc = RenderWindowProc;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.lpszClassName = L"OGLChild";
ATOM ClassAtom = RegisterClassEx(&wc);
// Create the child window
RECT r = {0, 0, 640, 480};
if (parent)
GetClientRect(parent, &r);
HWND WindowHandle = CreateWindowExW(ExStyle, (LPCTSTR)MAKELONG(ClassAtom, 0), 0, Style,
0, 0, r.right, r.bottom, parent, 0, hInstance, 0);
// Initialize OpenGL render context
PIXELFORMATDESCRIPTOR pfd = {0};
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 = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
HDC DeviceContext = GetDC(WindowHandle);
int format = ChoosePixelFormat(DeviceContext, &pfd);
SetPixelFormat(DeviceContext, format, &pfd);
HGLRC RenderContext = wglCreateContext(DeviceContext);
wglMakeCurrent(DeviceContext, RenderContext);
ShowWindow(WindowHandle, SW_SHOW);
GetClientRect(WindowHandle, &r);
glViewport(0, 0, r.right, r.bottom);
// Set up an accurate clock
u64 start, now, last, frequency;
QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
QueryPerformanceCounter((LARGE_INTEGER*)&now);
start = last = now;
u32 frames = 0; // total frames this second
f64 nextFrameCount = 0; // next FPS update
f32 left = 0; // line position
MSG msg;
while (run)
{
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT || msg.message == WM_DESTROY)
run = false;
}
// Update the clock
QueryPerformanceCounter((LARGE_INTEGER*)&now);
f64 clock = (f64)(now - start) / frequency;
f64 delta = (f64)(now - last) / frequency;
last = now;
// Render a line moving
glOrtho(0, 640, 480, 0, -1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glColor3f(1.0f, 1.0f, 0.0f);
left += (f32)(delta * 320.0f);
if (left > 640.0f)
left = 0;
glBegin(GL_LINES);
glVertex2f(0.0f, 0.0f);
glVertex2f(left, 480.0f);
glEnd();
SwapBuffers(DeviceContext);
// Resize as necessary
if (parent)
{
RECT pr, cr;
GetClientRect(parent, &pr);
GetClientRect(WindowHandle, &cr);
if (pr.right != cr.right || pr.bottom != cr.bottom)
MoveWindow(WindowHandle, 0, 0, pr.right, pr.bottom, FALSE);
}
// Update FPS counter
frames++;
if (clock > nextFrameCount)
{
WCHAR title[16] = {0};
_snwprintf_s(title, 16, 16, L"FPS: %u", frames);
SetWindowText(parent ? parent : WindowHandle, title);
nextFrameCount = clock + 1;
frames = 0;
}
Sleep(1);
}
// Cleanup OpenGL context
wglDeleteContext(RenderContext);
wglMakeCurrent(0,0);
ReleaseDC(WindowHandle, DeviceContext);
DestroyWindow(WindowHandle);
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
int result = MessageBox(0, L"Would you like to run in threaded child mode?",
L"Threaded OpenGL Demo", MB_YESNOCANCEL | MB_ICONQUESTION);
if (result == IDNO)
return ThreadMain(0);
else if (result != IDYES)
return 0;
// Create the parent window class
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)AppWindowProc;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.lpszClassName = L"OGLFrame";
ATOM ClassAtom = RegisterClassEx(&wc);
// Create the parent window
HWND WindowHandle = CreateWindowExW(WS_EX_APPWINDOW, (LPCTSTR)MAKELONG(ClassAtom, 0),
0, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
0, 0, 640, 480, 0, 0, hInstance, 0);
ShowWindow(WindowHandle, SW_SHOW);
// Start the child thread
HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadMain, (LPVOID)WindowHandle, 0, 0);
MSG msg;
while (run)
{
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT)
run = false;
}
Sleep(100);
}
DestroyWindow(WindowHandle);
// Wait for the child thread to finish
WaitForSingleObject(thread, INFINITE);
ExitProcess(0);
return 0;
}
I've been running this on a NVIDIA GeForce 8800 GTX, but I'd hope that any solution will work equally on any modern card.
I have tried other methods, such as a thread rendering into a window in the main process but during resizing I've gotten artifacting and even a couple blue screens. My application will be cross platform so DirectX isn't an option.
The issue turned out to be that prolonged debugging of OpenGL applications in Visual Studio can cause OpenGL to start failing to create contexts. Since I didn't trap for any errors, I never realized that it was running without hardware acceleration. It required a full reboot to recover and now works fine.
Also, replacing the pump WinMain with this fixes any issues with resizing:
MSG msg;
while (GetMessage(&msg, 0, 0, 0) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
run = false;