GDI 和 DirectX 渲染

Posted

技术标签:

【中文标题】GDI 和 DirectX 渲染【英文标题】:GDI and DirectX rendering 【发布时间】:2016-07-29 19:53:32 【问题描述】:

我有两个窗口,一个是由 D3D11 渲染的父窗口,另一个是我想在父窗口上移动的子窗口。

这是我如何创建窗口的代码:

wcex.cbSize         = sizeof(WNDCLASSEX);
wcex.style          = 0;
wcex.lpfnWndProc    = WndProc;
wcex.cbClsExtra     = 0;
wcex.cbWndExtra     = 0;
wcex.hInstance      = hInstance;
wcex.hIcon          = LoadIcon(NULL,IDI_APPLICATION);
wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground  = (HBRUSH)0;
wcex.lpszMenuName   = 0;
wcex.lpszClassName  = L"Parent";
wcex.hIconSm        = LoadIcon(NULL,IDI_APPLICATION);

if(!RegisterClassEx(&wcex))
    return E_FAIL;


if(!(hWnd = CreateWindowEx(WS_EX_COMPOSITED,L"Parent",L"WINDOW",
                           WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
                            WS_VISIBLE,
                            CW_USEDEFAULT,CW_USEDEFAULT,
                            WIDTH,HEIGHT,
                            NULL,NULL,
                            hInstance,NULL)))
    return E_FAIL;

和子窗口

wcex2.cbSize         = sizeof(WNDCLASSEX);
wcex2.style          = 0;
wcex2.lpfnWndProc    = WndProc2;
wcex2.cbClsExtra     = 0;
wcex2.cbWndExtra     = 0;
wcex2.hInstance      = hInstance;
wcex2.hIcon          = LoadIcon(NULL,IDI_APPLICATION);
wcex2.hCursor        = LoadCursor(NULL, IDC_ARROW);
wcex2.hbrBackground  = (HBRUSH)0;
wcex2.lpszMenuName   = 0;
wcex2.lpszClassName  = L"Child";
wcex2.hIconSm        = LoadIcon(NULL,IDI_APPLICATION);

if(!RegisterClassEx(&wcex2))
    return E_FAIL;

if(!(chilWnd = CreateWindowEx(0, wcex2.lpszClassName, NULL, 
                                WS_CHILD|WS_CLIPSIBLINGS,
                                    0,0,
                                    200, 100,
                                    hWnd,NULL,
                                    hInstance,0)))

    return FALSE;

这是子窗口的 WndProc

 case WM_LBUTTONDOWN:
 dragWindow = true;
 SetCapture(hWnd);
 break;

 case WM_LBUTTONUP:
 ReleaseCapture();
 dragWindow = false;
 break;

case WM_MOUSEMOVE:

 if (dragWindow == true)
   
    RECT mainWindowRect;
    POINT pos;
    int windowWidth, windowHeight;

    pos.x = (int)(short) LOWORD(lp);
    pos.y = (int)(short) HIWORD(lp);

    GetWindowRect(hWnd,&mainWindowRect);
    windowHeight = mainWindowRect.bottom - mainWindowRect.top;
    windowWidth = mainWindowRect.right - mainWindowRect.left;

    ClientToScreen(hWnd, &pos);

    HDWP hdwp = BeginDeferWindowPos(1);

    DeferWindowPos(hdwp, 
                    hWnd,
                    HWND_TOP,
                    pos.x,
                    pos.y, 
                    windowWidth,
                    windowHeight, 
                    SWP_NOZORDER);

    EndDeferWindowPos(hdwp);

    LockWindowUpdate(hWnd);
    RedrawWindow(hWnd, 0, 0, RDW_UPDATENOW);
    LockWindowUpdate(NULL);
    ....
    case WM_PAINT:
    hdc = BeginPaint(hWnd, &Ps);

    FillRect( hdc, &r, (HBRUSH)GetStockObject(GRAY_BRUSH));

    EndPaint(hWnd, &Ps);

如果没有 LockWindowUpdate() 函数,我在移动它时会有子窗口跟踪。 所以最终结果是我移动它时子窗口是黑色的。我还能做什么? 我尝试了 GDI 双缓冲,即在 WM_MOUSEMOVE 事件上绘制屏幕外缓冲区 并在 WM_PAINT 事件的窗口上绘制,但结果相同。

【问题讨论】:

在子进程中你可以添加case WM_NCHITTEST: return HTCAPTION;这将移动窗口(删除WM_MOUSEMOVE/LBUTTONUP/DOWN)也尝试将WS_EX_COMPOSITED更改为零 我添加了case WM_NCHITTEST: return HTCAPTION;并删除了WM_MOUSEMOVE/LBUTTONUP/DOWN,但是当我移动它时,我有子窗口的痕迹,就像旧位置一样。 不应有任何尾随行。更新您的问题以显示您在做什么。 【参考方案1】:

我的代码几乎所有内容。我向你展示了我是如何创建窗口的,我也有 DirectX 渲染循环:

bool done = false;
MSG  msg;

while(!done)

    if(PeekMessage(&msg,0,0,0,PM_REMOVE))
    
        if(msg.message == WM_QUIT)
        
            done = true;
        
        else
        
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        
    
    else
    
        Render();
    

所以在这个 Render() 函数中,我只是清除渲染目标和翻转缓冲区。没有别的了。

D3DXCOLOR color(0.5f,0.5f,0.85f,1.0f);
pDevContext->ClearRenderTargetView(pRenderTarget,color);


pSwapChain->Present(1,0);

【讨论】:

这不是答案。您应该更新您的问题并将其删除。上面的代码也不是一个完整的程序。请参阅帮助部分***.com/help/mcve【参考方案2】:

在子窗口中,覆盖WM_NCHITTEST移动,覆盖WM_WINDOWPOSCHANGING强制重绘:

LRESULT CALLBACK ChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    switch (message)
    
    case WM_WINDOWPOSCHANGING: Render(); break;
    case WM_NCHITTEST: return HTCAPTION;    
    
    return DefWindowProc(hWnd, message, wParam, lParam);


LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

    if (msg == WM_DESTROY)
    
        PostQuitMessage(0);
        return 0;
    
    return DefWindowProc(hWnd, msg, wParam, lParam);


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)

    HWND hWnd;
    WNDCLASSEX wc =  sizeof(WNDCLASSEX) ;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass";
    RegisterClassEx(&wc);

    hWnd = CreateWindow(wc.lpszClassName, L"appName", 
        WS_CLIPCHILDREN | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
        0, 0, 800, 600, NULL, NULL, hInstance, NULL);

    //initialize Direct3D
    initD3D(hWnd, 800, 600);

    wc.lpfnWndProc = ChildProc;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.lpszClassName = L"child";
    RegisterClassEx(&wc);
    CreateWindow(wc.lpszClassName, 0, 
        WS_BORDER | WS_VISIBLE | WS_CHILD, 
        0, 0, 300, 200, hWnd, 0, 0, 0);

    MSG msg =  0 ;
    while (msg.message != WM_QUIT)
    
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        
        else
        
            Render();
        
    

    // clean up Direct3D
    cleanD3D();

    return msg.wParam;

【讨论】:

我覆盖WM_NCHITTEST 来移动,覆盖WM_WINDOWPOSCHANGING 来强制重绘,是的,痕迹消失了,但是当我移动子窗口时,边缘正在切割,子窗口变得越来越小四边形. 没有调整大小的代码,所以没有理由让子窗口变小。你做错了什么,或者你的 Direct3D 代码中有什么东西我猜不出来。 我更新了我有完整代码的帖子link

以上是关于GDI 和 DirectX 渲染的主要内容,如果未能解决你的问题,请参考以下文章

DirectX 12和DirectX 11选哪个,有什么区别

DirectX11第二篇 DirectX11渲染管线(2016.05.09更新)

DirectX11第二篇 DirectX11渲染管线(2016.05.09更新)

DirectX 11 渲染到特定区域

DirectX* 11 多线程渲染的性能方法和实践

使用 DirectX 11 后重置窗口