Search code examples
directxwinui-3c++-winrt

How to implement a SwapChainPanel with C++/WinRT and WinUI 3


I'm trying to implement a SwapChainPanel using C++/WinRT and WinUI 3. I've found answers for C# and WinUI 3 and C++/CX and WinUI, but not my particular combination.

As a start, I create a C++/WinRT app using the "Blank App, Packaged (WinUI 3 in Desktop" template. I then add the code from the SwapChainPanel section of the "DirectX and XAML interop" page 1 in learn.microsoft.com.

The code compiles and runs, but when I click the button, I get an error in this code:

App::App()
{
    InitializeComponent();

#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
    UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e)
    {
        if (IsDebuggerPresent())
        {
            auto errorMessage = e.Message();
            __debugbreak();  <--- Error on this line
        }
    });
#endif
}

The error is: "A breakpoint instruction (__debugbreak() statement or a similar call) was executed in SCPDemo.exe."

The stack is:

SCPDemo.exe!winrt::SCPDemo::implementation::App::{ctor}::__l2::<lambda>(const winrt::Windows::Foundation::IInspectable & __formal, const winrt::Microsoft::UI::Xaml::UnhandledExceptionEventArgs & e) Line 31   C++
    SCPDemo.exe!winrt::impl::delegate<winrt::Microsoft::UI::Xaml::UnhandledExceptionEventHandler,void <lambda>(const winrt::Windows::Foundation::IInspectable &, const winrt::Microsoft::UI::Xaml::UnhandledExceptionEventArgs &)>::Invoke(void * sender, void * e) Line 4824   C++
    Microsoft.ui.xaml.dll!DirectUI::CFTMEventSource<struct ABI::Microsoft::UI::Xaml::IUnhandledExceptionEventHandler,struct ABI::Microsoft::UI::Xaml::IApplication,struct ABI::Microsoft::UI::Xaml::IUnhandledExceptionEventArgs>::Raise(struct ABI::Microsoft::UI::Xaml::IApplication *,struct ABI::Microsoft::UI::Xaml::IUnhandledExceptionEventArgs *)   Unknown
    Microsoft.ui.xaml.dll!DirectUI::FrameworkApplication::RaiseUnhandledExceptionEvent(long,struct HSTRING__ *,unsigned int *)  Unknown
    Microsoft.ui.xaml.dll!DirectUI::ErrorHelper::RaiseUnhandledExceptionEvent(long,class xstring_ptr const &,unsigned int *)    Unknown
    Microsoft.ui.xaml.dll!DirectUI::ErrorHelper::ProcessUnhandledError(class DirectUI::ErrorInfo &,unsigned int,unsigned int *) Unknown
    Microsoft.ui.xaml.dll!DirectUI::ErrorHelper::ReportUnhandledError(long) Unknown
    Microsoft.ui.xaml.dll!DirectUI::DXamlCore::FireEvent()  Unknown
    Microsoft.ui.xaml.dll!CCoreServices::CLR_FireEvent()    Unknown
    Microsoft.ui.xaml.dll!CommonBrowserHost::CLR_FireEvent()    Unknown
    Microsoft.ui.xaml.dll!CControlBase::ScriptCallback()    Unknown
    Microsoft.ui.xaml.dll!CXcpDispatcher::OnScriptCallback()    Unknown
    Microsoft.ui.xaml.dll!CXcpDispatcher::OnWindowMessage() Unknown
    Microsoft.ui.xaml.dll!CXcpDispatcher::WindowProc()  Unknown
    user32.dll!00007ff819afe858()   Unknown
    user32.dll!00007ff819afde1b()   Unknown
    user32.dll!00007ff819afd68a()   Unknown
    Microsoft.ui.xaml.dll!CXcpBrowserHost::SyncScriptCallbackRequest()  Unknown
    Microsoft.ui.xaml.dll!CEventManager::Raise()    Unknown
    Microsoft.ui.xaml.dll!CEventManager::RaiseRoutedEventBubbling() Unknown
    Microsoft.ui.xaml.dll!CInputServices::RaiseDelayedPointerUpEvent()  Unknown
    Microsoft.ui.xaml.dll!CInputServices::ProcessGestureInput() Unknown
    Microsoft.ui.xaml.dll!CInputServices::ProcessTouchInteractionCallback() Unknown
    Microsoft.ui.xaml.dll!CCoreServices::ProcessTouchInteractionCallback()  Unknown
    Microsoft.ui.xaml.dll!GestureRecognizerAdapter::OnTapped()  Unknown
    Microsoft.ui.xaml.dll!Microsoft::WRL::Details::DelegateArgTraits<long (__cdecl ABI::Windows::Foundation::ITypedEventHandler_impl<ABI::Windows::Foundation::Internal::AggregateType<ABI::Microsoft::UI::Input::GestureRecognizer *,ABI::Microsoft::UI::Input::IGestureRecognizer *>,ABI::Windows::Foundation::Internal::AggregateType<ABI::Microsoft::UI::Input::TappedEventArgs *,ABI::Microsoft::UI::Input::ITappedEventArgs *>>::*)(ABI::Microsoft::UI::Input::IGestureRecognizer *,ABI::Microsoft::UI::Input::ITappedEventArgs *)>::DelegateInvokeHelper<Microsoft::WRL::Implements<Microsoft::WRL::RuntimeClassFlags<2>,ABI::Windows::Foundation::ITypedEventHandler<ABI::Microsoft::UI::Input::GestureRecognizer *,ABI::Microsoft::UI::Input::TappedEventArgs *>,Microsoft::WRL::FtmBase>,<lambda_45f026d2817627401d33114d1063e5ff> &,1,ABI::Microsoft::UI::Input::IGestureRecognizer *,ABI::Microsoft::UI::Input::ITappedEventArgs *>::Invoke()   Unknown
    Microsoft.UI.Input.dll!00007fff2e91cc7a()   Unknown
    Microsoft.UI.Input.dll!00007fff2e918276()   Unknown
    Microsoft.UI.Input.dll!00007fff2e915f32()   Unknown
    Microsoft.UI.Input.dll!00007fff2e915c3e()   Unknown
    ninput.dll!00007ff81236b0a8()   Unknown
    ninput.dll!00007ff812370798()   Unknown
    ninput.dll!00007ff81238f27f()   Unknown
    ninput.dll!00007ff81238f246()   Unknown
    ninput.dll!00007ff81238e9fa()   Unknown
    ninput.dll!00007ff81238eacf()   Unknown
    ninput.dll!00007ff81238d3ea()   Unknown
    ninput.dll!00007ff8123711b0()   Unknown
    ninput.dll!00007ff812370ac7()   Unknown
    ninput.dll!00007ff81236eff4()   Unknown
    Microsoft.UI.Input.dll!00007fff2e91a18b()   Unknown
    Microsoft.ui.xaml.dll!ElementGestureTracker::ProcessPointerInformation()    Unknown
    Microsoft.ui.xaml.dll!CInputServices::ProcessPointerMessagesWithInteractionEngine() Unknown
    Microsoft.ui.xaml.dll!CInputServices::ProcessInteractionPointerMessages()   Unknown
    Microsoft.ui.xaml.dll!ContentRootInput::PointerInputProcessor::ProcessPointerInput()    Unknown
    Microsoft.ui.xaml.dll!CInputServices::ProcessInput()    Unknown
    Microsoft.ui.xaml.dll!CXcpBrowserHost::HandleInputMessage() Unknown
    Microsoft.ui.xaml.dll!CJupiterControl::HandlePointerMessage()   Unknown
    Microsoft.ui.xaml.dll!CJupiterWindow::OnIslandPointerMessage(unsigned int,class CContentRoot *,struct ABI::Microsoft::UI::Input::IPointerPoint *,struct ABI::Microsoft::UI::Input::IPointerEventArgs *,bool,bool *) Unknown
    Microsoft.ui.xaml.dll!CXamlIslandRoot::InjectPointerMessage(unsigned int,struct ABI::Microsoft::UI::Input::IPointerEventArgs *) Unknown
    Microsoft.ui.xaml.dll!Microsoft::WRL::Details::DelegateArgTraits<long (__cdecl ABI::Windows::Foundation::ITypedEventHandler_impl<ABI::Windows::Foundation::Internal::AggregateType<ABI::Microsoft::UI::Input::InputPointerSource *,ABI::Microsoft::UI::Input::IInputPointerSource *>,ABI::Windows::Foundation::Internal::AggregateType<ABI::Microsoft::UI::Input::PointerEventArgs *,ABI::Microsoft::UI::Input::IPointerEventArgs *>>::*)(ABI::Microsoft::UI::Input::IInputPointerSource *,ABI::Microsoft::UI::Input::IPointerEventArgs *)>::DelegateInvokeHelper<Microsoft::WRL::Implements<Microsoft::WRL::RuntimeClassFlags<2>,ABI::Windows::Foundation::ITypedEventHandler<ABI::Microsoft::UI::Input::InputPointerSource *,ABI::Microsoft::UI::Input::PointerEventArgs *>,Microsoft::WRL::FtmBase>,<lambda_ce6152cb73defb12746a6f18006dfc8c> &,1,ABI::Microsoft::UI::Input::IInputPointerSource *,ABI::Microsoft::UI::Input::IPointerEventArgs *>::Invoke() Unknown
    Microsoft.UI.Input.dll!00007fff2e8ff3d8()   Unknown
    Microsoft.UI.Input.dll!00007fff2e900273()   Unknown
    Microsoft.UI.Input.dll!00007fff2e8fcfbf()   Unknown
    Microsoft.UI.Input.dll!00007fff2e8fa607()   Unknown
    Microsoft.UI.Input.dll!00007fff2e8feee1()   Unknown
    Microsoft.UI.Input.dll!00007fff2e8fa457()   Unknown
    Microsoft.UI.Input.dll!00007fff2e8fa173()   Unknown
    Microsoft.UI.Input.dll!00007fff2e8f985d()   Unknown
    Microsoft.UI.Input.dll!00007fff2e8f9693()   Unknown
    Microsoft.UI.Input.dll!00007fff2e90184c()   Unknown
    CoreMessagingXP.dll!00007fff2fe89007()  Unknown
    Microsoft.UI.Input.dll!00007fff2e8f6dbf()   Unknown
    Microsoft.UI.Input.dll!00007fff2e95daac()   Unknown
    CoreMessagingXP.dll!00007fff2fe3be28()  Unknown
    CoreMessagingXP.dll!00007fff2fe3b795()  Unknown
    CoreMessagingXP.dll!00007fff2fe3c002()  Unknown
    CoreMessagingXP.dll!00007fff2fe4edfe()  Unknown
    CoreMessagingXP.dll!00007fff2fe7fdd6()  Unknown
    CoreMessagingXP.dll!00007fff2fe2a434()  Unknown
    CoreMessagingXP.dll!00007fff2fe228d1()  Unknown
    CoreMessagingXP.dll!00007fff2fe2261b()  Unknown
    CoreMessagingXP.dll!00007fff2fe48376()  Unknown
    CoreMessagingXP.dll!00007fff2fe1221f()  Unknown
    CoreMessagingXP.dll!00007fff2fe47a44()  Unknown
    CoreMessagingXP.dll!00007fff2fe6222c()  Unknown
    CoreMessagingXP.dll!00007fff2fe19c5c()  Unknown
    CoreMessagingXP.dll!00007fff2fe1950e()  Unknown
    CoreMessagingXP.dll!00007fff2fe0ed7b()  Unknown
    CoreMessagingXP.dll!00007fff2fe11010()  Unknown
    CoreMessagingXP.dll!00007fff2fe114e6()  Unknown
    CoreMessagingXP.dll!00007fff2fe1140a()  Unknown
    CoreMessagingXP.dll!00007fff2fe6477f()  Unknown
    CoreMessagingXP.dll!00007fff2fe6471f()  Unknown
    user32.dll!00007ff819afe858()   Unknown
    user32.dll!00007ff819afe299()   Unknown
    Microsoft.ui.xaml.dll!DirectUI::FrameworkApplication::RunDesktopWindowMessageLoop() Unknown
    Microsoft.ui.xaml.dll!DirectUI::FrameworkApplication::StartDesktop()    Unknown
    Microsoft.ui.xaml.dll!DirectUI::FrameworkApplicationFactory::Start()    Unknown
    SCPDemo.exe!winrt::impl::consume_Microsoft_UI_Xaml_IApplicationStatics<winrt::Microsoft::UI::Xaml::IApplicationStatics>::Start(const winrt::Microsoft::UI::Xaml::ApplicationInitializationCallback & callback) Line 157 C++
    SCPDemo.exe!winrt::Microsoft::UI::Xaml::Application::Start::__l2::<lambda>(const winrt::Microsoft::UI::Xaml::IApplicationStatics & f) Line 12146    C++
    SCPDemo.exe!winrt::impl::factory_cache_entry<winrt::Microsoft::UI::Xaml::Application,winrt::Microsoft::UI::Xaml::IApplicationStatics>::call<void <lambda>(const winrt::Microsoft::UI::Xaml::IApplicationStatics &) &>(winrt::Microsoft::UI::Xaml::Application::Start::__l2::void <lambda>(const winrt::Microsoft::UI::Xaml::IApplicationStatics &) & callback) Line 6286    C++
    SCPDemo.exe!winrt::impl::call_factory<winrt::Microsoft::UI::Xaml::Application,winrt::Microsoft::UI::Xaml::IApplicationStatics,void <lambda>(const winrt::Microsoft::UI::Xaml::IApplicationStatics &)>(winrt::Microsoft::UI::Xaml::Application::Start::__l2::void <lambda>(const winrt::Microsoft::UI::Xaml::IApplicationStatics &) && callback) Line 6309   C++
    SCPDemo.exe!winrt::Microsoft::UI::Xaml::Application::Start(const winrt::Microsoft::UI::Xaml::ApplicationInitializationCallback & callback) Line 12147   C++
    SCPDemo.exe!wWinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, wchar_t * __formal, int __formal) Line 69  C++
    SCPDemo.exe!invoke_main() Line 123  C++
    SCPDemo.exe!__scrt_common_main_seh() Line 288   C++
    SCPDemo.exe!__scrt_common_main() Line 331   C++
    SCPDemo.exe!wWinMainCRTStartup(void * __formal) Line 17 C++
    kernel32.dll!00007ff818917034() Unknown
    ntdll.dll!00007ff819d226a1()    Unknown

The one instruction I did not follow on the linked page was modify the IDL, so that seems like the next thing to try. Here's the suggested IDL:

// MainPage.idl
namespace SCPDemo
{
    [default_interface]
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
    }
}

and here's the original IDL

namespace SCPDemo
{
    [default_interface]
    runtimeclass MainWindow : Microsoft.UI.Xaml.Window
    {
        MainWindow();
        Int32 MyProperty;
    }
}

I tried changing MainWindow to inherit from Microsoft.UI.Xaml.Controls.Page, but that doesn't compile. The error is in the App::OnLaunched() function in the line

window = make<MainWindow>();

It makes sense that you can't make a window from a class inheriting from Controls, but I don't have a clue as to what the correct IDL should be. Suggestions?

Update: I stepped through the code and found the line where the error occurs. Here's the function:

void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&)
{
    myButton().Content(box_value(L"Clicked"));
    
    // 3. In the MainPage class, we'll first create a Direct 3D device, a Direct 2D device, and a Direct 2D device context. 
    //      To do that, we'll call D3D11CreateDevice, D2D1CreateDevice, and ID2D1Device::CreateDeviceContext.
    uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };

    // Create the Direct3D device.
    winrt::com_ptr<::ID3D11Device> d3dDevice;
    D3D_FEATURE_LEVEL supportedFeatureLevel;
    winrt::check_hresult(::D3D11CreateDevice(
        nullptr,
        D3D_DRIVER_TYPE_HARDWARE,
        0,
        creationFlags,
        featureLevels,
        ARRAYSIZE(featureLevels),
        D3D11_SDK_VERSION,
        d3dDevice.put(),
        &supportedFeatureLevel,
        nullptr)
    );

    // Get the Direct3D device.
    winrt::com_ptr<::IDXGIDevice> dxgiDevice{d3dDevice.as<::IDXGIDevice>()};

    // Create the Direct2D device and a corresponding context.
    winrt::com_ptr<::ID2D1Device> d2dDevice;
    ::D2D1CreateDevice(dxgiDevice.get(), nullptr, d2dDevice.put());

    winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext;
    winrt::check_hresult(d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2dDeviceContext.put()));

    // 5. Next, call call IDXGIFactory2::CreateSwapChainForComposition to create a swap chain.
    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    // Get the DXGI adapter.
    winrt::com_ptr< ::IDXGIAdapter > dxgiAdapter;
    dxgiDevice->GetAdapter(dxgiAdapter.put());

    // Get the DXGI factory.
    winrt::com_ptr< ::IDXGIFactory2 > dxgiFactory;
    dxgiFactory.capture(dxgiAdapter, &IDXGIAdapter::GetParent);

    DXGI_SWAP_CHAIN_DESC1 swapChainDesc{ 0 };
    swapChainDesc.Width = 500;
    swapChainDesc.Height = 500;
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swapchain format.
    swapChainDesc.Stereo = false;
    swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 2;
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // We recommend using this swap effect for all applications.
    swapChainDesc.Flags = 0;

    // Create a swap chain by calling IDXGIFactory2::CreateSwapChainForComposition.
    winrt::com_ptr< ::IDXGISwapChain1 > swapChain;
    dxgiFactory->CreateSwapChainForComposition(d3dDevice.get(), &swapChainDesc, nullptr, swapChain.put());

    // 6. Get an ISwapChainPanelNative from the SwapChainPanel that you named swapChainPanel. 
    //      Then call ISwapChainPanelNative::SetSwapChain to set the swap chain on the SwapChainPanel.
    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    // Get native interface for SwapChainPanel
//////////////////////////////////////////////////////////////////////
//     ERROR OCCURS ON NEXT LINE
//////////////////////////////////////////////////////////////////////
    auto panelNative{ swapChainPanel().as<ISwapChainPanelNative>() };

    winrt::check_hresult(panelNative->SetSwapChain(swapChain.get()));

    // 7. Lastly, draw to the DirectX swap chain, and then present it to display the contents.
    // Create a Direct2D target bitmap associated with the swap chain back buffer, and set it as the current target.
    D2D1_BITMAP_PROPERTIES1 bitmapProperties =
        D2D1::BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
            96.f,
            96.f
        );

    winrt::com_ptr<::IDXGISurface> dxgiBackBuffer;
    swapChain->GetBuffer(0, __uuidof(dxgiBackBuffer), dxgiBackBuffer.put_void());

    winrt::com_ptr< ::ID2D1Bitmap1 > targetBitmap;
    winrt::check_hresult(
        d2dDeviceContext->CreateBitmapFromDxgiSurface(
            dxgiBackBuffer.get(),
            &bitmapProperties,
            targetBitmap.put()
        )
    );

    d2dDeviceContext->SetTarget(targetBitmap.get());

    // Draw using Direct2D context.
    d2dDeviceContext->BeginDraw();

    d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Orange));

    winrt::com_ptr<::ID2D1SolidColorBrush> brush;
    winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF::Chocolate),
        D2D1::BrushProperties(0.8f),
        brush.put()));

    D2D1_SIZE_F const size{ 500, 500 };
    D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f };
    d2dDeviceContext->DrawRectangle(rect, brush.get(), 100.0f);

    d2dDeviceContext->EndDraw();

    swapChain->Present(1, 0);
}

Any ideas on why the line

auto panelNative{ swapChainPanel().as<ISwapChainPanelNative>() };

is failing?


Solution

  • I just got a pm from IInspectable. He suggested I change a line in the pch.h file from

    #include <windows.ui.xaml.media.dxinterop.h>
    

    to

    #include <microsoft.ui.xaml.media.dxinterop.h>
    

    This solved the problem. I can hardly wait until the Microsoft documentation catches up with their move to WinUI 3.