【问题标题】:Creating Swap Chain causes Window Handle to become Invalid创建交换链导致窗口句柄变为无效
【发布时间】:2020-12-12 02:25:34
【问题描述】:

我正在尝试同时使用 Direct2D 和 Direct3D。现在,我让 Direct2D 将内容绘制到单独的设备上下文(带有 HDC),然后将该设备上下文的内容复制到我的窗口中。如果需要,我可以在此帖子的编辑中显示我用来设置它的代码,但在涉及 Direct3D 之前,该部分可以工作。

这是我使用的窗口绘图代码的简化版本。

        if (d3dEngine.Get())   // Object used to hold Direct3D Resources (.Get() returns a pointer for a null check)
        {
            // d3dEngine->PrepareScene(D2D1::ColorF(D2D1::ColorF::Wheat));
        }
        
        // Drawing Board holds the Direct 2D Render Target
        drawingBoard->GetRenderer()->BeginDraw();
        drawingBoard->GetRenderer()->Clear(D2D1::ColorF(1.0f,1.0f,1.0f,1.0f));

        mainPage->Draw(); // Main Page Holds various objects that draw to Direct2D

        if (d3dEngine.Get())
            d3dEngine->FinalizeScene();

        drawingBoard->GetRenderer()->EndDraw();

        // Get the Secondary Device Context that Direct2D draws to
        HDC dc = drawingBoard->GetDc();

        RECT r{ 0,0,0,0 };
        int err = 0;

        // Retrieve the Rectangle for the window (currentWindow is the window handle used)
        if(!GetClientRect(currentWindow, &r))
            err = GetLastError();

        // Use the BitBlt function to copy Direct2D content into a window
        if (!BitBlt(GetDC(currentWindow), r.left, r.top, r.right - r.left, r.bottom - r.top, dc, 0, 0, SRCCOPY))
            err = GetLastError();

在创建任何 Direct3D 资源之前(d3dEngine.Get() 调用返回 null),这段代码运行得令我满意。

但是,在创建 Direct3D 资源之后,代码会失败:

        RECT r{ 0,0,0,0 };
        int err = 0;

        // Retrieve the Rectangle for the window (currentWindow is the window handle used)
        if(!GetClientRect(currentWindow, &r))
            err = GetLastError();

窗口句柄 currentWindow 变为无效,因为在调用 GetClientRectGetLastError() 返回 1400。由于以下用于激活 Direct3D 的代码,我怀疑 Direct3D 11 中的交换链可能会起作用。

    GetClientRect(window, &Location);

    unsigned int width = Location.right - Location.left,
        height = Location.bottom - Location.top;

    D3D_DRIVER_TYPE dTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE//, D3D_DRIVER_TYPE_WARP
    };
    int tTypes = ARRAYSIZE(dTypes);
    D3D_FEATURE_LEVEL dLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0
    };
    int tLevels = ARRAYSIZE(dLevels);


    DXGI_SWAP_CHAIN_DESC swapChainDescription;



    // Initialize the swap cahin
    swapChainDescription.BufferCount = 2;
    swapChainDescription.BufferDesc.Width = Location.right - Location.left;
    swapChainDescription.BufferDesc.Height = Location.bottom - Location.top;
    swapChainDescription.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapChainDescription.BufferDesc.RefreshRate.Numerator = 30;
    swapChainDescription.BufferDesc.RefreshRate.Denominator = 1;
    swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDescription.OutputWindow = window;
    swapChainDescription.Windowed = true;
    swapChainDescription.SampleDesc.Count = 1;
    swapChainDescription.SampleDesc.Quality = 0;
    swapChainDescription.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    swapChainDescription.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    swapChainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
    swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;// DXGI_SWAP_EFFECT_DISCARD;

    unsigned int flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

    /// Other initialization Code
    HRESULT results = 0;
    // Initialize ID3D11Device "graphicsDevice"
    IDXGISwapChain* sc = nullptr;

    results = dxFact->CreateSwapChain(graphicsDevice, &swapChainDescription, &sc);

此代码与开头的 Window 绘图代码属于不同的类。

Direct3D 代码中的 window 变量与 currentWindow 在窗口代码中的值相同。

有没有人可以提供关于发生了什么以及窗口句柄停止工作的原因的见解?或许建议一种解决方法?

【问题讨论】:

    标签: c++ windows visual-c++ handle direct3d11


    【解决方案1】:

    虽然我仍然不确定为什么在检查时窗口句柄似乎没有用,但我能够开发出一种解决方法。

    基本上,我的 Window 类持有一个指示 Window 大小的 RECT,我使用它而不是每次都从 Window 获取 RECT。

            RECT r{ 0,0,0,0 };
            int err = 0;
    
            // Retrieve the Rectangle for the window (currentWindow is the window handle used)
            if(!GetClientRect(currentWindow, &r))
                err = GetLastError();
    
            // Use the BitBlt function to copy Direct2D content into a window
            if (!BitBlt(GetDC(currentWindow), r.left, r.top, r.right - r.left, r.bottom - r.top, dc, 0, 0, SRCCOPY))
                err = GetLastError();
    
    
            // Now, it uses a 'size' attribute
            int err = 0;
    
            if (!BitBlt(GetTWindowDc(), size.left, size.top, size.right - size.left, size.bottom - size.top, dc, 0, 0, SRCCOPY))
                err = GetLastError();
    
    

    在获取 Window Device Context 时,新的 GetTWindowDc() 方法会解决这个问题:

        if (d3dEngine.Get())
            return d3dEngine->GetDC();
        return GetDC(currentWindow);
    

    基本上,如果 Direct3D 被激活,3D 管理器对象(包含 DXGISurface)会从 Direct3D 检索 HDC 以使用。

    HDC TWindowEngine::GetDC()
    {
        if (!surface.Get()) // My Unique Smart pointer to a DXGISurface1 object
            return 0;
        HDC dc;
        assert(SUCCEEDED(surface->GetDC(FALSE, &dc)));
        return dc;
    }
    

    根据Surface-GetDc Documentation,需要在DXGISurface1对象上调用对应的ReleaseDc。所以我就是这样做的。

    在Window Draw代码中:

            if (!BitBlt(GetTWindowDc(), size.left, size.top, size.right - size.left, size.bottom - size.top, dc, 0, 0, SRCCOPY))
                err = GetLastError();
            FlushDc();
    

    这里是 FlushDc 方法:

    void TWindow::FlushDc()
    {
        if (d3dEngine.Get())
            d3dEngine->ClearDC();
    }
    

    ClearDC 方法被执行。

    void TWindowEngine::ClearDC()
    {
        if (surface.Get())
            surface->ReleaseDC(nullptr);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-24
      • 2011-10-17
      • 1970-01-01
      • 1970-01-01
      • 2013-09-07
      相关资源
      最近更新 更多