ahmed605 / imgui-uwp

UWP backend for Dear ImGui
20 stars 1 forks source link

xbox game bar? #1

Closed Annihil closed 7 months ago

Annihil commented 7 months ago

Would you be able to setup ImGui in an xbox game bar widget (c++17/CX)? https://github.com/microsoft/XboxGameBarSamples/tree/master/Samples/WidgetSampleCX That would be awesome

ahmed605 commented 7 months ago

I don't have much experience with Xbox Game Bar SDK but I don't see why it wouldn't work

Annihil commented 7 months ago

The problem is that it is Win2D, and we can only get a Microsoft::Graphics::Canvas::CanvasSwapChain^ which does not seem to be convertible to a DXGISwapChain*.

canvasSwapChainPanel->SwapChain = ref new CanvasSwapChain(CanvasDevice::GetSharedDevice(), Width, Height, displayInformation->LogicalDpi);

We cannot call CoreWindow::GetForCurrentThread() either, so cannot do https://github.com/ahmed605/imgui-uwp/blob/6e131e8de2d13f1824b2e0f32d3382f4c69ec4cd/examples/example_uwp_directx12/main.cpp#L315 to create a swap chain. Because of that I'm unable to run your example in it. Would you know how to obtain a DXGISwapChain in Win2D?

ahmed605 commented 7 months ago

CanvasSwapChain can be converted to IDXGISwapChain1, check Win2D Direct2D interop docs.

but why are you using XAML? this backend doesn't support being hosted in XAML yet, it would work but pretty sure input will be broken, proper XAML support is planned tho but in the meantime create a CoreApplication app instead, like shown in samples

also why CoreWindow::GetForCurrentThread() cannot be called? it should work fine in Xbox Game Bar widgets

Annihil commented 7 months ago

CanvasSwapChain can be converted to IDXGISwapChain1, check Win2D Direct2D interop docs.

Thanks for that, will give it another go!

but why are you using XAML? this backend doesn't support being hosted in XAML yet, it would work but pretty sure input will be broken, proper XAML support is planned tho but in the meantime create a CoreApplication app instead, like shown in samples

Because I believe it's the only available option in an xbox game bar widget. Yes input won't work, I just want to be able to draw things.

ahmed605 commented 7 months ago

Because I believe it's the only available option in an xbox game bar widget.

afaik Xbox Game Bar Widgets can be CoreApplication apps too, will give it a try in the future and will upload a sample of that here if I succeeded

Annihil commented 7 months ago

Because I believe it's the only available option in an xbox game bar widget.

afaik Xbox Game Bar Widgets can be CoreApplication apps too, will give it a try in the future and will upload a sample of that here if I succeeded

Ah alright, going to check this out too. Amazing, thanks!

Annihil commented 7 months ago

You were right, CoreWindow::GetForCurrentThread() works, it's the same as Window::Current->CoreWindow, but it returns a Windows::UI::Core::CoreWindow^ and

const auto window = CoreWindow::GetForCurrentThread();
// ...
dxgiFactory->CreateSwapChainForCoreWindow(
    m_pd3dCommandQueue.get(),
    reinterpret_cast<IUnknown*>(window),
    &swapChainDesc,
    nullptr,
    swapChain1.put()
);

fails 🫤 It seems impossible to create a 3d swapchain in an xgb widget 😒

ahmed605 commented 7 months ago

what's the HRESULT this returns?

btw note that the window needs to be shown for this to work

Annihil commented 7 months ago

what's the HRESULT this returns?

E_ACCESSDENIED

btw note that the window needs to be shown for this to work

Yes the window is visible at the time I call it.

I suspect the sandbox forbids calling CreateSwapChainForCoreWindow.

ahmed605 commented 7 months ago

the sandbox shouldn't be a problem as this function works fine for the samples which are sandboxed too

a rough guess but the compiler might be picking CppWinRT's IUnknown instead of raw one which wouldn't work with CX objects, so can you try using ::IUnknown instead of IUnknown for that cast?

Hopefully this is the case, if not I will try to investigate further as soon as I get the time to

DirectX Debug Layer might also help investigating the problem

ahmed605 commented 7 months ago

btw does your app initialize XAML? because if so then that's why, XAML would be already using the CoreWindow so creation of a swapchain over that CoreWindow would fail

Annihil commented 7 months ago

the sandbox shouldn't be a problem as this function works fine for the samples which are sandboxed too

Ah alright, well I don't know what's happening then.

a rough guess but the compiler might be picking CppWinRT's IUnknown instead of raw one which wouldn't work with CX objects, so can you try using ::IUnknown instead of IUnknown for that cast?

Interesting, I tried with :: but same error.

Hopefully this is the case, if not I will try to investigate further as soon as I get the time to

Alright, I'll let you have a look, thanks again.

DirectX Debug Layer might also help investigating the problem

Will check this out

Annihil commented 7 months ago

btw does your app initialize XAML? because if so then that's why, XAML would be already using the CoreWindow so creation of a swapchain over that CoreWindow would fail

It does, ah I see, let me have a look!

Annihil commented 7 months ago

Ok so I was able to create the swapchain by calling setWindow inside the Widget1 constructor

static void RenderingThread(App& app) {
    app.Run();
}

Widget1::Widget1() {
    //InitializeComponent();
    auto app = App();
    app.SetWindow(Window::Current->CoreWindow);
    thread RenderThread(RenderingThread, app);
    RenderThread.detach();
}

but now I get an error DCOMPOSITION_ERROR_WINDOW_ALREADY_COMPOSED in ImGui_ImplDX12_NewFrame

ahmed605 commented 7 months ago

this is still conflicting with XAML since it's still initialized

ahmed605 commented 7 months ago

To get XAML not to be initialized you have to add DISABLE_XAML_GENERATED_MAIN to Preprocessor Definitions then define your own main entrypoint function as shown in the samples in this repo, and avoid using anything in the XAML namespace (e.g Window class)

Annihil commented 7 months ago

To get XAML not to be initialized you have to add DISABLE_XAML_GENERATED_MAIN to Preprocessor Definitions then define your own main entrypoint function as shown in the samples in this repo, and avoid using anything in the XAML namespace (e.g Window class)

Gave that a try, all the files compile fine but at the very end of the build process it throws an error error APPX0702: Payload file '...\x64\Debug\WidgetSampleCX\WidgetSampleCX.winmd' does not exist.

Edit: Generate Windows Metadata linker switch to No (/WINMD:NO) fixed this issue

Now it runs but, only if I click play in Visual Studio, not once I run it as a widget using the game bar, which does make sense since I removed the XAML part that initialize the widget...

Now the trick is to figure out how to bootstrap the connection with Game Bar without using XAML 😅 https://github.com/microsoft/XboxGameBarSamples/blob/master/Samples/WidgetSampleCX/App.xaml.cpp#L85 So close yet so far 😊

ahmed605 commented 7 months ago

hm XboxGameBarWidget requiring a Frame will be complicating things a bit, I need to figure out how to initialize GameBar without this API in order to workaround this.. in the meantime I guess you can re-enable XAML and use a Frame with a SwapChainPanel as its child and use it to initialize ImGui, it won't give the best experience but should work as a temporary solution

Annihil commented 7 months ago

in the meantime I guess you can re-enable XAML and use a Frame with a SwapChainPanel as its child and use it to initialize ImGui, it won't give the best experience but should work as a temporary solution

That's what I've been trying to do initially already, did not manage to do it :/ Did you get the time to try?

ahmed605 commented 7 months ago

@Annihil I added an Xbox Game Bar example to the repo https://github.com/ahmed605/imgui-uwp/tree/master/examples/example_uwp_gamebar_directx12 image

Annihil commented 7 months ago

@ahmed605 impressive work, many thanks! (I added you on discord btw, I'm anni)