ziacko / TinyWindow

a cross platform (Linux and Windows) OpenGL window library in a single header
MIT License
200 stars 12 forks source link

Crash on example window creation in visual studio 2017, windows 7, window 10 sdk #17

Closed LiveAsynchronousVisualizedArchitecture closed 1 year ago

LiveAsynchronousVisualizedArchitecture commented 7 years ago

I've had TinyWindow working with a previous version and msvc 2013. With the latest version combined with msvc 2017, it crashes on the following line:

glRenderingContextHandle = wglCreateContextAttribsARB(window->deviceContextHandle, NULL, attribs);

I've traced it back to where I think the problem starts - this line:

wglMakeCurrent(dummyDeviceContextHandle, glDummyContextHandle);

This returns 0, but does not set a windows error. The extension function addresses end up NULL and eventually it crashes on wglCreateContextAttribsARB(...) because the function address is NULL.

I've also noticed that there is minimal error handling. I used the following function that I found elsewhere to help debug this issue. It seems that there are dozens of windows functions that are not error checked, which I can understand for a prototype, but for the library to mature I think it will be very helpful.

auto GetLastErrorStdStr() -> std::string { DWORD error = GetLastError(); if (error) { LPVOID lpMsgBuf; DWORD bufLen = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); if (bufLen) { LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf; std::string result(lpMsgStr, lpMsgStr+bufLen);

  LocalFree(lpMsgBuf);

  return result;
}

} return std::string(); }

include

define printWinError std::cout << "\n" << LINE << " windows error " << GetLastErrorStdStr() << std::endl;

Let me know if I can do anything else to help!

ziacko commented 7 years ago

Hey I'm having trouble reproducing this issue. I'm using VS studio 2017 community and MSCV v141 and v140. but still I cannot reproduce the crash

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Thanks for looking into it. One more element is that I didn't have a wgext.h file in the include install so I used one from the Khronos website.

ziacko commented 7 years ago

ok so I installed the latest version of wglext.h but still no crash

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Thanks again for looking into it. On startup there is actually an error of "The specified module could not be loaded." I'll see what I can figure out about that. The Window utility Dependency Walker doesn't show anything out of the ordinary, just KERNEL32.dll USER32.dll GDI32.dll and OPENGL32.dll

ziacko commented 7 years ago

it could be your driver. if you have an NVIDIA card that might be trouble as I don't have a NVIDIA card to dev with

LiveAsynchronousVisualizedArchitecture commented 7 years ago

It is possible but I don't think it is likely since glfw and even a previous version of TinyWindow.h have both worked before. I do have two monitors running at different refresh rates however. I took a quick look at glfw and it looks like they loop through lots of pixel formats on initialization. I'm not certain if the pixel format is even affecting the problem I'm having, but it is a a difference I noticed at a glance.

The module not loaded windows error seemed to be from compiling statically. When I use the debug DLL in msvc it goes away, though this does't solve the wgl problem.

Here is what I've been running: https://github.com/LiveAsynchronousVisualizedArchitecture/lava/tree/master/TinyWindow

ziacko commented 7 years ago

it does seem like the most obvious answer. right now I'll be adding the ability to enumerate through pixel formats. I'm thinking of storing it either in the monitor class or making it a struct and having the window or manager store the information in a list. lucky i have the superbible to help me out

ziacko commented 7 years ago

you could change up the RGB | depth values in the tWindow constructor to see what works for you. you could debug GLFW to see what RGB|depth values it has picked for you and plug them into TinyWindow to hack it

LiveAsynchronousVisualizedArchitecture commented 7 years ago

That is true, and a good idea. It would at least get rid of one possible source of error.

ziacko commented 7 years ago

is this what you see?

tinywindow
LiveAsynchronousVisualizedArchitecture commented 7 years ago

Close - it crashes on wglCreateContextAttribsARB because the function pointer itself is NULL.

tinywindow 1

ziacko commented 7 years ago

if its null then It hasn't been loaded at all which means it's not supported. is the EXT version of the function NULL too?

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Yes. It doesn't get to that point normally though, because wglGetExtensionsStringARB and wglGetExtensionsStringEXT are both NULL, which makes the function return. If I comment out the error check, all the calls to wglGetProcAddress return NULL.

ziacko commented 7 years ago

are any of the other extensions being loaded? if none are that could mean the dummy context that I create in order to load these extensions may not be working on your setup

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Right. None of the extensions load as far as I can tell. dummyDeviceContextHandle from GetDC() is not NULL however its unused struct member can't be read by the msvc debugger (should it contain something?)

wglCreateContext returns a non-null handle with unused set to 0, then wglMakeCurrent returns false.

None of these functions create a windows error from what I've seen.

ziacko commented 7 years ago

ok by default the openGL version TinyWindow uses is 4.5. make sure your GPU can handle that. also change it from a core context to a compatibility context. just in case while im researching this

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Ok. I have a 970, but I have been using glfw with 3.3 so it is certainly worth a try.

LiveAsynchronousVisualizedArchitecture commented 7 years ago

How do I change the version? From what I can tell these errors all happen before any version numbers are used.

ziacko commented 7 years ago

the dummy context does'nt use version numbers because it need to use the old context creation system in order to load the extensions for the new context creation system that uses those numbers

LiveAsynchronousVisualizedArchitecture commented 7 years ago

That makes sense, but the crash comes from the extensions not loading, so I don't get to the point where I can choose the version.

ziacko commented 7 years ago

you can change the verision in the constructor for the window class as glMajor and glMinor parameters

ziacko commented 7 years ago

in Windows_CreateDummyContext can you do some error checks before and after the dummy context is made. maybe the dummy is invalid

LiveAsynchronousVisualizedArchitecture commented 7 years ago

I see the glMajor and glMinor variables in the tWindow constructor, but that is never hit.

I'm checking for windows errors before and after the GetDC(GetDesktopWindow()); line and not getting anything. dummyDeviceContextHandle isn't NULL so I'm not sure what else to check.

ziacko commented 7 years ago

in CreateDummyContexts try this. if it hits the error message then im out of ideas. make sure to comment out the code between pfd.redBits to pfd.Depthbits. this should widen the pool for creating dummy contexts

void Windows_CreateDummyContext() { dummyDeviceContextHandle = GetDC(GetDesktopWindow()); PIXELFORMATDESCRIPTOR pfd; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24;

        int LocalPixelFormat = ChoosePixelFormat(dummyDeviceContextHandle,
            &pfd);

        if (LocalPixelFormat)
        {
            if (!SetPixelFormat(dummyDeviceContextHandle, LocalPixelFormat, &pfd))
            {
                printf("Error: dummy context pixel format is invalid\n");
                return;
            }
        }

        glDummyContextHandle = wglCreateContext(dummyDeviceContextHandle);
        if (!glDummyContextHandle)
        {
            printf("Error: dummy context creation failed\n");
            return;
        }

        if (!wglMakeCurrent(dummyDeviceContextHandle, glDummyContextHandle))
        {
            printf("failed to make dummy context current\n");
            return;
        }
    }
LiveAsynchronousVisualizedArchitecture commented 7 years ago

Ok, I will try this, though it may be a few hours later.

ziacko commented 7 years ago

no worries i got to sleep in a few hours anyway

ziacko commented 7 years ago

just checking in. how'd it go?

LiveAsynchronousVisualizedArchitecture commented 7 years ago

I wasn't able to get it to work with the minimal settings. I took another look at glfw's source and didn't see anything that jumped out at me as too different. I think the next step might be for me to step through glfw to see it work. If I do that, maybe I can learn a lot.

ziacko commented 7 years ago

Ok i made another update that if the dummy context fails, then TinyWindow will create windows using the old fashioned method. I doubt this will solve the problem but now you should at lest still be able to work somewhat. plus some more smaller updates

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Your new update works to get a window up for me. The window title works and closing the window works. The screen is white however, even though it looks like it should be clearing to 0.25 grey.

Also I put

Platform_InitExtensions(); on line 2568 to see if the extensions would load correctly and they did.

Edit: 2658

ziacko commented 7 years ago

OK... that's interesting to note. it still works on my setup so I'm not sure what could possibly still be missing.

also line 2568? you put it inside the event handle case for WM_KILLFOCUS?

it seems to be an overall issue with pixel formatting. the only time I've really had a real issue with that stuff is with NVIDIA cards? is that what you run?

LiveAsynchronousVisualizedArchitecture commented 7 years ago

I got that very wrong, it should be 2658. I put it just after Platform_InitializeGL()

#if !defined(TW_USE_VULKAN)
   Platform_InitializeGL(window);
   Platform_InitExtensions();
#endif
  ShowWindow(window->windowHandle, 1);
  UpdateWindow(window->windowHandle);

I do have an nvidia card, but glfw works, so although there might be something fishy going on it can work somehow. Maybe I'll still step through glfw to see what pixel format it ends up using like you suggested.

ziacko commented 7 years ago

in InitializePixelFormat() at line 2729 if the variable localPixelFormat is NULL then it is definitely related to pixel formatting issues. basically choosePixelFormat() uses the RGB/depth/stencil values that are parsed in and sifts through all the available PFD's to find the one that is closest to the one you pick and hand it ack to you. if that fails then there are definately no compatible PFDs for your GPU in that case

LiveAsynchronousVisualizedArchitecture commented 7 years ago

That comes back as 1 for me using your updates. I've tried to pay close attention to that and I don't think it ever gave back false.

One thing is that the following is able to use the main path that sets attributes, since the initialization enables the extensions to work and the extensions working enables the typical path to execute.

Platform_InitializeGL(window); Platform_InitExtensions(); Platform_InitializeGL(window);

ziacko commented 7 years ago

does that run in your setup? if that's so it it the weirdest thing Iv'e seen in awhile. it does make me wonder why the dummy context is giving so much trouble for loading extensions when compared to your hack

LiveAsynchronousVisualizedArchitecture commented 7 years ago

It creates a window, but the window is still white, it doesn't clear to grey.

ziacko commented 7 years ago

ok in the printMonitorInfo function in the examaple, on the line where the moniror's rendering device name is used, can you tell me if the rendering device is your GPU or your CPU? i think that maybe your comp is using the CPU which doesnt really support accelerated PFD's which is causing your window to render white

ziacko commented 7 years ago

in creating the dummy context can you swap out PFD_GENERIC_ACCELERATED in line 2754 with PFD_GENERIC_FORMAT to see if that makes a difference

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Here is the printMonitorInfo output:

tinywindow 2

I added PFD_GENERIC_FORMAT to 2754 (neither of the flags you mentioned are there by default), though it didn't change the result or output from printMonitorInfo.

I also stepped through glfw and looked at their pixel format descriptors.

Separately one thing I noticed is that we have been working with the pfd for the dummy context, but not the attributes used to create the final context if extensions are loaded.

ziacko commented 7 years ago

well you have been talking about the extensions not being loaded the first time around after creating the dummy context so I'm trying to fix that issue first.

it could be that you have multiple monitors maybe but i only have 1 so i can't test dual screening.

you can mess with the PFD used for creating proper windows in line 2717 to see if that helps as well

LiveAsynchronousVisualizedArchitecture commented 7 years ago

That makes sense, thanks.

My thought is that if the backup method of creating a context works, maybe that should be the method of creating the dummy context so that the extensions will load. I haven't even worked out the differences between the two methods, so I have no idea if this is silly and missing a fundamental problem that needs to be addressed.

ziacko commented 7 years ago

well a dummy is just supposed to be a skeleton of a window (it doesn't even need to be rendered at all. It's made to be used and immediately killed like a sacrificial lamb). it shouldn't really matter what the PFD is as long as it works and doesn't crash or anything so making the dummy could be done easily as a copy-paste job or via a single function so the same block of code doesn't show up twice. but maybe the PFD ins't the problem when it comes to your setup.

The only real issue is that it's not loading your extensions with the dummy context. it instead uses the backup one which are basically the same. but the backup PFD seems to be invalid as any calls to openGL are invalid due to the white window. The single common thread between the 2 could be the PFD

im gonna look closer at PFD's and how to enumerate through them to get one that will always work GLFW style. i just assumed wglChoosePixelFormat would always work right off the bat since it does that automatically.

ziacko commented 7 years ago

OK looking at the documentation for DescribePixelFormat and ChoosePixelformat more closely it seems that ChoosePixelformat will pick the first compaitble PFD which may or may not have the necessary flags needed to be used by OpenGL.

GLFW uses DescribePixelFormat to iterate through all the compatible PFDs in order to weed out PFD's that don't play well with OpenGL so I'm going to integrate that as well. hopefully that will be the silver bullet to this mess

ziacko commented 7 years ago

ok made a big update for you hopefully this helps in both dummy creation and regular window creation for you. this should find the closest possible PFD for you that is compatible with OpenGL.

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Unfortunately the updated version has the same behavior.

ziacko commented 7 years ago

:| ok another theory. unplug one of your monitors to see if that makes any difference

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Interestingly, if I deactivate one of my monitors (not unplug), windowManager::monitorList ends up with a size of 5. The loop to print the the monitors crashes due to some of the fields being NULL.

LiveAsynchronousVisualizedArchitecture commented 7 years ago

Unplugging a monitor did not make a difference in the behavior, there is still a window created that stays white. The monitor list only has 1 element in this case though. One thing to keep in mind is that I still get a 'handle is invalid' windows error after creating the manager.

ziacko commented 7 years ago

hrmm there are a few handles that could be invalid in the window manager including the device context and GLrenderingcontext handles but the fact that it seems to be invalid for both the dummy and a regular window tells me that it's most likely the GL rendering context handle.

could you step through the constructor and CreateDummyContext and see where exactly the error pops up? say after line 3033 and 1575 just to determine which one is the problematic handle

ziacko commented 7 years ago

ok for the monitor bug you mentioned I've got an old monitor to debug with. if the monitor has been deactivated it should still show up in the mointer list but some of its variables will obviously still be NULL since the monitor is deactivated.

also monitorList should only have 2 members instead of the 5 you reported. I'm also looking for a way to recieve and process events when a monitor's settings have changed and exposing a callback specifically for that