Sharing opengl resources (OpenGL ES 2.0 Multithreading) - c++

I have developed an OpenGL ES 2.0 win32 application, that works fine in a single thread. But I also understand that UI thread and a rendering thread should be separate.
Currently my game loop looks something like that:
done = 0;
while(!done)
{
msg = GetMessage(..); // getting messages from OS
if(msg == QUIT) // the window has been closed
{
done = 1;
}
DispatchMessage(msg,..); //Calling KeyDown, KeyUp events to handle user input;
DrawCall(...); //Render a frame
Update(..); // Update
}
Please view it as a pseudo code, cause i don't want to bother you with details at this point.
So my next step was to turn done into an std::atomic_int and create a function
RenderThreadMain()
{
while(!done.load())
{
Draw(...);
}
}
and create a std::unique_ptr<std::thread> m_renderThread variable. As you can guess nothing has worked for me so far, so i made my code as stupid and simple as possible in order to make sure i don't break anything with the order i call methods in. So right now my game loop works like this.
done.store(0);
bool created = false;
while(!done)
{
msg = GetMessage(..); // getting messages from OS
if(msg == QUIT) // the window has been closed
{
done.store(1);
}
DispatchMessage(msg,..); //Calling KeyDown, KeyUp events to handle user input;
// to make sure, that my problem is not related to the fact, that i'm rendering too early.
if(!created)
{
m_renderThread = std::make_unique<std::thread>(RenderThreadMain, ...);
created = true;
}
Update(..); // Update
}
But this doesn't work. On every draw call, when i try to somehow access or use my buffers \ textures anything else, i get the GL_INVALID_OPERATION error code.
So my guess would be, that the problem is in me calling glGenBuffers(mk_bufferNumber, m_bufferIds); in the main thread during initialization and then calling glBindBuffer(GL_ARRAY_BUFFER, m_bufferIds[0]); in a render thread during the draw call. (the same applies to every openGL object i have)
But I don't now if i'm right or wrong.

Related

X11 DestroyNotify event returns wrong window parameter

I'm currently working on writing a reparenting window manager using C++ and the XCB library. I unmanage windows when I recieve an UnmapNotify event; in this case the code is very simple:
if (unmap_ignore > 0) {
unmap_ignore--;
return;
}
client *cl = nullptr;
size_t idx = 0;
for (client &c : clients) {
if (c.window == ev->window) {
cl = &c;
break;
}
idx++;
}
if (!cl)
return;
xcb_unmap_window(conn, cl->frame);
clients.erase(clients.begin() + idx);
However, this does not work for some clients that don't bother unmapping the window (usually happens when you force kill the process). Then instead a DestroyNotify is sent. However the ev->window field there (typeof ev = xcb_destroy_notify_event_t) is some value that isn't what I want... here are some example logs (also note each line is a new DestroyNotify event, I get it twice for some reason and each time the #window field is different but not correct):
Found a client window: 4194307 but this was destroyed: 4194305
Found a client window: 4194307 but this was destroyed: 4194313
I remember before I tried to write a window manager in Xlib and had this exact same issue. I must be missing something obvious though, but if I look at the code of other reparenting window managers like Awesome or Herbsluftwm they just use the window field and have no problems. What am I doing wrong?
(my full code is here: http://ix.io/3yDj)
One thing I suspect is that it is just giving me two children windows of the destroyed parent instead of sending the actual parent. no idea why, but it is the most logical thing I can come up with. I just want to be able to handle this so I can unmap the frame for things that don't send UnmapNotify events.
I also first thought that it may report DestroyNotify on the parent window instead of the client reparented one, so I tried checking the frame but to no avail:
Found a client window: 2097152 but this was destroyed: 4194305
Found a client window: 2097152 but this was destroyed: 4194313
I've been truly stumped on this so any help is appreciated, thanks!
Awesome for example does the exact same thing as I tried with ev->window:
static void
event_handle_destroynotify(xcb_destroy_notify_event_t *ev)
{
client_t *c;
if((c = client_getbywin(ev->window)))
client_unmanage(c, CLIENT_UNMANAGE_DESTROYED);
else
for(int i = 0; i < globalconf.embedded.len; i++)
if(globalconf.embedded.tab[i].win == ev->window)
{
xembed_window_array_take(&globalconf.embedded, i);
luaA_systray_invalidate();
}
}

Making a separate thread for rendering made my code slower

I had a method called run in which I am updating and rendering the game objects.
void Run(olc::PixelGameEngine* pge) noexcept
{
Update(pge);
Render(pge);
}
Frame rate then was fluctuating 300~400 frames in release mode and 200~300 frames in debug mode. I have yet to add lodes of game logic, so I thought I would do rendering in a separate thread, so after a quick tutorial later I changed it to this.
void Run(olc::PixelGameEngine* pge) noexcept
{
Update(pge);
std::thread renderer(&GameManager::Render, this, pge);
renderer.join();
}
Now the frame rate is around 100~150 frames in release mode and 60~100 frames in debug mode.
std::thread creates a thread.
renderer.join() waits until the thread has finished.
Basically the same logic as your first example, but you create and destroy a thread in your 'loop'. Much more work than before, not surprising that the framerate goes down.
What you can do:
define two functions, one for the update and one for the render thread
introduce an global atomic flag
set it in the update function (indicates scene has been updated)
clear it in the render function (indicates that the changes has been presented)
create a thread (runnable, either update or render)
if the scene gets updated, set the flag accordingly
the renderer can decide based on the flag to render the scene or wait until the scene gets updated or render anyway (clear the flag)
the updater can decide based on the flag to wait until the scene has been rendered or update it anyway
Example (c++11):
#include <atomic>
#include <thread>
atomic_bool active = true;
atomic_bool scene_flag = false;
void render(olc::PixelGameEngine* pge) noexcept
{
while (active) {
//while (scene_flag == false) //wait for scene update
renderScene(pge);
scene_flag = false; //clear flag
}
}
void update(olc::PixelGameEngine* pge) noexcept
{
while (active) {
//while (scene_flag == true) //wait for scene render
updateScene(pge);
scene_flag = true; //set flag
}
}
int main()
{
thread u(update, nullptr);
thread r(render, nullptr);
/*while (some_condition) ...*/
active = false;
u.join();
r.join();
return 0;
}
Note: The above code could/will update the scene while it is being rendered (could/will lead to several problems). Proper synchronization is recommended.

SFML thread synchronization?

I'm new to SFML, been trying to have a multi-threading game system (all of the game logic on the main thread, and the rendering in a dedicated thread using sf::Thread; mainly for practicing with threads) as explained in this page ("Drawing with threads" section):
Unfortunately my program has a long processing time during it's update() and makes the rendering process completely out of control, showing some frames painted and some others completely empty. If it isn't obvious my rendering thread is trying to paint something that isn't even calculated, leaving this epileptic effect.
What I'm looking for is to allow the thread to render only when the main logic has been calculated. Here's what I got so far:
void renderThread()
{
while (window->isOpen())
{
//some other gl stuff
//window clear
//window draw
//window display
}
}
void update()
{
while (window->isOpen() && isRunning)
{
while (window->pollEvent(event))
{
if (event.type == sf::Event::Closed || sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
{
isRunning = false;
}
else if (m_event.type == sf::Event::Resized)
{
glViewport(0, 0, m_event.size.width, m_event.size.height);
}
}
// really resource intensive process here
time = m_clock.getElapsedTime();
clock.restart().asSeconds();
}
}
Thanks in advance.
I guess the errors happen because you manipulate elements that are getting rendered at the same time in parallel. You need to look into mutexes.
Mutexes lock the element you want to manipulate (or draw in the the other thread) for as long as the manipulation takes and frees it afterwards.
While the element is locked it can not be accessed by another thread.
Example in pseudo-code:
updateThread(){
renderMutex.lock();
globalEntity.manipulate();
renderMutex.unlock();
}
renderThread(){
renderMutex.lock();
window.draw(globalEntity);
renderMutex.unlock();
}

How to combine RegisterDragDrop, RoInitialize to work together in one thread?

I have a dilemma. My GUI-based C++ app requires to implement drag-and-drop functionality. At the same time, I'm converting this Win32 app to UWP to submit to Windows Store. But there's one issue:
To implement drag-and-drop I need to call these two methods:
OleInitialize(NULL);
//...
HRESULT hr = RegisterDragDrop(hMainWnd, pDropTarget);
and to init WinRT stuff to work with Windows Store, I need to call:
HRESULT hr = RoInitialize(RO_INIT_MULTITHREADED);
Unfortunately OleInitialize initialized COM as single-thread apartment and RoInitialize requires multi-threaded model, while RegisterDragDrop cannot function without calling OleInitialize.
Any idea how to resolve it? (apart from moving RoInitialize and all WinRT code into a worker thread, that will complicate things.)
Raymond Chen in his usual condescending way is pretty good at criticizing things but offers no fix to an existing problem. I'm posting this mostly for later self-reference and in case someone else stumbles upon the same issue as well. I just spent several days trying to resolve this bug, so maybe it will save time for someone else.
Problem
First off, this is a native Win32 code (no .NET or C++/CX.) It is C++ with a sprinkle of WRL for easier handling of WinRT/COM stuff.
In my case I have a Win32 GUI app that implements drag-and-drop of files into its main window. So to init it, one needs to do this from the main thread, right when the app starts:
OleInitialize(NULL);
//...
HRESULT hr = RegisterDragDrop(hMainWnd, pDropTarget);
The OleInitialize call above will initialize COM for the main thread to use single-thread apartment, which is required for RegisterDragDrop to succeed. Without it, the drag-and-drop function will not work.
Then, say you decide to convert this Win32 app to UWP using Microsoft's Project Centennial converter for inclusion into Windows 10 store.
When the app is converted and listed in the store under their trial-license scheme, you will employ the following logic to check if the user has a trial or an activated (i.e. purchased) copy of the app. You'll begin it as such:
//Init COM for WinRT
RoInitialize(RO_INIT_MULTITHREADED);
ComPtr<IStoreContextStatics> pStoreContextStatics;
if(SUCCEEDED(RoGetActivationFactory(
HStringReference(L"Windows.Services.Store.StoreContext").Get(),
__uuidof(pStoreContextStatics),
&pStoreContextStatics)) &&
pStoreContextStatics)
{
//Get store context for the app
ComPtr<IStoreContext> pStoreContext;
if(SUCCEEDED(pStoreContextStatics->GetDefault(&pStoreContext)) &&
pStoreContext)
{
//Got store context
//....
}
}
and then if you need to know trial vs. activated status of the app, using this logic, you'd call:
ComPtr<IAsyncOperation<StoreAppLicense*>> p_opAppLic;
if(SUCCEEDED(pStoreContext->GetAppLicenseAsync(p_opAppLic)) &&
p_opAppLic)
{
ComPtr<IAsyncOperationCompletedHandler<StoreAppLicense*>> p_onAppLicCallback =
Callback<Implements<RuntimeClassFlags<ClassicCom>, IAsyncOperationCompletedHandler<StoreAppLicense*>, FtmBase>>(
[](IAsyncOperation<StoreAppLicense*>* pOp, AsyncStatus status)
{
if (status == AsyncStatus::Completed)
{
ComPtr<IStoreAppLicense> pAppLicResult;
if(SUCCEEDED(pOp->GetResults(&pAppLicResult)) &&
pAppLicResult)
{
BYTE nActive = -1;
BYTE nTrial = -1;
pAppLicResult->get_IsActive(&nActive);
pAppLicResult->get_IsTrial(&nTrial);
//Get app's store ID with SKU
HString strStoreId;
pAppLicResult->get_SkuStoreId(strStoreId.GetAddressOf());
if(nActive == 1 &&
nTrial == 0)
{
//Activated, or purchased copy
}
else if(nActive == 1 &&
nTrial == 1)
{
//Trial copy
}
else
{
//Error -- store returned some gibberish
}
}
}
return S_OK;
});
if(SUCCEEDED(p_opAppLic->put_Completed(p_onAppLicCallback.Get())))
{
//Success initiating async call
}
}
So, if you do all this, your UWP-converted app will behave in a very strange way. Here's an example. Say a user purchases a license for the app thru Windows Store. In turn your app logic calls the code above to see if the app is activated, but what you get back is nActive=0 and nTrial=1. Then if you check strStoreId it will be your app store ID but without the SKU. WTF!?
I know, it's really confusing. As an aside, let me explain. When you first list your app in a Windows Store it will be assigned a Store ID. Something like: ABCDEFG12345. Then if you submit any follow-up update(s) to the first version of the same app, they will add a SKU number to it, that will make the whole app ID change to ABCDEFG12345/0010, then ABCDEFG12345/0011 for the next update, and so on.
Well, the WinRT code above would return my app store ID as ABCDEFG12345 without any SKU attached to it. Which was wrong, since it was a third or so update to the first version of the app. And thus any additional attributes for that app store ID were also wrong.
So that was the issue that I was faced with...
Cause
All the headache that I described above was caused by my omission to check the result code returned from the first RoInitialize call. I would be able to catch the problem much faster if I did this:
//Init COM for WinRT
if(FAILED(RoInitialize(RO_INIT_MULTITHREADED)))
{
//WinRT COM initialization failed
//Go scratch your head why....
}
In this case RoInitialize will fail with error code RPC_E_CHANGED_MODE. The documentation for it is as helpful as Windows Help (F1) option:
A previous call to RoInitialize specified the concurrency model for
this thread as multithread apartment (MTA). This could also indicate
that a change from neutral-threaded apartment to single-threaded
apartment has occurred.
What previous call? The only parameter anyone can call it with is RO_INIT_MULTITHREADED.
So I started digging deeper and by the process of elimination found that the OleInitialize call earlier was the reason why RoInitialize failed and caused the cascade of events that I described above.
Thus I was at the point of asking the question here.
Note on the side, that the bug ridden WinRT library (ref1, ref2, ref3, ref4, ref5) gave me no indications of a problem in all the calls following RoInitialize and somewhere internally silently failed to retrieve the app's SKU because of a single-thread apartment COM initialization.
Hack/Workaround
As was suggested by RbMm in the comments above, doing the following will work, but is a totally undocumented behavior:
if(SUCCEEDED(OleInitialize(0))
{
CoUninitialize();
}
CoInitializeEx(NULL, COINIT_MULTITHREADED);
So if you don't want your app to start crashing for no apparent reason, I would not use it.
Solution
My solution that I went with was to move all the WinRT COM stuff (code I listed above: 2nd and 3rd code segments) into a separate worker thread. It will work fine from there. The issue is marshalling calls between your main thread and this worker thread. It is doable, but requires some work, i.e. using mutexes and events for synchronized access, etc.
So if anyone finds an easier fix for this, please post your solution. I'll mark it as the answer.
solution to the IDsObjPicker crashes I mentioned in the comment ealier, quick code I wrote just now.
Use code below as:
TDsObjPicker lv_PickInfo;
memset(&lv_PickInfo, 0, sizeof(TDsObjPicker));
Sec_InitDsObjPicker(&lv_PickInfo, &lv_InitInfo);
Sec_InvokeDsObjPicker(&lv_PickInfo, 0, &lv_oData);
Solution is to run the dialog in another thread and init the thread without the Ole+Com combination:
// command codes
#define DSOPCMD_EXITTHREAD 1
#define DSOPCMD_INITIALIZE 2
#define DSOPCMD_INVOKE 3
// parameters of object picker via thread
typedef struct tagDsObjPicker
{
// thread handle
HANDLE hThread;
// events
HANDLE hCmdEvt;
HANDLE hRdyEvt;
// commands
UINT CmdCode;
HRESULT hResult;
// command parameters - DSOPCMD_INITIALIZE
DSOP_INIT_INFO *InitInfo;
// command parameters - DSOPCMD_INVOKE
HWND hWnd;
IDataObject **oData;
//
} TDsObjPicker;
DWORD CALLBACK _Sec_DsObjPickerThread(VOID *in_Param)
{
/* locals */
HRESULT lv_hCreateResult;
HRESULT lv_hResult;
TDsObjPicker *lv_PickInfo;
IDsObjectPicker *lv_oPicker;
// get info structure
lv_PickInfo = (TDsObjPicker*)in_Param;
// init COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// preclear object pointer
lv_oPicker = NULL;
// create instance of picker
lv_hCreateResult = CoCreateInstance(
CLSID_DsObjectPicker, NULL, CLSCTX_INPROC_SERVER,
IID_IDsObjectPicker, (VOID**)&lv_oPicker);
// while thread is not aborted
while (lv_PickInfo->CmdCode != DSOPCMD_EXITTHREAD)
{
// wait for command event
if (WaitForSingleObject(lv_PickInfo->hCmdEvt, INFINITE) == 0)
{
// what command?
switch (lv_PickInfo->CmdCode)
{
// call init
case DSOPCMD_INITIALIZE:
{
// call object
if (lv_hCreateResult)
lv_hResult = lv_hCreateResult;
else
lv_hResult = lv_oPicker->Initialize(lv_PickInfo->InitInfo);
// done
break;
}
// call invoke
case DSOPCMD_INVOKE:
{
// call object
if (lv_hCreateResult)
lv_hResult = lv_hCreateResult;
else
lv_hResult = lv_oPicker->InvokeDialog(lv_PickInfo->hWnd, lv_PickInfo->oData);
// done
break;
}
// other command codes
default:
lv_hResult = E_FAIL;
break;
}
// store result
lv_PickInfo->hResult = lv_hResult;
// notify caller
SetEvent(lv_PickInfo->hRdyEvt);
}
}
// destroy the picker object
if (lv_oPicker)
lv_oPicker->Release();
// cleanup COM
CoUninitialize();
// leave the thread
return 0;
}
VOID Sec_DoneDsObjPicker(TDsObjPicker *in_PickInfo)
{
// is thread created?
if (in_PickInfo->hThread)
{
// set command code
in_PickInfo->CmdCode = DSOPCMD_EXITTHREAD;
// trigger the thread to process the code
SetEvent(in_PickInfo->hCmdEvt);
// wait for thread to finish
WaitForSingleObject(in_PickInfo->hThread, INFINITE);
// close thread handle
CloseHandle(in_PickInfo->hThread);
}
// close event handles
if (in_PickInfo->hCmdEvt) CloseHandle(in_PickInfo->hCmdEvt);
if (in_PickInfo->hRdyEvt) CloseHandle(in_PickInfo->hRdyEvt);
// clear
memset(in_PickInfo, 0, sizeof(TDsObjPicker));
}
HRESULT Sec_InitDsObjPicker(TDsObjPicker *in_PickInfo, DSOP_INIT_INFO *in_InitInfo)
{
/* locals */
DWORD lv_TID;
// thread not yet created?
if (!in_PickInfo->hThread)
{
// create events
in_PickInfo->hCmdEvt = CreateEvent(0,0,0,0);
in_PickInfo->hRdyEvt = CreateEvent(0,0,0,0);
// if ok
if (in_PickInfo->hCmdEvt && in_PickInfo->hRdyEvt)
{
// create the thread
in_PickInfo->hThread = CreateThread(
0, 0, _Sec_DsObjPickerThread, in_PickInfo, 0, &lv_TID);
}
// failed?
if (!in_PickInfo->hThread)
{
// cleanup
Sec_DoneDsObjPicker(in_PickInfo);
// return with error
return E_OUTOFMEMORY;
}
}
// store parameter
in_PickInfo->InitInfo = in_InitInfo;
// set command code
in_PickInfo->CmdCode = DSOPCMD_INITIALIZE;
// trigger the thread to process the code
SetEvent(in_PickInfo->hCmdEvt);
// wait for result
WaitForSingleObject(in_PickInfo->hRdyEvt, INFINITE);
// return the result
return in_PickInfo->hResult;
}
HRESULT Sec_InvokeDsObjPicker(TDsObjPicker *in_PickInfo, HWND in_hWnd, IDataObject **out_oData)
{
/* locals */
MSG lv_Msg;
// thread not yet created?
if (!in_PickInfo->hThread)
return E_FAIL;
// store parameters
in_PickInfo->hWnd = in_hWnd;
in_PickInfo->oData = out_oData;
// set command
in_PickInfo->CmdCode = DSOPCMD_INVOKE;
// trigger the thread
SetEvent(in_PickInfo->hCmdEvt);
// process messages of this thread while picker runs in other thread until event
while (MsgWaitForMultipleObjects(1, &in_PickInfo->hRdyEvt, 0, INFINITE, QS_ALLINPUT) != 0)
{
// get next message
while (PeekMessage(&lv_Msg, 0,0,0, PM_REMOVE))
{
// translate/dispatch the message
TranslateMessage(&lv_Msg);
DispatchMessage(&lv_Msg);
}
}
// return the result
return in_PickInfo->hResult;
}
You asked why calling OleInitialize() first, followed by CoUnintialize and then reinit COM via CoInitializeEx works and is safe, look at the code of the rewritten OLE server in WINE, https://github.com/wine-mirror/wine/blob/master/dlls/ole32/ole2.c it comes pretty close to the "real thing". The OleInitialize calls CoInitializeEx itself with COINIT_APARTMENTTHREADED and fails before doing the OLE-specific initializations upon a fail of CoInitializeEx. There is no reason to fail as the OLE code can run as well in MULTITHREADED mode. Remember MULTITHREADED means the caller must take care of synchronisation/locking while with APARTMENTTHREADED the COM libray will handle it for the code. So if you make sure you do not call the OLE code like dragdrop and clipboard at the same time from multiple threads then there is no problem. Keeping all UI in the main thread will do that. As you should already write multithreaded-aware code yourself using the requested MULTITHREADED mode.
I have the problem with directshow filters/drivers which lock the process when COM is initialized with APARTMENTTHREADED even when directshow is called from a thread with THREADED while the main UI thread runs in APARTMENTTHREADED.
Uninitializing COM after initializing OLE, then re-inititializing COM with MULTITHREAED during startup in the main UI thread makes you bypass the failure in OleInitialize. It is the best solution to make sure all runs well.

OpenGL VBO not updating properly

I am having an OpenGL VBO problem. I downloaded the old VBO example called Lesson45 from NeHe and I modified it to check something.
My end result is to create about 9 tiles, one of them being the origin. Then as the player moves on the screen, the top/bottom rows/columns update the data. But for now I want something basic:
I create one VBO and then I want to update the data in another thread. While the data is being uploaded, I do not want to draw the VBO because that would cause problems.
Here I create the VBO:
glGenBuffersARB( 1, &m_nVBOVertices );
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_nVBOVertices);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_DYNAMIC_DRAW_ARB);
I create a thread, I set up an OpenGL context, I share lists. Then I process the data, when the user presses "R" on the keyboard:
while(TerrainThreadRun)
{
//look for R
if(window.keys->keyDown[82] == TRUE && keyactivated == false)
{
keyactivated = true;
window.keys->keyDown[82] = FALSE;
}
if(keyactivated)
{
for(int i = 0; i < g_pMesh->m_nVertexCount; i++)
{
g_pMesh->m_pVertices[i].y = 800.0f;
}
while(!wglMakeCurrent(window.hDCThread,window.hRCThread))//This was removed
Sleep(5);//This was removed
glBindBufferARB(GL_ARRAY_BUFFER_ARB, g_pMesh->m_nVBOVertices);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, g_pMesh->m_nVertexCount*3*sizeof(float), g_pMesh->m_pVertices);
keyactivated = false;
}
}
To draw the data:
if(!keyactivated)
{
glEnableClientState( GL_VERTEX_ARRAY );
glBindBufferARB(GL_ARRAY_BUFFER_ARB, g_pMesh->m_nVBOVertices);
glVertexPointer(3, GL_FLOAT, 0, (char*)NULL);
glDrawArrays(GL_TRIANGLES, 0, g_pMesh->m_nVertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
}
I know that using ARB extensions is not recommended, but this is just for a quick basic example.
The problem is that when I first press "R", the data does not get updated. The VBO draws the same. The second time that I press "R", it updates the data. What can I do to force the draw. Am I doing something wrong?
Does the data need to be forced to the video card? Am I missing something?
Update: I looked over my code and now I use wglMakeCurrent only once, when the context is initialized. In the thread, I use it after sharing the lists and on the main thread as soon as the lists are shared, like this:
window->hRC = wglCreateContext (window->hDC);
if (window->hRC ==0)
{
// Failed
}
TerrainThreadRun = true;
TerrainThread = CreateThread(NULL, NULL,(LPTHREAD_START_ROUTINE)TerrainThreadProc, 0, NULL, NULL);
while(!sharedContext)
Sleep(100);
if (wglMakeCurrent (window->hDC, window->hRC) == FALSE)
And in the thread:
if (!(window.hRCThread=wglCreateContext(window.hDCThread)))
{
//Error
}
while(wglShareLists(window.hRC, window.hRCThread) == 0)
{
DWORD err = GetLastError();
Sleep(5);
}
sharedContext = true;
int cnt = 0;
while(!wglMakeCurrent(window.hDCThread,window.hRCThread))
Sleep(5);
while(TerrainThreadRun)
{
//look for R
Second update: I tried using glMapBuffer instead of glBuferSubData, but the application behaves the same. Here is the code:
void *ptr = (void*)glMapBuffer(GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);
if(ptr)
{
memcpy(ptr, g_pMesh->m_pVertices, g_pMesh->m_nVertexCount*3*sizeof(float));
glUnmapMapBuffer(GL_ARRAY_BUFFER_ARB);
}
Update three:
I was doing some things wrong, so I modified them, but the problem remains the same. Here is how I do everything now:
When the application loads, I create two windows, each with its own HWND. Based on them, I create two device contexts.
Then I share the lists between them:
wglShareLists(window.hRC, window.hRCThread);
This is done only once when I initialize.
After that I show the OGL window, which renders; I make the context active. Then I load the function pointers and create the VBO.
After the main rendering OGL is done, I create the thread. When the thread is loaded, I make its device context active.
Then we do normal stuff.
So my question is: Do I need to update the function pointers for each device context? Could this be my problem?
As an update, if I run my test app in gDEBugger and I first press "R" and then pause, it doesn't display correctly. I take a look at the memory (Textures, Buffers and Image Viewers) and GLContext1(I think the main rendering thread) device context has the OLD data. While GLContext2 (Shared-GL1) (I think the thread context) has the correct data.
The odd part, if I look back at GLContext1, with the program still in pause mode, now it displays the new data, like it "refreshed" it somehow. And then if I press play, it starts drawing correctly.
I found the solution, I need to call glFinish() in the worker thread after calling glUnmapBuffer. This will solve the problem and everything will render just fine.