Sign in to follow this  

D3D11 Rendering in other thread, window in Main thread

This topic is 1449 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have an issue I want to solve:

 

When I move/drag/resize a window, my message pump get stuck and window content is not updated (well known problem with DefWIndowProc). I want to create another thread and make rendering there, but I don`t know how to correctly do that, because if I start rendering loop in other thread, messages seems not to reach window or something like that.

 

Here is some code. I use C++/CLI mixed mode and WPF window:

 

This is main. Here I start my engine



[STAThreadAttribute]
int main()
{
	try
	{

		ManagedEngine^ managed = gcnew ManagedEngine();
		managed->InitializeSystem();
		ThreadStart^ threadDelegate = gcnew ThreadStart(managed, &ManagedEngine::StartEngine);
		Thread^ newThread = gcnew Thread(threadDelegate);
		newThread->Start();
		//managed->StartEngine();
		managed->Stop();

	}
	catch (Exception^ exception)
	{
		Console::WriteLine(exception->Message + exception->StackTrace + exception->TargetSite);
	}
}

In initializeSystem() I do initialization of D3D, Camera, DirectInput

 

Rendering thread is in method Run()



void Engine::Run()
{
	MSG msg;
	bool done, result;

	//////?????????????? ????????? MSG
	ZeroMemory(&msg, sizeof(MSG));

	//??????? ????, ??????? ????? ???????? ?? ??? ??? ???? ?? ???????
	//????????? ?????????? ?????? ?? ???? ??? ?? ????????????
	done = false;
	int counter = 0;
	while(!done)
	{
		Console::WriteLine("????? ? ???? ??????????");
		//???????????? ????????? Windows
		if (PeekMessage(&msg, NULL, 0, 0,PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		
		//???? ?????? ????????? ??????? ??????????, ????? ???????
		if (msg.message == WM_QUIT)
		{
			done = true;
		}
		else
		{
			//????? ???????????? ????
			result = Frame();
			Console::WriteLine(counter);
			if (!result)
			{
				done = true;
			}
			counter++;
		}
	}
}

I did my engine structure almost exactly as in rastertek lessons if you see them.

 

So, As I understand the main my problem is that I have message pump run in another thread, but window is main thread, so messages cant be read. That lead window to get stuck in that time when rendering goes well.

 

Now I want to understand how to organize thread to leave message pump outside (in main thread), but listen this messages inside second thread.

Correct me if I'm wrong, please. I need your help!

Edited by BlackJoker

Share this post


Link to post
Share on other sites

Yes. Try doing a normal loop on the main-thread with GetMessage instead of PeekMessage, and then on the other thread just something like:

while(true) {
  render();
  swapChain->present();
}

And then some synchronization to stop the rendering on exit and maybe sleep on alt-tab and such.

Share this post


Link to post
Share on other sites

So, I need to make 2 loops?

 

One in main thread:

while(!done)
	{
		Console::WriteLine("????? ? ???? ??????????");
		//???????????? ????????? Windows
		if (Getssage(&msg, NULL, 0, 0,PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		
		//???? ?????? ????????? ??????? ??????????, ????? ???????
		if (msg.message == WM_QUIT)
		{
			done = true;
		}
		
	}

Second in other thread:

while (true)
{
    Frame();
}

Am I right?

Share this post


Link to post
Share on other sites

Yes, but GetMessage works a bit differently. It only returns when there is a message available, and returns 0 on WM_QUIT.

So:

while(!done) {
	MSG msg;
	BOOL bRet = GetMessage(&msg, NULL, 0, 0);
	if(bRet > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	else {
		done = true;
	}
}

Share this post


Link to post
Share on other sites

Erik Rufelt

 

 

Thanks, It works, But I have now another problem:

I implement resizing swapchain method and now when i resize window, my rendering crash in BeginScene method in line:

d3d11_DeviceContext->ClearDepthStencilView(d3d11_DepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);



void D3D11::BeginScene(float red, float green, float blue, float alpha)
{
	
		//Console::WriteLine("windowResizing = false");
		float color[4];

		//????????????? ?????, ????? ???????? ?????
		color[0] = red;
		color[1] = green;
		color[2] = blue;
		color[3] = alpha;

		//??????? ?????? ?????
		d3d11_DeviceContext->ClearRenderTargetView(d3d11_RenderTargetView, color);

		//??????? ????? ???????
		d3d11_DeviceContext->ClearDepthStencilView(d3d11_DepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
	
}

And this is my method which I call when window resizing:



void D3D11::ResizeSwapChain()
{
	//DeinitializeD2D11();
	HRESULT result;
	if (swapChain)
	{
		RECT rc;
		GetClientRect(hwnd, &rc);
		UINT width = rc.right - rc.left;
		UINT height = rc.bottom - rc.top;

	d3d11_DeviceContext->OMSetRenderTargets(0,0,0);
	d3d11_RenderTargetView->Release();
	
	swapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
	swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
	d3d11_Device->CreateRenderTargetView(backBufferPtr, NULL, &d3d11_RenderTargetView);


	//?????????????? ???????? ????????? Depth_Buffer
	ZeroMemory(&depthBufferDescription, sizeof(depthBufferDescription));

	//????????? ???????? ?????????
	depthBufferDescription.Width = width;
	depthBufferDescription.Height = height;
	depthBufferDescription.MipLevels = 1;
	depthBufferDescription.ArraySize = 1;
	depthBufferDescription.Format = DXGI_FORMAT_D32_FLOAT;
	depthBufferDescription.SampleDesc.Count = 4;
	depthBufferDescription.SampleDesc.Quality = 0;
	depthBufferDescription.Usage = D3D11_USAGE_DEFAULT;
	depthBufferDescription.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthBufferDescription.CPUAccessFlags = 0;
	depthBufferDescription.MiscFlags = 0;

	d3d11_DepthStencilBuffer->Release();
	d3d11_DepthStencilBuffer = NULL;

	//??????? ???????? ??? ?????? ??????? ????????? ??????????? ?????????
	result = d3d11_Device->CreateTexture2D(&depthBufferDescription, NULL, &d3d11_DepthStencilBuffer);

	//?????? ????????? Depth Stencil State
	d3d11_DeviceContext->OMSetDepthStencilState(d3d11_DepthStencilState, 1);

	//?????????????? ????????? Depth Stencil View
	ZeroMemory (&depthStencilViewDescription, sizeof(depthStencilViewDescription));

	//????????? ???????? ????????? Depth_Stencil_View
	depthStencilViewDescription.Format = DXGI_FORMAT_D32_FLOAT;
	depthStencilViewDescription.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
	depthStencilViewDescription.Texture2D.MipSlice = 0;

	d3d11_DepthStencilView->Release();
	d3d11_DepthStencilView = NULL;

	//?????? ???????? ????????? Depth_Stencil_View
	result = d3d11_Device->CreateDepthStencilView(d3d11_DepthStencilBuffer, &depthStencilViewDescription, &d3d11_DepthStencilView);

	//??????????? ????????? ?? ?????? ?????, ??? ??? ?? ?????? ?? ?????
	backBufferPtr->Release();
	backBufferPtr = 0;
	
	d3d11_DeviceContext->OMSetRenderTargets(1, &d3d11_RenderTargetView, d3d11_DepthStencilView);
	
	//d3d11_DeviceContext->OMSetRenderTargets(1, &d3d11_RenderTargetView, NULL);
	
	//????????????? ??????? ??? ??????????
	D3D11_VIEWPORT viewPort;

	viewPort.Width = (float)width;
	viewPort.Height = (float)height;
	viewPort.MinDepth = 0.0f;
	viewPort.MaxDepth = 1.0f;
	viewPort.TopLeftX = 0.0f;
	viewPort.TopLeftY = 0.0f;

	//??????? ViewPort
	d3d11_DeviceContext->RSSetViewports(1,&viewPort);

	//InitializeD2D11();
	}
}

I found out that app crash because of resizing window, but I have no idea why. Before it works fine.

Edited by BlackJoker

Share this post


Link to post
Share on other sites

Do you resize on the main thread when you get a resize message?

That won't work as the render thread might be busy with the back-buffer. You should atomically set width and height variables, and then at the beginning of every frame on the render thread check if the swap-chain needs to be resized, or similar. You shouldn't touch the same device-context on multiple threads simultaneously.

Share this post


Link to post
Share on other sites

@Erik Rufelt

But one more question regarding this theme:

 

How I can get rid of that blinking effect when I resizing the window? It becomes white for a few milliseconds and then painted properly again.

Is it possible to make background not became white even on those milliseconds?

Share this post


Link to post
Share on other sites

How do you create your window and window-class?

My guess is you set a white brush to the window-class hbrBackground, and DefWindowProc uses it to erase the window. If set to NULL, then the window will not be erased. You can also handle WM_ERASEBKGND and return non-zero from your WndProc.

 

If you use CS_HREDRAW or CS_VREDRAW for your window-class I would also remove those, as those tell Windows to redraw the window when resized.

Edited by Erik Rufelt

Share this post


Link to post
Share on other sites

Not sure, I haven't used it. Try setting the background to transparent if possible.

I Googled it and many people have the problem. Seems like you can override hbrBackground by using SetClassLong, or use D3DImage to do your drawing. Not sure how it plays with multi-threading. I would create a new topic asking about WPF DirectX flickering issue specifically, and maybe someone knows how to solve it the best way.

Share this post


Link to post
Share on other sites

This topic is 1449 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this