matus-chochlik / oglplus

OGLplus is a collection of open-source, cross-platform libraries which implement an object-oriented facade over the OpenGL® (version 3 and higher) and also OpenAL® (version 1.1) and EGL (version 1.4) C-language APIs. It provides wrappers which automate resource and object management and make the use of these libraries in C++ safer and more convenient.
http://oglplus.org/
Boost Software License 1.0
491 stars 72 forks source link

ObjectDescRegistryArchive Access violation reading location #57

Closed vif closed 10 years ago

vif commented 10 years ago

Hi, I am using Visual Studio 2013, and currently doing stuff with shaders, vertexattributearrays, uniforms, and buffers. The issue reproduced with both the bleeding edge version, and version 0.42.0

I am using unique_ptr to manage my memory, but right now, after my program has returned I am running into the error that ObjectDescRegistryArchive is trying to access the _maps map variable with a key that no longer exist. Looking with the debugger, the _maps size is 0, but a call is made to read the element with the key '7'.

I am going to guess that it has something to do with the order that objects get destroyed, but I don't think a desctuctor should fail, not in this way at least. Is there anything I could put into the constructors/destructors to get a better idea of which order everything is happening in?

The issue should be observable here (https://github.com/vif/3D-STG/tree/ec6aa61861c98c32b42038c1d1b1d3c73fae8f99), although it is likely not isolated enough to be useful.

I have attached a call stack below.

--------------------------
ntdll.dll!_NtRaiseException@12()   Unknown
--------------------------
ntdll.dll!_KiUserExceptionDispatcher@8()   Unknown
--------------------------
test.exe!std::_Tree<std::_Tmap_traits<int,std::map<unsigned int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > >,std::less<int>,std::allocator<std::pair<int const ,std::map<unsigned int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > > > >,0> >::_Lbound(const int & _Keyval) Line 2109 C++
--------------------------
test.exe!std::_Tree<std::_Tmap_traits<int,std::map<unsigned int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > >,std::less<int>,std::allocator<std::pair<int const ,std::map<unsigned int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > > > >,0> >::lower_bound(const int & _Keyval) Line 1575 C++
--------------------------
test.exe!std::map<int,std::map<unsigned int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > >,std::less<int>,std::allocator<std::pair<int const ,std::map<unsigned int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::basic_string<char,std::char_traits<char>,std::allocator<char> > > > > > > >::operator[](const int & _Keyval) Line 228  C++
--------------------------
test.exe!oglplus::aux::ObjectDescRegistryArchive(int id) Line 29    C++
--------------------------
test.exe!oglplus::aux::ObjectDescRegistry<oglplus::ProgramOps>::_archive() Line 159 C++
--------------------------
test.exe!oglplus::aux::ObjectDescRegistry<oglplus::ProgramOps>::_unregister_desc(unsigned int type, unsigned int name) Line 187 C++
--------------------------
test.exe!oglplus::Object<oglplus::ProgramOps>::_undescribe(const oglplus::Object<oglplus::ProgramOps> * that) Line 340  C++
--------------------------
test.exe!oglplus::Object<oglplus::ProgramOps>::_cleanup_if_needed() Line 452    C++
--------------------------
test.exe!oglplus::Object<oglplus::ProgramOps>::~Object<oglplus::ProgramOps>() Line 525  C++
--------------------------
[External Code] 
--------------------------
matus-chochlik commented 10 years ago

Hi, the ObjectDescRegistry is one of the things at the top of TODO list that need to be fixed. Initially it was used for internal debugging and care needs to be taken, when using it. It doesn't work well for example with static initialization, it complicates exception handling, etc. I plan to replace it eventually, probably with EXT_debug_label, EXT_debug_marker and integrating it with the ARB_debug_output wrapper, or I'll rewrite it completely.

Currently, if it causes you problems it can be completely disabled by defining OGLPLUS_NO_OBJECT_DESCS PP-symbol and rebuilding your app. Or you need to take care and manually release all OGLplus Objects before the cleanup of statically initialized variables starts.

HTH

vif commented 10 years ago

I see, I tried enabling the symbol.

I am now failing the assert

assert((OGLPLUS_GLFUNC(GetError)()) == GL_NO_ERROR);

With the callstack

--------
msvcr120.dll!67f36bb8() Unknown
--------
[Frames below may be incorrect and/or missing, no symbols loaded for msvcr120.dll]  
--------
[External Code] 
--------
test.exe!oglplus::ProgramOps::_cleanup(int _count, unsigned int * _name) Line 73    C++
--------
test.exe!oglplus::Object<oglplus::ProgramOps>::_do_cleanup(int _c, unsigned int * _n) Line 412  C++
--------
test.exe!oglplus::Object<oglplus::ProgramOps>::_cleanup_if_needed() Line 453    C++
--------
test.exe!oglplus::Object<oglplus::ProgramOps>::~Object<oglplus::ProgramOps>() Line 525  C++
--------
[External Code] 
--------
matus-chochlik commented 10 years ago

IIUC this happens in your code when an OGLplus object is deallocated after the main function finishes, right? If so, again this is understandable, because after main finishes, the GL context in which the object was created does not exist anymore and this means that the glDestroyXY(...) function fails and sets proper error code which the assert catches. It is a program error to try to cleanup GL objects after its GL context has been destroyed.

vif commented 10 years ago

Yes you understood correctly. That indeed was the problem. Thank you.

matus-chochlik commented 10 years ago

No problem :)