Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drawing the Imgui Windows in Thread A but rendering them in Thread B #5109

Closed
bulllit opened this issue Mar 14, 2022 · 3 comments
Closed

Drawing the Imgui Windows in Thread A but rendering them in Thread B #5109

bulllit opened this issue Mar 14, 2022 · 3 comments

Comments

@bulllit
Copy link

bulllit commented Mar 14, 2022

Version: Dear ImGui 1.87
Backend Platform imgui_impl_win32
Backend Renderer imgui_impl_dx111
Operating System: Windows 10
Compiler Msvc 14.2 c++17

Hello I am currently writing an overlay for a game that uses a main Gamethread and a separate Renderthread. In my case, I am deploying hooks in both the game's mainloop and the directxpresent . The reason I do this is because the game's mainthread has alot of vtable functions that access the gamethreads TLS, which makes it very inconvenient to call those functions from the renderthread.

Ideally, my code would look like this:

//mainthread hook
void __fastcall hkGameLoop(int a1, int a2)
{
	if (imguiinit && g_ImGuiResourceMutex.try_lock()) { 
		ImGui::SetCurrentContext(g_imContext);
		ImGui_ImplDX11_NewFrame();
		ImGui_ImplWin32_NewFrame();
		ImGui::NewFrame();
		RenderUI::drawMenus();
		ImGui::Render();
		g_ImGuiResourceMutex.unlock();
	}
	origGameLoop(a1,a2);
}

//present hook
HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags)
{
	if (!dxhookinit)
	{
		if (SUCCEEDED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&pDevice)))
		{
			pDevice->GetImmediateContext(&pContext);
			DXGI_SWAP_CHAIN_DESC sd;
			pSwapChain->GetDesc(&sd);
			window = sd.OutputWindow;
			ID3D11Texture2D* pBackBuffer;
			pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
			pDevice->CreateRenderTargetView(pBackBuffer, NULL, &mainRenderTargetView);
			pBackBuffer->Release();
			InitImGui();
			RenderUI::init();
			dxhookinit = true;
		}
		else return oPresent(pSwapChain, SyncInterval, Flags);
       }

	std::shared_lock< std::shared_mutex > lock(g_ImGuiResourceMutex);
	
	ImGui::SetCurrentContext(g_imContext);
	pContext->OMSetRenderTargets(1, &mainRenderTargetView, NULL);
	ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
	lock.unlock();
	return oirgPresent(pSwapChain, SyncInterval, Flags);
}

Well this code crashes when calling ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());. Looking at the debugger, the drawdata seems valid and it crashes when doing things with the backenddata because of Segfault ACCESS_VIOLATION. I assume the backend data gets somehow invalidated during this process.

The way I understand ImGui::Render() is that it it wraps everything up, ends the frame and compiles Everything into an internal ImDrawData struct.

At this point ive also tried storing just the Drawdata in the game thread and adding them into an empty Frame on the renderthread using

        ImGui_ImplDX11_NewFrame();
	ImGui_ImplWin32_NewFrame();
	ImGui::NewFrame();
	ImGui::Render();
	ImDrawData* d = &g_drawdataBuffer;
	ImDrawList** l = new ImDrawList* [d->CmdListsCount];
	for (int i = 0; i < d->CmdListsCount; i++) {
		l[i] = d->CmdLists[i]->CloneOutput();
	}
	ImGui::GetDrawData()->CmdLists = l;
	ImGui::GetDrawData()->CmdListsCount = d->CmdListsCount;
	pContext->OMSetRenderTargets(1, &mainRenderTargetView, NULL);
        ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

This renders what I've drawn in the gameloop but is completely unresponsive to interactions (window collision).
At this point my question would be what is the best approach to do that?

@ocornut
Copy link
Owner

ocornut commented Mar 15, 2022

You should deep clone the ImDrawData after calling ImGui::Render() and then pass that to your render thread/function. Not sure what else you need to know.

Your last snippet somehow does a clone from the wrong source and then proceed to overwrite the original source that's a little strange and backward.

@ocornut
Copy link
Owner

ocornut commented Mar 15, 2022

Your last snippet somehow does a clone from the wrong source and then proceed to overwrite the original source that's a little strange and backward.

I'll close it as it looks like your ImDrawData cloning code is simply wrong. It works if you write the code correctly.

(Btw if you keep clone every frame as a second step you may want to keep a double buffered version of ImDrawData to avoid reallocating buffers.)

@bulllit
Copy link
Author

bulllit commented Mar 18, 2022

Yes you were right, at that time i got confused about what went wrong so i started trying out stuff that made no sense whatsoever.. But basically my fault was to initialize Imgui in the renderthread. All what i had to do was to basically move that part to the gamethread and everything worked like a charm.
There was no need to clone anything whatsoever either.

ocornut added a commit that referenced this issue Jul 12, 2023
…DrawData itself. Faclitate user-manipulation of the array (#6406, #4879, #1878) + deep swap. (#6597, #6475, #6167, #5776, #5109, #4763, #3515, #1860)

+ Metrics: avoid misleadingly iterating all layers of DrawDataBuilder as everything is flattened into Layers[0] at this point.

# Conflicts:
#	imgui.cpp
#	imgui_internal.h
ocornut added a commit that referenced this issue Aug 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants