异常:设备为 nullptr。 D3D11 中的读取访问冲突

Exception: device is nullptr. read access violation in D3D11

大家。

两年前写过一个3D小场景,只有1700行源代码(不含.h文件)。现在回到 GitHub 和 运行 我的应用程序,我在调试模式下发现了一个非常有趣的错误。 Debbuger 在为顶点缓冲区调用 CreateBuffer 时抛出异常:

auto result = device->CreateBuffer(&vertex_buffer_desc, &vertex_data, &vertex_buffer);
        if(FAILED(result))
            return false;

基本上,调试器说 (d3d11) 设备是 nullptr,但事实并非如此,因为 运行 在非调试模式下,一切正常。但是当我在创建设备之前定义 UINT create_device_flag = D3D11_CREATE_DEVICE_DEBUG; 时,我抛出了这个异常:读取访问冲突。设备是 nullptr。几天过去了,我还是找不到问题所在,因为指针定义的顺序是正确的。

这里是Main.cpp:

#include <StdAfx.h>

#include <Window.h>
#include <FPSCamera.h>

#include <DirectInput8.h>
#include <D3D11Renderer.h>

#include <Terrain.h>
#include <TerrainShader.h>

using namespace bm;

int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int)
{
    auto resource_directory_name = L"..\..\..\Resource\"s;
    auto terrain_name = L"terrain"s;

    auto dds_file_extension = L".dds"s;
    auto hlsl_file_extension = L".hlsl"s;

    std::wstring resources[] = {resource_directory_name + L"heightmap.bmp"s,
                                resource_directory_name + terrain_name + dds_file_extension,
                                resource_directory_name + terrain_name + L"_bump"s + dds_file_extension,
                                resource_directory_name + terrain_name + L"_vs"s + hlsl_file_extension,
                                resource_directory_name + terrain_name + L"_ps"s + hlsl_file_extension};

    constexpr auto ENABLE_FULLSCREEN = false;
    constexpr auto ENABLE_VSYNC = false;

    constexpr auto SCREEN_WIDTH = 1366;
    constexpr auto SCREEN_HEIGHT = 768;

    auto window = std::make_shared<bm::Window>(SCREEN_WIDTH, SCREEN_HEIGHT, ENABLE_FULLSCREEN);
    window->registerClass();
    window->create();

    auto d3d11_renderer = std::make_shared<bm::D3D11Renderer>(SCREEN_WIDTH, SCREEN_HEIGHT, ENABLE_FULLSCREEN, window->getHandle(), ENABLE_VSYNC);

    // Exception is thrown in the following ponter, but d3d11 device should be already initialized.
    auto terrain = std::make_shared<bm::Terrain>(d3d11_renderer->getDevice(), resources[0].c_str(), resources[1].c_str(), resources[2].c_str());
    auto terrain_shader = std::make_shared<bm::TerrainShader>(d3d11_renderer->getDevice(), resources[3].c_str(), resources[4].c_str());

    auto fps_camera = std::make_shared<bm::FPSCamera>(static_cast<float>(SCREEN_WIDTH),  static_cast<float>(SCREEN_HEIGHT));
    fps_camera->setPosition(500.f, 75.f, 400.f);
    fps_camera->setRotation(20.f, 30.f, 0.f); // in degree.

    auto direct_input_8 = std::make_shared<bm::DirectInput8>(window->getHandle());

    constexpr float CLEAR_COLOR[] = {0.84f, 0.84f, 1.f, 1.f};

    while(window->update())
    {
        direct_input_8->update(fps_camera->getMoveLeftRight(), fps_camera->getMoveBackForward(), fps_camera->getYaw(), fps_camera->getPitch());
        fps_camera->update();

        d3d11_renderer->clearScreen(CLEAR_COLOR);

        terrain->render(d3d11_renderer->getDeviceContext());

        terrain_shader->render(d3d11_renderer->getDeviceContext(),
                               terrain->getIndexCount(),
                               fps_camera->getWorld(),
                               fps_camera->getView(),
                               fps_camera->getProjection(),
                               {0.82f, 0.82f, 0.82f, 1.0f},
                               {-0.0f, -1.0f, 0.0f},
                               terrain->getColorTexture(),
                               terrain->getNormalMapTexture());

        d3d11_renderer->swapBuffers();
    }

    return 0;
}

P.S。 我知道网站上有一篇 6 年前的文章:CreateBuffer throwing an "Access violation reading location" 但它几乎无法解释任何事情,因为我没有全局变量和指针。我想纠正我的旧错误,所以如果需要,我很乐意指定任何内容。

抱歉,这都是抽象代码,所以我们看不到您调用的实际位置 D3D11CreateDevice

也就是说,您描述的症状听起来像是您的操作系统上没有安装正确的 Debug Device SDK 层。您也可能无法从 D3D11CreateDevice 检查 FAILED HRESULT。

DWORD createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
D3D_FEATURE_LEVEL fl;
HRESULT hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE,
    nullptr, createDeviceFlags, nullptr,
    0, D3D11_SDK_VERSION, &device, &fl, &context );
if (FAILED(hr))
    ...

在没有安装 Debug Device SDK 层的系统上,这将在 _DEBUG 中失败。

在 Windows 8.x 或 Windows 10 上,安装 legacy DirectX SDK 不会安装任何调试运行时 .

对于 Windows 8.x,您可以通过安装 Windows 8.x SDK 或 Windows 10 SDK 来获取 Direct3D 11 调试运行时。

对于 Windows10,您可以通过安装名为 Graphics Tools[=38] 的 Windows 可选功能 来获得 Direct3D 调试运行时=].对于 Windows 10,这是 版本特定的 因此请确保您已启用它,以便它具有与您的版本相匹配的版本。参见 this blog post

Anatomy of Direct3D 11 Create Device