【问题标题】:Getting 0xC000041D crash in simple directX12 application在简单的 directX12 应用程序中出现 0xC000041D 崩溃
【发布时间】:2021-01-30 06:18:57
【问题描述】:

我有一个简单的 Window 类,我使用 WM_PAINT 函数清除渲染目标视图并显示最终图像。所以没有太多内容,但我在FLOAT clearColor[] = { 0.2f, 0.4f, 0.6f, 1.0f }; 线上遇到了崩溃,上面写着:0xC000041D: An在用户回调期间遇到未处理的异常。 这是窗口类:

class Win32Window : public WindowsWindow
{
public:
    Win32Window(const WindowProps& props);
    virtual ~Win32Window();
    void OnUpdate() override;
    unsigned int GetWidth() const override { return m_Data.Width; }
    unsigned int GetHeight() const override { return m_Data.Height; }
    void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; }
    void SetVSync(bool enabled) override;
    bool IsVSync() const override;
    virtual void* GetNativeWindow() const override { return m_Window; }
private:
    HWND m_Window;
    RECT m_WindowRect;
    HINSTANCE m_hInst;
    LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
    static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
    virtual void Init(const WindowProps& props);
    virtual void Shutdown();
};

定义:

Win32Window::Win32Window(const WindowProps& props)
{
    HZ_PROFILE_FUNCTION();
    Init(props);
}
Win32Window::~Win32Window()
{
    HZ_PROFILE_FUNCTION();
    Shutdown();
}
void Win32Window::Init(const WindowProps& props)
{
    m_hInst = GetModuleHandle(0);
    m_Data.WideCharacterTitle = props.WideCharacterTitle;
    m_Data.Width = props.Width;
    m_Data.Height = props.Height;
    HZ_CORE_INFO("Creating window {0} ({1}, {2})",props.StringTypeTitle, props.Width, props.Height);

    WNDCLASSEXW windowClass = {};
    windowClass.cbSize = sizeof(WNDCLASSEX);
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    windowClass.lpfnWndProc = &WndProc;
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hInstance = m_hInst;
    windowClass.hIcon = ::LoadIcon(m_hInst, NULL);
    windowClass.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    windowClass.lpszMenuName = NULL;
    windowClass.lpszClassName = m_Data.WideCharacterTitle;
    windowClass.hIconSm = ::LoadIcon(m_hInst, NULL);
    static ATOM atom = ::RegisterClassExW(&windowClass);
    assert(atom > 0);

    int screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = ::GetSystemMetrics(SM_CYSCREEN);
    RECT windowRect = { 0, 0, static_cast<LONG>(m_Data.Width), static_cast<LONG>(m_Data.Height) };
    ::AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);
    int windowWidth = windowRect.right - windowRect.left;
    int windowHeight = windowRect.bottom - windowRect.top;
    int windowX = std::max<int>(0, (screenWidth - windowWidth) / 2);
    int windowY = std::max<int>(0, (screenHeight - windowHeight) / 2);
    m_Window = ::CreateWindowExW(NULL, m_Data.WideCharacterTitle, m_Data.WideCharacterTitle, WS_OVERLAPPEDWINDOW, windowX, windowY, windowWidth, windowHeight, NULL, NULL, m_hInst, this);
    assert(m_Window && "Failed to create window");
    m_Context = GraphicsContext::Create(m_Window);
    m_Context->Init();
    ::ShowWindow(m_Window, SW_SHOW);
    ::UpdateWindow(m_Window);
}
LRESULT Win32Window::MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
       m_Context->RenderWindow();
        break;
    case WM_DESTROY:
        ::PostQuitMessage(0);
        break;
    default:
        return ::DefWindowProcW(hwnd, message, wParam, lParam);
    }
}
LRESULT Win32Window::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    Win32Window* instance;
    if (message == WM_CREATE)
    {
        instance = (Win32Window*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
        instance->m_Window = hwnd;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)instance);
    }
    else
    {
        instance = (Win32Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    }
    if (instance) 
    {
        return instance->MainWndProc(hwnd, message, wParam, lParam);
    }
    return ::DefWindowProcW(hwnd, message, wParam, lParam);
}
void Win32Window::Shutdown()
{
    DestroyWindow(m_Window);
    m_Window = nullptr;
}
void Win32Window::OnUpdate()
{
    m_Context->Update();
}
void Win32Window::SetVSync(bool enabled)
{
    m_Data.VSync = enabled;
}
bool Win32Window::IsVSync() const
{
    return m_Data.VSync;
}

m_Context 来自父类,还有 WindowProps 有一些用于初始化窗口的数据。m_Context->RenderWindow() 的定义:

m_CommnadQueue = D3D12Core::Get().GetCommandQueue(D3D12_COMMAND_LIST_TYPE_DIRECT);
    auto m_CommandList = m_CommnadQueue->GetCommandList();
    m_CurrentBackBufferIndex = m_SwapChain->GetCurrentBackBufferIndex();
    auto m_BackBuffer = D3D12Core::Get().m_BackBuffers[m_CurrentBackBufferIndex];
    auto m_RTVDescriptorSize = D3D12Core::Get().GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
    {
        CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_BackBuffer.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
        m_CommandList->ResourceBarrier(1, &barrier);
        FLOAT clearColor[] = { 0.2f, 0.4f, 0.6f, 1.0f };
        CD3DX12_CPU_DESCRIPTOR_HANDLE rtv(m_RTVDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), m_CurrentBackBufferIndex, m_RTVDescriptorSize);
        m_CommandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr);
    }
    {
        CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_BackBuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
        m_CommandList->ResourceBarrier(1, &barrier);
        m_CommandList->Close();
        ID3D12CommandList* const commandLists[] =
        {
            m_CommandList.Get()
        };
        D3D12Core::Get().m_FenceValues[m_CurrentBackBufferIndex] = m_CommnadQueue->ExecuteCommandList(m_CommandList);
        UINT syncInterval = m_VSync;
        UINT presentFlags = m_TearingSupport && !m_VSync ? DXGI_PRESENT_ALLOW_TEARING : 0;
        m_SwapChain->Present(syncInterval, presentFlags);
        m_CommnadQueue->WaitForFenceValue(D3D12Core::Get().m_FenceValues[m_CurrentBackBufferIndex]);
    }

如果还有什么我应该提出的问题,请告诉我。如果你愿意,你可以因为我的愚蠢而对我大喊大叫,但请帮助我,谢谢。 编辑: 调试层:

#ifdef _DEBUG
        ComPtr<ID3D12Debug> debugInterface;
        if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugInterface))))
        {
            debugInterface->EnableDebugLayer();
        }
#endif

【问题讨论】:

  • 关于你之前的问题,我怀疑调试层没有发现你的堆初始化错误,所以我测试了它,调试层实际上显示了一个错误。我的观点是确保您的调试层确实有效。如果您的调试层周围有#ifdef _DEBUG,请确保您在调试模式下运行。关于您的问题,尽管 clearColor 行显示崩溃,但它可能是上面带有资源障碍的行。我的第一个猜测是,也许你在录制之前没有重置你的命令列表?但请确保调试层已启用并正常工作。
  • 我也没有看到对 OMSetRenderTargets 的调用,如果您添加带有图形内容的代码可能会更好(您可能可以删除窗口类,我怀疑它有问题)。也许考虑不使用 WM_PAINT 来渲染gamedev.stackexchange.com/questions/12901/wm-paint-and-direct3d
  • 感谢您的提示。解决此崩溃后,我将停止使用 WM_PAINT。首先,我将调试层放置在错误的地方。现在我把它放在它被正确调用的地方,现在崩溃消失了,但是我得到一个白屏,调试层没有任何错误。我在教程中测试了这些代码,它运行良好,但现在当我尝试在引擎中使用它们时,我总是得到一个白屏。我确定代码本身没有任何问题,所以我很困惑我做错了什么。
  • 现在我将调试层移到了比以前更合理的位置,现在我又遇到了同样的崩溃。我有一个命令队列类,它返回命令列表,在它返回之前,重置它。
  • 只是为了检查一下,您能否使用上一个问题中的描述符堆重现您的错误,并确保调试层按应有的方式输出消息?我猜你知道消息在哪里输出。像这样玩猜谜游戏很难,所以我想确保你正确启用它。您还应该在帖子中发布有关调试层的代码。

标签: crash unhandled-exception directx-12


【解决方案1】:

WaitForFenceValue 在每个 Present 之后是在 DirectX 12 中渲染的一种极其缓慢的方式。您通常应该向 GPU 提示 2-3 帧,而不是强制 GPU 停止每一帧。拥有您的WndProc 块也很成问题,这就是这里发生的情况。您的WndProc 应始终快速完成,因为对于大多数应用程序而言,每秒处理数百条消息。

通常 Direct3D 渲染不在WM_PAINT 内完成。有关常见渲染循环的一些示例,请参阅GitHub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-12
    • 1970-01-01
    • 1970-01-01
    • 2016-07-09
    • 2015-12-31
    • 1970-01-01
    相关资源
    最近更新 更多