【问题标题】:Direct3D 11: only showing clear colorDirect3D 11:只显示清晰的颜色
【发布时间】:2022-01-17 05:11:57
【问题描述】:

我第一次尝试学习DX11,目标是将API添加到my renditionThe Cherno's Hazel Engine。沿着 OpenGL 轨道,我让 Dear ImGui 工作,我可以转换一个带纹理的四边形。呵呵。

我已经启动并运行了 Direct3D,但出现了问题。我的窗口具有适当的清晰颜色,但甚至不显示 RivaTuner 的 FPS/资源利用率 OSD。对我来说这似乎是合理的,这意味着我在绘制到后台缓冲区之前交换缓冲区,但对于我的生活,我无法隔离错误。我的代码基于 Microsoft Getting Started tutorials。希望我会尽可能多地发布有意义的代码,但不会过火。

这里有五个类很重要:

  • Application:拥有窗口、渲染 API,并运行主循环
  • Window:拥有渲染上下文并处理 Win32 回调
  • RenderingContext:保存所有设备/显示器/等信息,并负责交换缓冲区
  • RenderAPI: 清除后台缓冲区并接收绘图调用
  • Layer: 一种排序绘制不同元素的方法

主循环(在Application中)遍历层栈并调用每个人的update()函数,然后在Window上调用update()并重复。 “客户端”也应该将自己的 Layer 子类推送到堆栈中。

它的简单程度令人费解,但这既是设计/架构实践,也关乎图形或游戏。无论如何,以上所有内容都适用于 OpenGL,但对于 D3D,我会得到上述清除但空白的屏幕。

我将从主循环开始,并在下面发布更完整的设置代码。

void Application::run() {
    while(_running) {
        for(auto layer : _layers) {
            layer->update();
        }
        _window->update();
    }
}

当客户端的Layer 子类更新时,它要求其RenderAPI 设置清除颜色并清除后台缓冲区。这些请求调用此代码:

void DX11RenderAPI::set_clear_color(const float color[4]) {
    _clear_color[0] = color[0];
    _clear_color[1] = color[1];
    _clear_color[2] = color[2];
    _clear_color[3] = color[3];
}

void DX11RenderAPI::clear() {
    _dc  = _context->device_context();
    _rtv = _context->render_target_view();
    _dsv = _context->depth_stencil_view();

    _dc->ClearRenderTargetView(_rtv.Get(), _clear_color);
    _dc->ClearDepthStencilView(_dsv.Get(),
                               D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL,
                               1.0f, 0);
    _dc->OMSetRenderTargets(1, _rtv.GetAddressOf(), _dsv.Get());
    _dc->RSSetViewports(1, &_context->viewport());
}

然后主循环更新其关联的Window,结果如下:

void DX11Window::update() {
    MSG msg{ };
    while(::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
    {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
    }

    _context->swap_buffers();
}

最后,WindowRenderingContext 交换缓冲区:

void DX11Context::swap_buffers() {
    _swapchain->Present(1, 0);
    _device_context->DiscardView(_render_target_view.Get());
    if(_depth_stencil_view) {
        _device_context->DiscardView(_depth_stencil_view.Get());
    }
}

为了完整起见,这里是 Win32 设置代码:

void DX11Window::init() {
    ENGINE_INFO("Creating Win32 Window {}: {}x{}", _data._title,
                                                   _data._width,
                                                   _data._height);
    if(!DirectX::XMVerifyCPUSupport()) {
        ENGINE_CRITICAL("CPU does not support DirectXMath.");
        return;
    }

    HRESULT hr = ::CoInitializeEx(nullptr, COINITBASE_MULTITHREADED);
    if (FAILED(hr)) {
        ENGINE_CRITICAL("CoInitializeEx() failed.");
        return;
    }

    WNDCLASSEX wcex{ };
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = window_proc;
    wcex.hIcon = ::LoadIcon(0, "IDI_ICON");
    wcex.hCursor = ::LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
    wcex.lpszClassName = _data._title.c_str();
    wcex.hIconSm = ::LoadIcon(wcex.hInstance, "IDI_ICON");
    wcex.cbWndExtra = sizeof(void *);

    if(!::RegisterClassEx(&wcex)) {
        ENGINE_CRITICAL("Failed to register window class.");
        return;
    }

    RECT rc = {
        0, 0,
        static_cast<LONG>(_data._width),
        static_cast<LONG>(_data._height)
    };

    ::AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);

    _window = ::CreateWindowEx(
        0,
        _data._title.c_str(),
        _data._title.c_str(),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        rc.right - rc.left,
        rc.bottom - rc.top,
        nullptr, nullptr, 0, this);

    if(_window == nullptr) {
        ENGINE_ERROR("CreateWindowExW failed: ",
                         get_last_error_as_string());

        ::UnregisterClass(wcex.lpszClassName, 0);
        return;
    }

    ::SetWindowLongPtr(_window, 0, reinterpret_cast<LONG_PTR>(this));

    _context = new DX11Context(_window);
    _context->init();
    set_vsync(_data._vsync);

    ::ShowWindow(_window, SW_SHOWNORMAL);
}

还有 D3D 设置代码。它几乎是从 Microsoft 教程中提取的,但我已经在我的转录中发现了几个错误:

void DX11Context::init() {
    create_device_resources();
    create_window_dependent_resources();
}

void DX11Context::create_device_resources() {
    HRESULT hr = E_FAIL;

    create_factory();

    Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
    get_hardware_adapter(adapter.GetAddressOf());

    // Create the Direct3D 11 API device object and a corresponding context.
    Microsoft::WRL::ComPtr<ID3D11Device> device;
    Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;

    UINT device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#ifdef DEBUG
    device_flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    if(adapter) {
        hr = D3D11CreateDevice(
            adapter.Get(),
            D3D_DRIVER_TYPE_UNKNOWN,
            nullptr,
            device_flags,
            feature_level_list,
            1,
            D3D11_SDK_VERSION,
            device.GetAddressOf(),  // Returns the Direct3D device created.
            &_feature_level,        // Returns feature level of device created.
            context.GetAddressOf()  // Returns the device immediate context.
        );
    }

#ifndef NDEBUG
    Microsoft::WRL::ComPtr<ID3D11Debug> d3d_debug;
    if(SUCCEEDED(device.As(&d3d_debug))) {
        Microsoft::WRL::ComPtr<ID3D11InfoQueue> d3d_info_queue;
        if (SUCCEEDED(d3d_debug.As(&d3d_info_queue)))
        {
#ifdef DEBUG
            d3d_info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
            d3d_info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
#endif
            D3D11_MESSAGE_ID hide [] =
            {
                D3D11_MESSAGE_ID_SETPRIVATEDATA_CHANGINGPARAMS,
            };
            D3D11_INFO_QUEUE_FILTER filter = {};
            filter.DenyList.NumIDs = static_cast<UINT>(std::size(hide));
            filter.DenyList.pIDList = hide;
            d3d_info_queue->AddStorageFilterEntries(&filter);
        }
    }
#endif

    hr = device.As(&_device);
    if(FAILED(hr)) {
        ENGINE_CRITICAL("Failed to create DX11 device: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
        return;
    }

    hr = context.As(&_device_context);
    if(FAILED(hr)) {
        ENGINE_CRITICAL("Failed to create DX11 device context: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
        return;
    }

    ENGINE_INFO("Initialized DirectX 11.4 device and device context");

    hr = context.As(&_annotation);
    if(FAILED(hr)) {
        ENGINE_CRITICAL("Failed to create DX11 annotation: ({}) {}",
                        hr, DX11Window::get_last_error_as_string());
        return;
    }
}

void DX11Context::create_factory() {
    HRESULT hr = E_FAIL;
#if defined(_DEBUG) && (_WIN32_WINNT >= 0x0603 /*_WIN32_WINNT_WINBLUE*/)
    bool debugDXGI = false;
    {
        Microsoft::WRL::ComPtr<IDXGIInfoQueue> info_queue;
        if(SUCCEEDED(::DXGIGetDebugInterface1(0,
                        IID_PPV_ARGS(info_queue.GetAddressOf())))) {
            debugDXGI = true;

            hr = ::CreateDXGIFactory2(
                DXGI_CREATE_FACTORY_DEBUG,
                IID_PPV_ARGS(_factory.ReleaseAndGetAddressOf())
            );

            if(FAILED(hr)) {
                ENGINE_CRITICAL("CreateDXGIFactory2() failed: ({}) {}",
                                    hr, DX11Window::get_last_error_as_string());
                return;
            }

            info_queue->
                SetBreakOnSeverity(DXGI_DEBUG_ALL,
                                   DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR,
                                   true);
            info_queue->
                SetBreakOnSeverity(DXGI_DEBUG_ALL,
                                   DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION,
                                   true);

            DXGI_INFO_QUEUE_MESSAGE_ID hide[] =
            {
                80 /* IDXGISwapChain::GetContainingOutput:
                      The swapchain's adapter does not control the output on
                      which the swapchain's window resides. */,
            };

            DXGI_INFO_QUEUE_FILTER filter{ };
            filter.DenyList.NumIDs = static_cast<UINT>(std::size(hide));
            filter.DenyList.pIDList = hide;
            info_queue->AddStorageFilterEntries(DXGI_DEBUG_DXGI, &filter);
        }
    }

    if (!debugDXGI)
#endif
    {
        hr = ::CreateDXGIFactory1(
            IID_PPV_ARGS(_factory.ReleaseAndGetAddressOf())
        );

        if(FAILED(hr)) {
            ENGINE_CRITICAL("CreateDXGIFactory1() failed: ({}) {}",
                                hr, DX11Window::get_last_error_as_string());
            return;
        }
    }
}

void DX11Context::create_window_dependent_resources() {
    HRESULT hr;
    // Clear the previous window size specific context.
    ID3D11RenderTargetView* null_views[] = {nullptr};
    _device_context->OMSetRenderTargets(static_cast<UINT>(std::size(null_views)),
                                        null_views, nullptr);
    _render_target_view.Reset();
    _depth_stencil_view.Reset();
    _render_target.Reset();
    _depth_stencil.Reset();
    _device_context->Flush();

    // Determine the render target size in pixels.
    const UINT back_buffer_width =
        std::max<UINT>(
            static_cast<UINT>(_output_size.right - _output_size.left),
            1u
        );

    const UINT back_buffer_height =
        std::max<UINT>(
            static_cast<UINT>(_output_size.bottom - _output_size.top),
            1u
        );

    if (_swapchain) {
        // If the swap chain already exists, resize it.
        hr = _swapchain->ResizeBuffers(
            _back_buffer_count,
            back_buffer_width,
            back_buffer_height,
            _back_buffer_format,
            0u
        );

        if(FAILED(hr)) {
            ENGINE_CRITICAL("Could not resize swapchain: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
            return;
        }
    }
    else
    {
        // Create a descriptor for the swap chain.
        DXGI_SWAP_CHAIN_DESC1 swapchain_desc{ };
        swapchain_desc.Width = back_buffer_width;
        swapchain_desc.Height = back_buffer_height;
        swapchain_desc.Format = _back_buffer_format;
        swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapchain_desc.BufferCount = _back_buffer_count;
        swapchain_desc.SampleDesc.Count = 1;
        swapchain_desc.SampleDesc.Quality = 0;
        swapchain_desc.Scaling = DXGI_SCALING_STRETCH;
        swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        swapchain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
        swapchain_desc.Flags = 0;

        DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_swapchain_desc{ };
        fs_swapchain_desc.Windowed = TRUE;

        // Create a SwapChain from a Win32 window.
        hr = _factory->CreateSwapChainForHwnd(
            _device.Get(),
            _window,
            &swapchain_desc,
            &fs_swapchain_desc,
            nullptr, _swapchain.ReleaseAndGetAddressOf()
        );

        if(FAILED(hr)) {
            ENGINE_CRITICAL("Could not create swapchain for HWND: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
        }

        // This class does not support exclusive full-screen mode and prevents
        // DXGI from responding to the ALT+ENTER shortcut
        hr = _factory->MakeWindowAssociation(_window, DXGI_MWA_NO_ALT_ENTER);

        if(FAILED(hr)) {
            ENGINE_CRITICAL("Unable to make window association: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
            return;
        }
    }

    // Create a render target view of the swap chain back buffer.
    hr = _swapchain->
        GetBuffer(0, IID_PPV_ARGS(_render_target.ReleaseAndGetAddressOf()));

    if(FAILED(hr)) {
        ENGINE_CRITICAL("Could not get back buffer from swapchain: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
        return;
    }

    CD3D11_RENDER_TARGET_VIEW_DESC
        rt_view_desc(D3D11_RTV_DIMENSION_TEXTURE2D, _back_buffer_format);
    hr = _device->CreateRenderTargetView(
        _render_target.Get(),
        &rt_view_desc,
        _render_target_view.ReleaseAndGetAddressOf()
    );

    if(FAILED(hr)) {
        ENGINE_CRITICAL("Unable to create render target view: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
        return;
    }

    if (_depth_buffer_format != DXGI_FORMAT_UNKNOWN) {
        // Create a depth stencil view for use with 3D rendering if needed.
        CD3D11_TEXTURE2D_DESC depth_stencil_desc(
            _depth_buffer_format,
            back_buffer_width,
            back_buffer_height,
            1, // This depth stencil view has only one texture.
            1, // Use a single mipmap level.
            D3D11_BIND_DEPTH_STENCIL
        );

        hr = _device->CreateTexture2D(
            &depth_stencil_desc,
            nullptr,
            _depth_stencil.ReleaseAndGetAddressOf()
        );

        if(FAILED(hr)) {
            ENGINE_CRITICAL("Unable to create depth stencil texture: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
            return;
        }

        CD3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc(D3D11_DSV_DIMENSION_TEXTURE2D);
        hr = _device->CreateDepthStencilView(
            _depth_stencil.Get(),
            &dsv_desc,
            _depth_stencil_view.ReleaseAndGetAddressOf()
        );

        if(FAILED(hr)) {
            ENGINE_CRITICAL("Failed to create depth stencil view: ({}) {}",
                            hr, DX11Window::get_last_error_as_string());
            return;
        }
    }

    // Set the 3D rendering viewport to target the entire window.
    _viewport = CD3D11_VIEWPORT(
        0.0f,
        0.0f,
        static_cast<float>(back_buffer_width),
        static_cast<float>(back_buffer_height)
    );
}

void DX11Context::get_hardware_adapter(IDXGIAdapter1 **adapter) {
    HRESULT hr;

    *adapter = nullptr;

    Microsoft::WRL::ComPtr<IDXGIAdapter4> possible_adapter;
    Microsoft::WRL::ComPtr<IDXGIFactory6> factory6;

    hr = _factory.As(&factory6);
    if(SUCCEEDED(hr)) {
        for(UINT adapter_index = 0;
            SUCCEEDED(factory6->EnumAdapterByGpuPreference(
                adapter_index,
                DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
                IID_PPV_ARGS(possible_adapter.ReleaseAndGetAddressOf())));
            adapter_index++)
        {
            DXGI_ADAPTER_DESC1 desc;
            possible_adapter->GetDesc1(&desc);

            if(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
                // Don't select the Basic Render Driver adapter.
                continue;
            }

            break;
        }
    }

    if(!possible_adapter) {
        ENGINE_CRITICAL("Could not locate IDXGIAdapter4");
        return;
    }

    *adapter = possible_adapter.Detach();
}

最后,客户端的Layer 子类:

class ExampleLayer : public Layer {
public:
    void on_event(Event &event) override {
        if(event._type == EventType::KeyPressed) {
            if(event.key() == KEY_ESCAPE) {
                CLIENT_INFO("Closing the window");
                _app->window()->trigger_close_event();
            }
        }
    }

    void update() {
        _render_api->set_clear_color(clear_color);
        _render_api->clear();
    }

    ExampleLayer(Application * app) :
        Layer("Gameplay"),
        _app{ app }
    {
        _render_api = RenderAPI::create();
        _render_api->set_context(_app->window()->context());
    }

    ~ExampleLayer() {
        delete _render_api;
    }

private:
    Application *_app;
    RenderAPI *_render_api;

    float clear_color[4]{ 0.1f, 0.15f, 0.1f, 1.0f };
};

很抱歉没有更多内容可以继续。 Visual Studio 的调试输出对与 DirectX 相关的问题保持沉默(尽管并非总是如此 - 它帮助我昨天发现了一个错误,所以至少我知道它有效)。我不确定我还能做些什么来收集证据或寻找线索,所以我来了。

提前感谢所有愿意阅读本文的人,更不用说那些提供帮助的高尚灵魂了。 =)

【问题讨论】:

    标签: c++ winapi directx


    【解决方案1】:

    正如人们在这种情况下所期望的那样,这是一个简单的错误。具体来说,我从未在 DX11 上下文中设置 _output_size 成员变量。

    void DX11Context::init() {
        create_device_resources();
        ::GetWindowRect(_window, &_output_size);
        create_window_dependent_resources();
    }
    

    这个函数的第二行完全解决了这个问题。 =)

    【讨论】:

      猜你喜欢
      • 2018-01-20
      • 2020-10-29
      • 2012-10-07
      • 1970-01-01
      • 1970-01-01
      • 2019-12-24
      • 1970-01-01
      • 2018-10-26
      • 2016-08-21
      相关资源
      最近更新 更多