상세 컨텐츠

본문 제목

OpenGL에서 텍스쳐 로딩, 스레드 thread 에서의 텍스처 로딩 관련

프로그래밍 관련/3D,2D DRAW 관련

by AlrepondTech 2020. 9. 15. 17:38

본문

반응형

 

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 

출처: https://www.opengl.org/discussion_boards/showthread.php/179036-Some-multithreading-questions

 

Some multithreading questions

I had thought that I understood threading and OpenGL, but I seem to be experiencing some multithreaded bugs and its made me question myself. Perhaps they are just driver issues because I'm off the beaten video game path. I wanted to sort of play a true/false game to check if my understanding agrees with everyone. Also I'm more interested in what is the best practice (that will help me avoid driver quirks rather than what the GL specification says which is somewhat incomplete with regards to multithreading shared contexts imo).

1) If contextA is current to threadA and contextB is current to threadB and the contexts have been previously shared, do we have to worry about thread protection/mutexes? I'd assume that in the driver contextA and contextB share a table of textures and furthermore since OpenGL is not thread safe then we need explicit protection for modification of existing objects and whenever creating/destroying objects:
(a) to prevent threadA and threadB from modifying the same object at once and 
(b) to handle the case where the texture table could be modified simultaneously by multiple threads at once. eg. suppose contextA creates a new texture ID and the driver decides it needs to grow the shared texture table data structure whilst at the same time contextB is attempting to bind a texture from the very same table

2) If I have shared contextA and contextB and now wish to delete just contextB but continue to use contextA then its safe to delete it, but both must be not current (or just no OpenGL operations must occur on contextA during wglDeleteContext?).

3) If I create contextA and contextB on the same thread then they must be shared on that same thread or the driver may become upset.

4) If I want to share contextA and contextB they must be created on the same thread or the driver may become upset. Said differently: you shouldn't share contexts that were created on different threads.

5) It is not required but it is the best practice to share your new context before you create any GL objects on it. ie. always share new empty context with existing context

6) It is not required but it is the best practice to have one hub context (that you never use except to share contexts with) which all other contexts share themselves with

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

I am busy right now but there is a current thread all about multithread (http://www.opengl.org/discussion_boa...ltiple-threads) that you might find insightful. Will check back here later...

God have mercy on the soul that wanted hard decimal points and pure ctor conversion in GLSL.

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

1) The OpenGL 4.3 specification has an entire section on objects and multiple contexts (and thus threads). In particular, you need to prevent 1a, but 1b should be fine. That is, you can create different object names and object instances in different threads. The creation is, for all intents and purposes, atomic.

2) If you're deleting contextB, contextA does not care. You do need to make sure contextB isn't current, but that's about it.

3)

If I create contextA and contextB on the same thread then they must be shared on that same thread or the driver may become upset.

Perhaps, but one would hope that you're using wgl/glXCreateContexAttribARB(), which specifies that the newly created context will be shared with another at creation time.

4) No idea.

5)

It is not required but it is the best practice to share your new context before you create any GL objects on it. ie. always share new empty context with existing context

It is required. For example, wglShareLists states:

 Originally Posted by MSDN

Specifies the OpenGL rendering context to share display lists with hglrc1. The hglrc2 parameter should not contain any existing display lists when wglShareLists is called.

If we extend this to all shared objects, then the new context must not have created any shareable objects. Again, wgl/glXCreateContextAttribsARB creates and shares contexts; use that where possible.

6) That's up to you. However you want to manage your contexts, objects, etc is your prerogative. If you want to keep a phantom context around just to keep objects alive, you can do that.

Such a thing might be useful for dealing with window re-creation (such as when resizing the desktop or some-such) without destroying every OpenGL object.

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Hi Stephen_H
I have two threads: one loading thread and one render thread.

At the beginning of the program, I create two render contexts in one of the threads (render thread in this case):

Code :

///--- OpenGL 2.1
int attributes[] = {
    WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
    WGL_CONTEXT_MINOR_VERSION_ARB, 1,
    WGL_CONTEXT_FLAGS_ARB, 0,
    0
    };
HGLRC rcGL21=wglCreateContextAttribsARB(hDC, NULL, attributes);
if(rcGL21!=NULL){
    g_hRTRC=rcGL3x;
    g_hMTRC=wglCreateContextAttribsARB(hDC, NULL, attributes);
    if(g_hMTRC==NULL){
        ... Error
    }
}
 else{
   ... Error
}
 
if(!wglShareLists(g_hMTRC, g_hRTRC)){
   ... Error
}
if(!wglMakeCurrent(hDC, g_hRTRC)){
   ... Error
}


And after that in the other thread:

Code :

if(!wglMakeCurrent(hDC, g_hMTRC)){ ... Error }


And everything works properly. The only thing I have noticed is that you cannot call to glTexImage2D at the same time in the two threads. But if you use one thread to create/load 'objects' (textures, framebuffers, ...) and the other to render, everything works properly.

Hope this helps.

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

I think the best practice is to not use multi threading for OpenGL. That is, only one thread is needed to keep the graphics card busy too 100%, and so you can't gain any FPS by using more than one thread.

Of course, you can still use threads to prepare data that will be used. A good way to design this is to use some kind of thread pool.

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

If you're trying to optimize texture transfers, you could check out this presentation by NVidia. Or the slides here(PDF)

 

 

 

=================================

=================================

=================================

 

 

 

출처: https://www.opengl.org/discussion_boards/showthread.php/161378-OpenGL-rendering-on-a-seperated-thread

Hello,

I whould like to use 2 threads, one for the OpenGL rendering and an other for the rest of my software. However, I'm not able to make OpenGL working on the thread... When I use wglGetCurrentContext, the result is always 0 but it work well on the main thread.

When we use a thread, is there special setting to set? Are we supposed to create the OpenGL context on the thread that will use it? 

Thanks

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

@Zengar:
Strange... I did some tests with multiple contexts and threads. In this test I did:
- create main render context
- create two more contexts and share lists with main render context (wglCreateContext and wglShareLists)
- fire up two additional threads
- in thread func call wglMakeCurrent once, enter render loop and render result in texture (one thread - one context)
- each of this two threads work at different framerates
- main thread (and main render context) collect RTT textures from other two contexts and render in on backbuffer.

So.. to access to context from thread it MUST be current only in calling thread. If some other thread keep copntext current any other wglMakeCurrent call will fall until owning thread release it (by calling wglMakeCurrent(0,0))

Code :

#include "StdAfx.h"
#define GLDEBUG
#include "Composer.h"
 
CThreadContext::CThreadContext()
{
	m_msInterval = 0;
	m_hDC = NULL;
	m_parentRC = NULL;
	m_hRC = NULL;
	m_hThread = NULL;
	m_TexW = 0;
	m_TexH = 0;
}
 
CThreadContext::~CThreadContext()
{
	Stop();
}
 
void CThreadContext::SetParams(int ms, HDC hDC, HGLRC hRC)
{
	m_msInterval = ms;
	m_hDC = hDC;
	m_parentRC = hRC;
 
	m_hRC = wglCreateContext (m_hDC);
 
	if (wglShareLists(m_parentRC, m_hRC) == FALSE)
	{
		DWORD err = GetLastError();
		Log.Error("wglShareLists fail %d", err);
	}
}
 
void CThreadContext::SetSize(int w, int h)
{
	m_TexW = w;
	m_TexH = h;
}
 
void CThreadContext::SetRenderObject(IRenderObject* pRO)
{
	m_pRO = pRO;
}
 
void CThreadContext::Run()
{
	if (m_hThread != NULL) return;
	DWORD nThreadID;
 
	m_hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, (LPVOID)this, 0, (LPDWORD)&nThreadID);
}
 
DWORD WINAPI CThreadContext::ThreadFunc( LPVOID lpParameter )
{
	CThreadContext* pThis = (CThreadContext*)lpParameter;
	pThis->Work();
	return 1;
}
 
void CThreadContext::Work()
{
	if (m_hRC)
	{
		if (wglMakeCurrent(m_hDC, m_hRC) == FALSE)
		{
			DWORD err = GetLastError();
			Log.Error("wglMakeCurrent fail %d", err);
		}
		rtt.Create(GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, true, m_TexW, m_TexH);
 
		m_pRO->Init();
 
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60, (float)m_TexW/(float)m_TexH, 0.1f, 500.0f);
 
		glMatrixMode(GL_MODELVIEW);
 
		while (1)
		{
			if (m_evStop.Wait(m_msInterval)) break;
 
			m_pRO->Update();
			rtt.Activate();
			m_pRO->Render();
			rtt.Deactivate();
		}
		rtt.Delete();
		m_pRO->Done();
	}
}
 
void CThreadContext::Stop()
{
	m_evStop.Set();
	Sleep(m_msInterval*2);
}
 
class RenderObject1: public IRenderObject
{
public:
	RenderObject1(){alpha = 0.0f;}
	virtual ~RenderObject1(){}
 
	void Update()
	{
		alpha += 0.5;
		m_Fps.Update();
	}
 
	void Render()
	{
		GLCALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
 
		glMatrixMode(GL_MODELVIEW);
		gluLookAt(5,5,5, 0,0,0, 0,1,0);
		glRotatef(alpha, 0,1,0);
		glColor3f(1,0,0);
		auxSolidBox(3,3,3);
	}
 
	float alpha;
 
};
 
class RenderObject2: public IRenderObject
{
public:
	RenderObject2(){alpha = 0.0f;}
	virtual ~RenderObject2(){}
 
	void Update()
	{
		alpha += 0.5;
		m_Fps.Update();
	}
 
	void Render()
	{
		GLCALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
 
		glMatrixMode(GL_MODELVIEW);
		gluLookAt(5,5,5, 0,0,0, 0,1,0);
		glRotatef(alpha, 0,1,0);
		glColor3f(0,1,0);
		auxSolidBox(3,3,3);
	}
 
	float alpha;
};
 
 
 
/* ********************************************************************** 
 
********************************************************************** */
 
CComposerApp::CComposerApp(void)
{
}
 
CComposerApp::~CComposerApp(void)
{
}
 
 
void CComposerApp::Update(float t, float d)
{
	__super::Update(t, d);
}
 
void CComposerApp::Render()
{
	BeginRender();
 
	RenderGrid(1.0);
	glColor3f(1,1,1);
	glEnable(GL_TEXTURE_RECTANGLE_ARB);
 
	// texture from first context
	c1.rtt.EnableTexture(0);
	glPushMatrix();
	glTranslatef(-1.1,0,0);
	glBegin(GL_QUADS);
	{
		glTexCoord2f(0,0);									glVertex3f(-1,0,0);
		glTexCoord2f(m_ViewPortSize.x,0);					glVertex3f(1,0,0);
		glTexCoord2f(m_ViewPortSize.x, m_ViewPortSize.y);	glVertex3f(1,1,0);
		glTexCoord2f(0,m_ViewPortSize.y);					glVertex3f(-1,1,0);
	}
	glEnd();
	glPopMatrix();
	c1.rtt.DisableTexture(0);
 
	// texture from second context
	c2.rtt.EnableTexture(0);
	glPushMatrix();
	glTranslatef(1.1,0,0);
	glBegin(GL_QUADS);
	{
		glTexCoord2f(0,0);									glVertex3f(-1,0,0);
		glTexCoord2f(m_ViewPortSize.x,0);					glVertex3f(1,0,0);
		glTexCoord2f(m_ViewPortSize.x, m_ViewPortSize.y);	glVertex3f(1,1,0);
		glTexCoord2f(0,m_ViewPortSize.y);					glVertex3f(-1,1,0);
	}
	glEnd();
	glPopMatrix();
	c2.rtt.DisableTexture(0);
 
 
	glDisable(GL_TEXTURE_RECTANGLE_ARB);
 
	AddReport("RO1 %f", ro1->m_Fps.GetFps());
	AddReport("RO2 %f", ro2->m_Fps.GetFps());
	EndRender();
 
}
 
void CComposerApp::Done()
{
	c1.Stop();
	c2.Stop();
}
 
bool CComposerApp::Init()
{
	ro1 = new RenderObject1;
	ro2 = new RenderObject2;
 
	c2.SetParams(90, g_hDC, g_hRC);
	c1.SetParams(10, g_hDC, g_hRC);
 
	c1.SetRenderObject(ro1);
	c2.SetRenderObject(ro2);
 
	c1.SetSize(m_ViewPortSize.x, m_ViewPortSize.y);
	c2.SetSize(m_ViewPortSize.x, m_ViewPortSize.y);
 
	c1.Run();
	c2.Run();
 
	wglMakeCurrent(g_hDC, g_hRC);
	Sleep(100);
	__super::Init();
 
	return true;
}
 
IEngine* NewEngine(){ return new CComposerApp;}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Re: OpenGL rendering on a seperated thread

I am experiencing the exact same problem as the original poster. I have some code that was doing fine, but now that I have spun the creation of this window off into another thread, it is no longer working. wglGetCurrent fails with a return code of zero, and a GL_INVALID_OPERATION error. I've checked, and everything looks like it should be fine. If the original poster has any resolution, or anyone has any other suggestions, I would appreciate hearing them.

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://alleysark.tistory.com/233

 

GLUT를 사용하지 않고 openGL을 사용하기 위한 가장 중요한 과정은 openGL의 context를 얻는것이다. Windows에서 rendering context라 불리는 이 context를 얻기 위해서는 먼저 device context를 가지고 있어야한다. device context는 GDI에서 정의하는 그래픽 오브젝트에 대한 여러 특성을 담고있는 구조체(핸들러)이며 다음과 같이 윈도우 핸들러에 대한 DC를 얻어올 수 있다.

mhDC = GetDC(mhWnd);

 

다음으로 아래와 같이 DC의 포멧 설정을 한 후,

PIXELFORMATDESCRIPTOR pfd;

int nPixelFormat;

memset(&pfd, 0, sizeof(pfd));

pfd.nSize = sizeof(pfd);

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;

nPixelFormat = ChoosePixelFormat(mhDC, &pfd);

SetPixelFormat(mhDC, nPixelFormat, &pfd);

 

설정 된 DC에 대한 RC를 가져온다.

mhRC = wglCreateContext(mhDC);

 

이렇게 만들어진 context를 내 어플리케이션에서 사용하기 위해서는 생성된 context를 현재 사용되는 context로 설정해줘야한다.

wglMakeCurrent(mhDC, mhRC);

 

이는 메모리가 굉장히 작던 시절 context에 대한 메모리 비용이 크기때문에 여러 프로세스에서 하나의 context를 공유하며 쓰던 방식이 남아있기 때문이다. 하지만 opengl의 api가 context를 문제 없이 사용하기 위해서는 윈도우에 DC를 온전히 할당해줄 필요가 있는데, 이를 위해서 윈도우의 스타일에 CS_OWNDC 속성을 반드시 추가시켜야 한다.

이제 context를 가졌으니 gl의 함수를 원하는 만큼 사용하면 된다. 픽셀 포멧에 더블 버퍼를 사용한다 하였으니 렌더링을 위해서

SwapBuffers(mhDC);

 

를 통해 버퍼를 스왑해주도록 한다.

context를 다 사용하고 난 후에는 제거해주는 과정이 필수적이다. RC는 더이상 사용하지 않으니 제거하고, DC는 윈도우에게 돌려준다.

 

wglMakeCurrent(NULL, NULL);

wglDeleteContext(mhRC);

ReleaseDC(mhWnd, mhDC);



출처: http://alleysark.tistory.com/233 [앨리삵]

 

 

 

 

=================================

=================================

=================================

 

 

 

 

출처: https://mail.naver.com/?n=1495154124248&v=f#%7B%22fClass%22%3A%22read%22%2C%22oParameter%22%3A%7B%22charset%22%3A%22%22%2C%22prevNextMail%22%3Atrue%2C%22threadMail%22%3Atrue%2C%22mailSN%22%3A%22136668%22%2C%22previewMode%22%3A2%7D%7D

 

OpenGL 을 MDI 윈도우에서 사용하기...

전체글 글쓴이: sFaster » 2006-07-10 14:56

수고하십니다.

저는 Delphi 에서 OpenGL을 사용하고 있습니다.

아래 코드는 델파이 코드이고, MFC의 다중 윈도우의 View Class라고 생각하시면 됩니다.

코드: 모두 선택

TMyForm = class(TForm) . . . private myOgl : TMyOgl; //<- OpenGL 용 객체. . . end;


와 같은 Form 객체가 있습니다.

Form이 생성이 되면 OpenGL이 해당 Form의 핸들을 받아 초기화 하여 Form 전체에 그리게 됩니다.

그리고 Main-Form에서 이 Form을 동적으로 생성하게끔 만들었습니다.


문제는 Form이 여러개가 만들어지면,

마치 myOgl 객체를 서로 공유하는 것 같은 증상이 납니다.

예를 들어 같은 A 그림을 그리는 Form(이하 A-Form)을 2개 생성할때는 이상이 없지만,

둘 중에 한쪽의 Form 사이즈가 변경이 되면 다른 한쪽의 ViewPort도 틀어집니다.

또한 A-Form끼리는 그나마 괜찮지만, B 그림을 그리는 Form(이하 B-Form)을 생성하면

멀쩡하던 A-Form 마져도 화면 출력을 못하더군요.


이렇게 한 프로그램 안에서 서로 다른 OpenGL 객체를 이용해서 그림을 그릴 수는 없는 것인지요.


부탁드립니다.

 

TMyOgl 의 구조와 동작을 알 수 없으니 정확한 대답은 아니겠습니다만,
일단 랜더링 컨텍스트(RC)는 필요한 윈도마다 독립적으로 생성하세요.

이후 특별히 wglShareLists()를 호출하지 않는 한 서로 따로 노는 게 기본입니다. ^^

ps... 혹시, TMyOgl 내에서 RC가 전역변수는... 아니겠죠? ^^

보통 View가 Create할 때 ::wglMakeCurrent(m_hDC, m_hRC)ㅣ 

한번해주고 Destory될때 ::wglMakeCurrent(NULL,NULL); 해주는데요.

view에서 opengl 함수쓸때마다 
::wglMakeCurrent(m_hDC, m_hRC);
~~~~
렌더링 혹은 뷰포트세팅
~~~~
::wglMakeCurrent(NULL,NULL);

이렇게 코딩해주시면 될겁니다.

 

청키 작성:보통 View가 Create할 때 ::wglMakeCurrent(m_hDC, m_hRC)ㅣ 
한번해주고 Destory될때 ::wglMakeCurrent(NULL,NULL); 해주는데요.

view에서 opengl 함수쓸때마다 
::wglMakeCurrent(m_hDC, m_hRC);
~~~~
렌더링 혹은 뷰포트세팅
~~~~
::wglMakeCurrent(NULL,NULL);

이렇게 코딩해주시면 될겁니다.


은인이십니다...^^


그리고 앞서 말씀해주신 분들께도 감사드립니다.

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://darkblitz.tistory.com/227

 

OpenGL에서 텍스쳐 로딩에 대한 고찰

 

1. gluBuild2DMipmaps vs. glTexImage2D

OpenGL에서 제공하는 gluBuild2DMipmaps는 내부적으로 gluScaleImage를 사용하여 mipmap을 생성한다고 한다. 따라서 glTexImage2D와 달리 입력 영상의 크기에 상관없이 사용이 가능하나, 이 때문에 glTexImage2D에 비해 매우 느리다고 한다.

 

2. OpenGL에서 RC(Rendering Context)는 thread간에 공유가 되지 않는다. 따라서 각 Thread마다 서로 다른 RC를 사용하여야 하고, 여기서 texture나 display list 등의 리소스를 공유하고자 한다면 이를 위해서는 wglShareLists를 사용하여야 한다. 그리고 Multi-thread 환경에서는 각 thread마다 glBindTexture 앞 뒤로 critical section 등을 이용하여 동기화를 해주어야 한다.

 

Reference

http://www.gpgstudy.com/forum/viewtopic.php?t=1165&sid=dd746a3230ee36248ae310530290dfde

http://blog.naver.com/baboneo?Redirect=Log&logNo=80007487701

 

출처: http://darkblitz.tistory.com/227 [긍정적 사고]

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://www.gpgstudy.com/forum/viewtopic.php?t=1165#p3712

 

멀티쓰레드 환경에서 OpenGL ...

전체글 글쓴이: 362496 » 2002-11-23 10:56

안녕들 하십니까...

쓰레드를 하나 생성시켜서 텍스쳐를 로딩하고 그것을 메인쓰레드에서 렌더링하는 프로그램을 만들었는데, (그렇게 한 이유는 디스크

 I/O 가 일어날때 프레임 드랍이 생기는 것을 최대한 막아보려고... 상당히 효과가 있습디다) 문제는 무엇인가 하면... 쓰레드에서 

생성된 텍스쳐는 메인쓰레드에서 사용이 불가능하다... 하는 겁니다. 정확히 말하자면 텍스쳐가 생성이 안됩니다. GetLastError의 값은

170 The requested resource is in use. ERROR_BUSY 

이런 값이 뜹니다.

즉...


unsigned int iTexture; // 텍스쳐 번호

UINT ThreadFunc(LPVOID p) 
{

...

< 텍스쳐를 로딩하고 iTexture 에 그 id를 넣는다 >
< 다른 GL 함수는 사용하지 않음 >

----> iTexture 값이 0 으로 되어버린다는... 

 
...

}



void RenderScene()
{
...

glBindTexture(GL_TEXTURE_2D, iTexture);
// 이 함수가 효과가 없음
// iTexture 의 값이 0 이기 때문에 당연히... 

 

...
}


문제가 뭔지는 알겠는데 해결방법을 생각하려니 난감하기도 하고...
이런 문제로 고민해보신 분들의 의견을 구합니다...

 

제가 멀티스레드는 모르지만
스레드를 만들떄

beginthread를 사용하셧는지 아니면
CreateThread를 사용하셧는지.....


CreateThread는 뭐뭐가 안된다고 beginthread를 쓰라고 하더군요....

잡담이엇습니다 

 

그냥 짐작인데 그래픽 카드가 장면을 렌더링하는 도중에 텍스처를 갱신하려고 한 게 아닐까 싶네요. 동기화를 어떻게 처리하셨는지 

궁금합니다...

그리고 CreateThread와 beginthread 차이는 스레드 안에서 Win32 API 를 호출하느냐 않느냐의 차이라고 알고 있는데 역시 자세하게는 

잘 모르겠구요 -.-

 

CreateThread와 beginthreadex의 차이.

 

c runtime library를 사용할 수 있느냐 없느냐의 차이라는군요.
CreateThread로 생성한 스레드 안에서 c runtime library를 사용하면
메모리가 줄줄 샌다는 소문이.... 있던가? --;; 합니다.
beginthreadex는 c runtime library의 초기화 루틴이 들어가 있다는군요.

참고로 beginthread는 쓰레기라는.. 반드시 beginthreadex를 써야 한다는...

 

일단 모든 문제의 발단은 "렌더링하면서 텍스트를 갱신한다"는데 있는 것 같습니다.

물론 현재 바인드되어 있는 텍스트를 갱신하지는 않습니다. 있지도 않는 텍스트를 바인딩하게 코딩할 리가 있겠습니까...

그리고 beginthread 냐 CreateThread 냐는 별로 중요한 문제처럼 보이지는 않는데요... 텍스쳐로 사용할 데이타는 잘 읽어지거든요...

렌더링을 하면서 백그라운드로 텍스쳐를 읽어오고, 렌더링하는 쓰레드에서는 텍스쳐가 읽어졌는지를 확인해서 읽어져 있는 

텍스트만 바인딩해서 렌더링에 활용하는데... 어디서 문제가 생기는지...

 

GL에 쓰레드 지원을 위한 기능이 없는것이 문제가 아닐까요?
여러 쓰레드에서 한 자원을 사용 할려면 우선 선점한 쓰래드에서 락을 걸어 다른 쓰레드의 진입?을 막는데 GL에선 과정이 따로

 없다는것이 문제 일것 같군요. 에러에 BUSY가 나온거 봐서는 관련 익스텐션이 있을것 같기도 합니다.

없다면 IO쓰레드는 이미지만 메인 메모리에 올려 두었다가 GL쓰레드에서 텍스쳐 로딩/바인딩해서 사용하는것이 좋을것 같네요.

MS GL 소프트웨어버전은 멀티스레드를 지원 안한다고 나와있긴 한데... 가속기는 정확히 모르겠습니다.

 

MSDN을 보면... 모든 GL 함수들은 "rendering context(RC)"에다가 렌더링을 하게 되고 이 RC들은 "calling thread"와 연관되어 있는

 것으로 나와 있거든요...

그래서... makefile 님 의견대로 I/O 쓰레드에서 로딩만 하고, 렌더링하면서 텍스쳐로 로딩/바인딩하게도

 해봤습니다. (안해봤겠습니까... 



그렇게 하면 텍스쳐가 제대로 나오기는 하지만, 프레임 드랍이 생깁니다. 싱글쓰레드로 했을 때보다는 훨씬 낫지만, 그래도 텍스쳐를 

로딩/바인딩 하는 과정이 시간이 상당히 많이 걸리게 됩니다. 아마도 텍스쳐가 커서 그럴겁니다... 2048x2048 ... 
생각할수록 괴물을 만들고 있다는 느낌입니다...

에구 362496 님 그동안 시도해 본 것, 고려해 본 것도 알려주셔야 토론이 불필요하게 뒤로 감기는 일이 없을 것 같네요...

텍스트를 바인딩하게 코딩할 리가 있겠습니까... 
....

(안해봤겠습니까... )

다른 분들이야 알 수 없죠 ^^;;

그렇게 되나요...


자... 다시, 이해를 돕기 위해 사건의 내막을 다시 정리합니다...


지금 작업하는 것은 flight sim. 류의 프로그램이고, 문제가 되는 것은 지형을 로딩하는 부분입니다. 지형 데이타가 워낙 

방대하기 때문에 여러개의 구역으로 쪼갰고요... 시점이 이동함에 따라서 로딩해야 될 구역과 그렇지 않은 구역을 검사합니다. 

그래서 필요한 구역을 로딩하는 것이지요. 이 때 로딩할 때의 I/O 타임 동안 프레임 드랍이 생기는 것을 막기 위해 "검사하고 

로딩하는 I/O 과정"을 별도의 쓰레드를 생성시켜 백그라운드로 돌리게 하는 것입니다. 이렇게요...


(0) 처음 로직...

void ThreadFunc() {
while(bRunning) {
CheckSectors(); // 검사하고 로딩하는 부분...
}
}


void Init() {
...
CreateThread(
...
ThreadFunc,
...
);
...

}

void RenderScene() {
....
RenderSectors(); // 로드된 구역들을 렌더링하는 부분
....

glutSwapBuffers();
}


뭐 대충 이런 식의 로직입니다. 이렇게 하면 I/O가 일어나는 동안에도 프레임 드랍이 거의 안생기는데, 텍스쳐가 안나온다는거지요...


(1) 싱글쓰레드 버전
그래서... 쓰레드를 돌리지 않고...

void Init() {
...
/*
CreateThread(
...
ThreadFunc,
...
);
*/ // 이부분을 막아버리고
...

}

void RenderScene() {
....
CheckSectors(); // 검사하고 로딩하는 부분...
RenderSectors(); // 로드된 구역들을 렌더링하는 부분
....

glutSwapBuffers();
}

이렇게 싱글쓰레드로 하면, 텍스쳐는 잘 나오지만 로딩하는 순간 프레임 드랍이 심각합니다.



(2) 텍스쳐 바인딩 부분을 떼어내면...
그래서 생각해낸 것이, CheckSectors() 함수 속에 들어있던 

glBindTexture(....);
gluBUildMipmap(....);

이런 코드들을 RenderSectors() 로 옮기고, 

로딩이 다 되면 CheckSectors() 함수에서 플래그를 true 로 만든 다음에...
RenderSectors() 함수에서는 이 플래그만 검사해서 true 면 아까 떼어 온

glBindTexture(....);
gluBUildMipmap(....);

을 실행하게 해놨습니다. 이렇게 하면 텍스쳐도 잘나오고 동작도 잘하는데, 역시나 프레임 드랍이 전혀 안생기는 것은

 아니라서 쓸만한 결과를 보여주지는 못합니다.


------------
현재 진도는 여기까지입니다. 좀더 고민해보고, 경과를 올려드리지요...

 

여기서 잠깐 나름대로 정리를 해보면요...

-. 프레임 드랍을 막는다는 것의 구체적인 의미는 텍스처 갱신의 부담을 모든 프레임들로 분산시키는 것을 

말한다( = 스레드를 사용한다고 해서 파일 I/O, 텍스처 업로드 자체가 빨라지는 것은 아니다).

-. 주된 병목은 순서대로 파일 I/O, 밉맵 생성, 텍스처 업로드이다.(물론 구체적인 프로파일링이 필요하겠지만요)

제시하신 0 번은 명시적이고 세밀한 동기화가 없다면 당연히 안 되리라고 보구요.

1 번은 갱신 부담을 분산시키지 못하는 것이므로 역시 비현실적.

현재 2 번이 가장 바람직하나 밉맵 생성을 분산시키지는 못하는 것 같습니다. 텍스처 업로드는 그래픽 카드와의 상호작용이니 

분산이 불가능하구요... (그 상호작용에는 렌더링도 포함되니 어쩔 수 없는 문제입니다).

밉맵 생성은 메모리 할당, 평균 연산 등등 상당히 비싼 과정이라고 알고 있는데, 그걸 스레드에 넣는 쪽으로 해볼만하지 않을까요? 

gluBuildMipmap이 스레드 문제에 걸린다면, 파일에서 읽은 데이터로부터 직접 밉맵들을 생성하고, 업로드에서 glTexImage2D 의 

두 번째 인수를 통해서 밉맵들을 직접 올리는 것도 좋을 듯 합니다. 이 때 각 밉맵을 한 꺼번에 모두 올리지 말고 몇 프레임에 걸쳐서

 올릴 수도 있겠구요.

더 나아가서, 스레드를 쓰지 않고 텍스처 갱신 전과정을 여러 프레임들로 직접 분산시키는 것도 해볼만할 것이라고 봅니다. 

스레드가 편하긴 하겠지만, 처리 과정을 직접 시분할한다면 렌더링과 텍스처 처리 작업에 투여되는 CPU 시간을 좀 더 명시적으로

 제어할 수 있는 장점이 생길 것 같습니다. (A* 길찾기에도 그런 예들이 있구요...)

또 더 나아가서, 비행기의 최근 n 프레임(또는 n 초) 간의 평균 방향을 얻고 다음 섹터 또는 다음 다음 섹터도 미리 읽어두는 

식의 휴리스틱적인 접근도 필요할 것이라고 봅니다...

 

일단 MSDN 이랑 인터넷을 뒤져 알아낸 것은...

GL 함수의 대상이 되는 "rendering context"는 반드시 하나의 쓰레드에서만 "make current" 되어야 한다는 것입니다. 즉, 

렌더링하고 있는 RC를 놔두고 백그라운드로는 다른 어떤 GL 함수도 사용할 수 없다(!) 

는...

뭐 그런 것입니다. 즉 "동기화의 대상"이 I/O나 로딩할 파일, 텍스쳐로 올릴 비트맵 등등 뿐만 아니라 RC 그 자체도 해당된다는

 얘기지요...

대대적인 "갈아엎음" 이 불가피할 것으로 보입니다... 안타깝지만... 


-------------------------


그리고... 광님의 의견...

-. 프레임 드랍을 막는다는 것의 구체적인 의미는 텍스처 갱신의 부담을 모든 프레임들로 분산시키는 것을 말한다( = 스레드를

 사용한다고 해서 파일 I/O, 텍스처 업로드 자체가 빨라지는 것은 아니다). 

-----> 맞습니다. 하지만 파일I/O 자체가 CPU를 먹는다기보다는 I/O가 이루어질 동안 CPU가 놀면서 기다리는 시간이 더 많기

 때문에, 싱글쓰레드로 순차적인 작업이 이루어질 때보다 시간은 확실히 적게 걸립니다. 게다가 타겟머신을 멀티CPU 기계로 

잡고 있기 때문에 더더욱... (빨라지지는 않지만 시간은 적게 걸린다...는 말도안되는 결과가...^^;)



-. 주된 병목은 순서대로 파일 I/O, 밉맵 생성, 텍스처 업로드이다.(물론 구체적인 프로파일링이 필요하겠지만요) 

----> 맞습니다. 저도 구체적인 프로파일링은 안해봤지만, 대충 몇초나 걸리나 세어보면 이 순서대로 되는 것 같습니다.





제시하신 0 번은 명시적이고 세밀한 동기화가 없다면 당연히 안 되리라고 보구요. 

----> 말씀처럼 명시적이고 세밀한 동기화 (섹터단위의 Lock, Unlock)을 다 해놨기 때문에 "쫑나는" 사태는 안납니다. 

유일한 문제는 위에서 써놓은 것처럼 GL 함수가 안먹는다는... RC 자체에 대한 명시적이고 세밀한 동기화가 역시 들어가야

 된다...는 얘기지요...



1 번은 갱신 부담을 분산시키지 못하는 것이므로 역시 비현실적. 

----> 맞습니다. 1번으로 코드를 완성하려 했다면 처음부터 게시판에 썼겠습니까... ^^;




현재 2 번이 가장 바람직하나 밉맵 생성을 분산시키지는 못하는 것 같습니다. 텍스처 업로드는 그래픽 카드와의 상호작용이니 

분산이 불가능하구요... (그 상호작용에는 렌더링도 포함되니 어쩔 수 없는 문제입니다). 

----> 궁극적으로 "텍스쳐 업로드"를 백그라운드로 빼낼 수만 있다면 모든 문제가 해결되는 셈입니다. 관련 FAQ들을 읽어봐도, 

쓰레드들 간의 "업로드된 텍스쳐의 공유"에 관한 문제들이 나오는데, 이것을 구현하는데 문제가 많은 모양이었습니다... 

결국 원하는 것은 이건데... 

 



밉맵 생성은 메모리 할당, 평균 연산 등등 상당히 비싼 과정이라고 알고 있는데, 그걸 스레드에 넣는 쪽으로 해볼만하지 않을까요? 

gluBuildMipmap이 스레드 문제에 걸린다면, 파일에서 읽은 데이터로부터 직접 밉맵들을 생성하고, 업로드에서 glTexImage2D 의

 두 번째 인수를 통해서 밉맵들을 직접 올리는 것도 좋을 듯 합니다. 이 때 각 밉맵을 한 꺼번에 모두 올리지 말고 몇 프레임에 걸쳐서 

올릴 수도 있겠구요. 

----> 그렇다면 밉맵을 생성시키지 않고 싱글 LOD로 텍스쳐를 만든다면 시간이 단축되겠습니까...? 한번 해보고 결과를 알려드리도록 

하지요... 저도 궁금합니다...





더 나아가서, 스레드를 쓰지 않고 텍스처 갱신 전과정을 여러 프레임들로 직접 분산시키는 것도 해볼만할 것이라고 봅니다. 스레드가 

편하긴 하겠지만, 처리 과정을 직접 시분할한다면 렌더링과 텍스처 처리 작업에 투여되는 CPU 시간을 좀 더 명시적으로 제어할 수 

있는 장점이 생길 것 같습니다. (A* 길찾기에도 그런 예들이 있구요...) 

또 더 나아가서, 비행기의 최근 n 프레임(또는 n 초) 간의 평균 방향을 얻고 다음 섹터 또는 다음 다음 섹터도 미리 읽어두는 식의

 휴리스틱적인 접근도 필요할 것이라고 봅니다...

----> 현재는, 비행기가 섹터의 경계를 넘어갈 때 주위 섹터들을 다시 검색해서 필요한 섹터들을 로딩하는 방식이라, 한순간에 I/O가 

몰리면서 프레임이 드랍되는 현상이 발생합니다. 이 과정을 분산하면 상당히 도움이 많이 되겠군요...

--------------------

관심 고맙습니다...

 

님의 글을 보니 제가 하는 것과 거의 똑같은 작업을 하시는 것 같은데요..

위에 언급한 내용에 대해 제가 아는 바에 대해 답글을 달겠습니다. 


1. 밉맵 생성을 다른 쓰레드로 분리하기
"그렇다면 밉맵을 생성시키지 않고 싱글 LOD로 텍스쳐를 만든다면 시간이 단축되겠습니까...? 한번 해보고 결과를 알려드리도록

 하지요... 저도 궁금합니다... 
"
이런 의견을 말씀하셨는데요, 제 생각에는 싱글 LOD 로 텍스처를 만드는 것보다
류광님의 의견대로 하시는게 좀 더 도움이 되리라 생각됩니다. 
그 이유는 밉맵의 사용하는 이점과 더불어, 
밉맵 레벨에서 가장 자세한 레벨의 텍스처의 크기의 약 3배가 되기 때문입니다. ( (1/4)/(1-1/4) )
즉 가장 자세한 레벨의 텍스처를 올리는것이 그 이전에 비해 크다고 생각됩니다. 
따라서, 류광님의 의견대로 gltexImage2 두 번째 인수를 이용하여 필요한 레벨에 해당하는 텍스처를 업로딩하는 것이 

효율적이라는 생각이 듭니다. 
특히 다음과 같은 키워드가 유용하리라 생각됩니다. 
(GL_TEXTURE_MAX_LEVEL_EXT,GL_TEXTURE_BASE_LEVEL_EXT)

2. 지형 및 텍스처 분산 로딩(?)
(섹터라는 말을 제가 정확히 이해하지 못했지만.. )
지형 및 텍스처 자료를 로딩할때 필요한 부분만 읽게하면 
약간 문제가 발생할 수 있습니다. 
제가 경험한 바에 따르면, 저는 지형 및 텍스처 자료를 동서 , 남북 방향에 
평행한 4각형 셀로 나누어 저장하고 이를 로딩하였는데요
비행기가 동서 나 남북 방향에 평행하게 날게 되면.. 
셀 경계에서 한꺼번에 많은 지형 및 텍스처 자료를 읽어 
오는 현상이 발생합니다. 
지형 및 텍스처 자료를 예측하여 로딩한다고 해도 비슷한 현상이 
발생할 수 있기 때문에, 
지형 및 텍스처 자료를 예측해서 로딩한다는 개념보다는 류광님이 말씀하신대로
한 프레임의 일정량만 로딩되게 할 수 있도록 하는 분산로딩의 접근이..
프레임 드라핑(?) 현상을 보다 효과적으로 
막을 수 있는 방안이 아닐까 생각됩니다. 

써넣고 보니.. 그다지 새로운 내용이 없군요.. 
362496 님께 부탁 말씀드리겠습니다. 
멀티 쓰레딩에서 텍스처 공유에 관련된 문서나.. URL을 알려주시면 
감사하겠습니다. 이부분이 정말 저한테 시급한 것인데.. 
OPEN GL의 한계라고 생각하고 넘어간 부분이었거든요.
그러면 많은 도움이 되셨기 바랍니다.

 

동지가 한분 더 늘었군요...


sumis0 님의 의견에 대해...
----------------------------

이런 의견을 말씀하셨는데요, 제 생각에는 싱글 LOD 로 텍스처를 만드는 것보다 류광님의 의견대로 하시는게 좀 더 도움이

 되리라 생각됩니다. 

----> 물론입니다. 밉맵을 사용하면 안할 때보다 좋은 점이 많다는 것은 저도 잘 압니다. 제가 해보고 싶은 것은 "밉맵을 생성하는 

과정이 얼마나 CPU를 먹는가"를 측정(!)하는 것입니다. 그것이 측정이 되어야 어떻게 분산시킬지 판단이 서거든요... 밉맵 없이

 프로그램을 개발하려는 것은 아니랍니다...
지금 작업해놓은 코드에서 변수값 한개만 바꾸면 밉맵 없이 텍스쳐가 로딩되게 해놨기 때문에 간단히 측정해볼 수 있거든요...



섹터라는 말을 제가 정확히 이해하지 못했지만.. 
...
저는 지형 및 텍스처 자료를 동서 , 남북 방향에 
평행한 4각형 셀로 나누어 저장하고 이를 로딩하였는데요 

---> 제가 사용했던 구역, 또는 "섹터"의 개념은, 님께서 표현했던 "셀"과 정확히 일치합니다. "sector"의 사전적 의미 "부채꼴"과는

 별 상관없이 그냥 "구역"의 의미로 사용되었습니다. SF소설 따위에서 그런 예가 있는데요... 어감이 나쁘지 않아서 그냥 썼습니다. 

혼란을 끼쳐드렸다면 죄송하고요...




멀티 쓰레딩에서 텍스처 공유에 관련된 문서나.. URL을 알려주시면 
감사하겠습니다. 이부분이 정말 저한테 시급한 것인데.. 
OPEN GL의 한계라고 생각하고 넘어간 부분이었거든요

---> 여기저기 들쑤시고 다녀서 URL 기록해놓는 것도 잊어버렸습니다. 의외로 자료가 많지 않습니다. 어차피 몇번은 더 찾아야 

할테니 읽어볼만한 자료는 모아서 공유하도록 하지요...


------------------
관심 고맙습니다...

 

우선 texture bind만 얼마나 드롭이 생기는지 테스트 해보셔야 할것 같네요. 예전에 voodoo,riva tnt쓸때 테스트 

해본것으로는 256*256*32bit 텍스쳐 한장을 bind 자체만으로도(텍스쳐를 비디오메모리에 올릴때) 상당한 부하가 걸렸습니다. 

프래임이 1/5로 줄었던가 그랬죠. 요즘은 AGP4/8이니 조금 빨라졌겠지만 그래도 부하는 클 겁니다.

크기가 줄면 줄수록 프래임 드롭이 적어 질테니 적정수준의 크기부터 구해봐야겠죠. 하지만 너무 작아지면 어쩔수 없이 프래임

 드롭을 감수할수 밖에 없겠죠. 



그리고 밉맵생성은 따로 구현(아니면 GL클론 MESA의 것을...)해서 GL쓰레드에서 직접 바인딩하시는것이 좋겠습니다.

이건 해당사항이 아닌것 같지만 만약 비디오 메모리에 텍스쳐에 할당된양보다 많은 텍스쳐를 사용하신다면 계기 및 GUI등 

교체되지 않고 매 프래임 계속 출력되는 텍스쳐들의 우선순위를 높혀서 swap안되게 만들어 swap양을 줄이면 이득이 있을겁니다.

 

IO_n_MipMap_Thread() { 메인메모리에 밉맵 텍스쳐까지 생성 } GL_Render_Thread() { 렌더링.. SwapBuffer() if(새 텍스쳐?) { 텍스쳐 바인딩.. // 벤치결과 적정수준의 양만.. 넘치는건 다음 프래임에... glFlush() } 프래임 리미터 } Physics_Thread() { // 고정 프래임 }

제가 아는 한도내에선 이정도 밖에 안나오는군요. 

 

저도 이 문제점 지금 봉착되었는데
정보 좀 얻을 수 있을까요?

URL 이라든가 좀...

구글에서 검색해 봐도 영어가 딸려서 잘 모르겠더라구요.

 

MS의 OpenGL 구현에서는, 모든 GL 함수의 호출은 RC (rendering context)를 기반으로 이루어집니다. D3D 에서의 렌더링 관련 

함수 호출이 IDirect3DDevice... 인터페이스를 기반으로 이루어지는 것과 비슷합니다. 문제는 RC 가 쓰레드간 공유가 되지 않는 

데 있습니다. 그래서 쓰레드마다 RC를 만들고 이들이 리스트 공간(텍스쳐 번호, 디스플레이 리스트 등)을 공유할 수

 있게 wglShareLists 함수를 불러줍니다.
그러고 나서는 위에 있는 것처럼 (그새 세월이 좀 지났군요) 작업합니다.

그리고, 각 쓰레드(와 그에 따른 RC)간 동기화를 맞춰주기 위해서 각 쓰레드마다 glBindTexture 양끝에 크리티컬 섹션을 

넣어서 동시에 바인드가 되지 않게 합니다. 물론 갱신 중인 텍스쳐를 렌더링용으로 사용하지 말아야 합니다. 이건 텍스쳐 

갱신 쓰레드를 잘 구성하면 별도의 동기화개체 없이도 간단히 해결됩니다.

대충 이런 식입니다. 생각나는 대로 적어서 제대로 동작할 지는 모르겠는데, 전체적인 감을 잡는 정도로만 보시기 바랍니다.



// 전역변수
HGLRC hRC1, hRC2;
CRITICAL_SECTION cs;

// 바인드 텍스쳐 함수를 멀티쓰레드용으로 새로 만듬
void BindTexture(unsigned int iTex)
{
EnterCriticalSection(&cs);
glBIndTexture(GL_TEXTURE_2D, iTex);
LeaveCriticalSection(&cs);
}



// 텍스쳐 갱신 쓰레드
wglMakeCurrent(hdc, hRC2); // 텍스쳐 갱신 쓰레드는 RC2 를 사용

if( WeNeedTexture ) {
unsigned int iTex;
glGenTextures(1, &iTex);
BindTexture(iTex);
//그림을 로딩해서 텍스쳐로 만든다
BindTexture(0);
// 후처리: 텍스쳐를 별도의 전역변수, 리스트, 테이블 등등에 저장하여 렌더링에 쓸 수 있도록 한다
}

// 메인 쓰레드
// 초기화
hRC1 = wglCreateContext(hdc);
hRC2 = wglCreateContext(hdc);
wglShareLists(hRC1, hRC2); // RC 들이 리스트 공간을 공유하게 한다
wglMakeCurrent(hdc, hRC1); // 렌더링 쓰레드는 RC1 을 사용

CreateThread(...); // 텍스쳐 갱신 쓰레드를 생성한다.

// 렌더링 루프
...
if( WeNeedRender ) {
unsigned int iTex = <전역변수 리스트 테이블 등등에서 해당 텍스쳐 번호를 가져온다>;
BindTexture(iTex);
// 폴리곤 렌더링
BindTexture(0); // 다그리고 나면 unbind 를 해준다
}
...

 

저도 OpenGL로 공부를 시작해서 그런지 이 쓰레드에 관심이 가는데........

다이렉트X는 렌더링하면서 , 택스쳐를 업로딩하는게 가능한건가요?

 

D3DCREATE_MULTITHREADED 옵션을 주면 별도의 쓰레드에서 텍스쳐를 로딩하는 것이 가능합니다.

물론 현재 반쯤 로딩 중인 텍스쳐를 렌더링에 사용할 수는 없습니다.

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_QnA_etc&no=3074

 

window 버젼으로

opengl의 Freeglut을 이용하여 게임을 제작 중인데,

로딩하는 파트에서 CEGUI엔진에 관련된 리소스(scheme)들을 읽어 들이는 부분을 멀티스레딩으로 처리 하려 했는데,

scheme xml을 읽어드리는 부분에서 opengl의 렌더링 컨텍스트가 필요한 부분이 있는듯 하더군요,,

그래서 찾아보니 WGL 관련 API를 호출하여야 해결되는 문제인데,

쓰레드를 하나만 만들어서 처리했을때는 문제가 없었긴 했는데,

정말 다중으로 쓰레드를 돌리려고 하니 wgl 함수에서 문제가 생기더군요..

멀티 쓰레딩환경에서 렌더링 컨텍스트 이슈를 다뤄본적이 있으신 분들이 계시다면 저에게 알려주셨으면 좋겠습니다 ㅠ

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

직접 해보진 않았고요. 대신 OpenGL에서 멀티 쓰레드를 어떻게 사용하나 궁금해서 찾아본 링크를 알려드릴께요.

http://higherorderfun.com/blog/2011/05/26/multi-thread-opengl-texture-loading/


위 링크에 보면 멀티 쓰레드로 텍스쳐를 로딩하는데요.


우선 OpenGL 컨텍스트를 두개(mainContext, loaderContext) 만들고요.


wglShareLists를 사용해서 두 컨텍스트가 리소스를 공유할 수 있게 해줍니다(메인 컨텍스트로 뭔가하기 전에 호출!) .


해당 쓰레드에서 wglMakeCurrent로 사용할 컨텍스트를 지정해주고 원하는 작업을 한뒤 wglMakeCurrent, wglDeleteContext)를 호출하면 된다고 하네요.


구체적으로 어떤 문제가 발생하나요?
2014-06-24
12:48:25
 
    일단 저기 까지는 저도 어떻게 어떻게 해결은 되었어요저도 WGL 함수를 호출하여 메인 쓰레드외 1개의 쓰레드에서 작업하는 것은 문제가 없었습니다.


thread 함수 전문
{
wglMakeCurrent(hdc, gldc);
SchemeManager::getSingletonPtr()->createFromFile("HUDDemo.scheme");
SchemeManager::getSingletonPtr()->createFromFile("OgreTray.scheme");
SchemeManager::getSingletonPtr()->createFromFile("GameMenu.scheme");
SchemeManager::getSingletonPtr()->createFromFile("SampleBrowser.scheme");
SchemeManager::getSingletonPtr()->createFromFile("Alfiskoskin.scheme");
SchemeManager::getSingletonPtr()->createFromFile("Generic.scheme");
SchemeManager::getSingletonPtr()->createFromFile("TaharezLook.scheme");
SchemeManager::getSingletonPtr()->createFromFile("VanillaSkin.scheme");
SchemeManager::getSingletonPtr()->createFromFile("WindowsLook.scheme");
SchemeManager::getSingletonPtr()->createFromFile("VanillaCommonDialogs.scheme");


FontManager::getSingletonPtr()->createFromFile("DejaVuSans-12.font");
FontManager::getSingletonPtr()->createFromFile("Jura-13.font");
FontManager::getSingletonPtr()->createFromFile("mizufalp-12.font");
wglMakeCurrent(hdc, nullptr);
wglDeleteContext(gldc);
}


createFromFile하나의 함수당 한개의 쓰레드 단위로 나누어 실행하려 했더니


wglMakeCurrent(hdc, hglrc); 에서 에러를 뿜더라고요.
여기서 HGLRC 변수는 thread 만큼 나누어 주었고, wglsharelists로 메인 쓰레드의 HGLRC와 공유 시켜 주었습니다.
2014-06-24
16:44:49
 
    궁금해서 인터넷 찾아보니 OpenGL 최대 컨텍스트가 제한이 있더라구요. ssapo님 방식으로 파일 마다 컨텍스트를 만들면 꽤 많이 만들어 지겠네요.


혹시 쓰레드 갯수 만큼 wglCreateContext로 컨텍스트를 생성할 때 에러 안나나요(또는 리턴값 체크하셨나요?)  wglMakeCurrent할 때 나오는 에러는 잘못 된 hglrc로 해서 그런게 아닌가 싶어서요.

아래 프로그램 다운 받아서 실행해 보면 그래픽카드 벤더별로 최대 몇개까지 OpenGL 컨텍스트를 생성할 수 있는지 테스트 할 수 있는데요.


http://www.luki.webzdarma.cz/up/MaxGLContexts.zip


저는 AMD Radeon HD 7800 Series로 111개 까지 컨텍스트를 만들 수 있었어요.


참고
http://stackoverflow.com/questions/4951370/is-there-a-limit-to-how-many-opengl-rendering-contexts-you-can-create-simultaneo



2014-06-24
21:54:51
 
    새로운// 크 급한 마음에 ,,, wglCreateContext를 체크해보지 않았어요.. 아둔했네요 그러면 안되는건데,, 그리고 렌더링 컨텍스트가 제한이 있다는것도 몰랐네요 감사합니다. 조만간에 해보고 결과 알려드릴게요 2014-06-24
22:56:53

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://higherorderfun.com/blog/2011/05/26/multi-thread-opengl-texture-loading/

 

Multi-thread OpenGL texture loading

Those who have used OpenGL are probably aware that you can only invoke OpenGL procedures for a given context on the thread that currently owns it – usually, the main thread. This leads many programmers to assume that OpenGL is strictly single-threaded, and that no loading can be done on the background, inside some loader thread. This is not the actual case, and it’s actually fairly simple to work around this.

The key to multi-threaded OpenGL is in creating multiple contexts that share data between them. On Windows, it is done with the wglShareLists() procedure. On X and Apple APIs, it can be done during context creation (glXCreateContext() and aglCreateContext(), respectively). It boils down to creating a second context for the loader thread, and setting it to share data with the main context. This way, all data loaded on the second context will be available on the main one – you can just create a texture normally on the loader thread, and then glBindTexture() it on the main thread, for example.

On Windows/C++0x, the code looks like this:

void startLoader(HWND hwnd)
{
    // Assuming that this is the main thread
    HDC hdc = GetDC(hwnd);
    HGLRC mainContext = wglGetCurrentContext();
    HGLRC loaderContext = wglCreateContext(hdc);
    wglShareLists(loaderContext, mainContext); // Order matters
 
    boost::thread([=] () {
        wglMakeCurrent(hdc, loaderContext);
        runLoader();    // Your method for loading textures
        wglMakeCurrent(nullptr, nullptr);
        wglDeleteContext(loaderContext);
    });
}

Most APIs (such as Allegro, SDL, wxWidgets, etc) will provide you with a simple method of retrieving the window handle, which is all that you require to call the above procedure.

Note that you could create the context and share the lists inside the loader thread, but wglShareLists() must be called BEFORE anything is done on the main context, so the safest way is to do it on the main thread (otherwise, the new thread could take a while to run and be too late to do it).

IMPORTANT! I have observed that, on some cases (Windows 7 64, NVIDIA 320M), attempting to use a texture after it has been created (via glGenTextures()) but before its data finished uploaded (in this case, via glTexSubImage2D()) resulted in the texture being corrupted, and remaining corrupted even after it was uploaded. This will happen even if you wait for glTexSubImage2D() to return before using it on the main thread, since OpenGL is asynchronous. To avoid this problem, make sure that you glFinish() the loader thread before you attempt to use any textures initialized there.

 

 

 

=================================

=================================

=================================

 

 

출처: http://m.blog.daum.net/junek69/2

 

Windows OpenGL multi-thread 환경에서 texture 공유하기

1. Thread 별로 Rendering Context를 Thread가 직접 생성해야 한다. (다른 Thread가 생성해주면 안됨.)

 

Windows에서 OpenGL 사용시 Rendering Context 가 Thread별로 생성이 되어야 한다.

HGLRC hrc = wglCreateContext(hdc);

이렇게 생성된 Rendering Context 안에는 생성되어 바인딩된 Texture 정보 등도  들어 있고 

기본적으로 Thread별로 공유되지 않는다.

 

2. Thread 간에 Rendering Context를 Share할 수 있으나 Clean상태에서만 Share될 수 있고, 성능이 좋지 못함.

wglShareLists() API를 통해서 Thread별로 생성된 Rendering Context 를 Share할 수 있으나 

엄청 느리고, 또다른 문제점으로 Rendering Context 생성 후에 어떠한 OpenGL  명령어로

수행되지 않은 Clean한 상태에서만 Share될 수 있다.

wglShareLists(srcHrc, dstHrc);

 

3. Thread간에 Texture를 공유하기 위한 편법

wglShareLists() 보다 빠른 방법으로 Texture를 공유할 수 있는 편법은

 GL_AUX0 와 같은 눈에 보이지 않는 frame buffer에 다가 texture를 그리고 

이를 다른 thread가 frame buffer에서 texture로 만들어 가지면 된다.

 

Thread 1 에서 

glDrawBuffer(GL_AUX0);

// drawing texture ...

 

Thread 2 에서

glReadBuffer(GL_AUX0);

glBindTexture(GL_TEXTURE_2D, texId);

if (최초생성?) {  // texId 최초 생성시 

     glCopyTexImage2D(GL_TEXTURE_2D, 0, 4, 0, 0, width, height, 0);

}

else { // texId  갱신 시

      glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, y, x, y, w, h); // 기존 texture에서 (x,y) 시작으로 (w,h) 일부만 갱신

}

 

P.S.

GL_AUX0 가  obsolete해져 최신의 경우 지원되지 않는 경우가 많다고 한다. ㅠㅠ

이후 pbuffer가 이용되다 최근에는 FBO(framebuffer object)를 이용한 기법들이 사용되고 있다 한다.

하지만 안타깝게 FBO(framebuffer object)의 경우 thread간에 공유가 되지 않는다고 하니,

위에서 같이 GL_AUX0 와 같은 방식으로 지원되지 못하겠다.

역시 구조적으로 Rendering은 하나의 thread가 맡는 것이 정답이니 이미 작성된 legacy에서 불가능한 상황은 어찌해야할지...

 

 

=================================

=================================

=================================

 

 

반응형


관련글 더보기

댓글 영역