Отладка кода DirectX

Я изучал DirectX, следуя некоторым учебникам, но каждый раз, когда я что-то набираю сам, DirectX не работает. Вот пример последней ошибки, которую я не могу исправить после нескольких часов исследований:

     //Header.h
static HWND hWnd;
static IDXGISwapChain* swapChain;
static ID3D11Device* dev;
static ID3D11DeviceContext* devCon;
static ID3D11RenderTargetView* renderTarget;

//DirectX.cpp
bool InitD3D11(HINSTANCE hInst)
{
HRESULT hr;
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = 800;
bufferDesc.Height = 600;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hWnd;
swapChainDesc.Windowed = true;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &dev, NULL, &devCon);

ID3D11Texture2D* backBuffer;
hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
hr = dev->CreateRenderTargetView(backBuffer, NULL, &renderTarget);
backBuffer->Release();
devCon->OMSetRenderTargets(1, &renderTarget, NULL);
return true;
}
//This is not the full code - I linked directx and lib files and stuff like that.
//If I copy and paste code from tutorials, everything runs fine

Всякий раз, когда я вызываю InitD3d11, я получаю сообщение об ошибке, в котором говорится, что swapChain является пустым указателем. Я предполагаю, что bufferDesc и / или swapChainDesc содержат некоторые недопустимые данные, но компилятор не может дать мне никакой подсказки, что ответственно за ошибку. Может кто-нибудь показать мне, как отследить и исправить ошибки, как это? Благодарю.

1

Решение

Вы не проверяете HRESULT значения, так что вы пропускаете все обработки ошибок. Для программирования COM, даже с Direct3D, вы должен проверить HRESULT каждого метода, который может вернуть один для сбоя. Если возвращаемое значение безопасно игнорировать, оно возвращает void вместо.

проверка HRESULT Значения в старых программах C / C ++ делается либо с FAILED или же SUCCEEDED макросы.

hr = D3D11CreateDeviceAndSwapChain( /* ... */ *);
if (FAILED(hr))
return false;

В большинстве случаев провал HRESULT рассматривается как «быстрый сбой» или фатальная ошибка. Другими словами, программа не может продолжить работу, если вызов не удался.

В других случаях может быть специальная обработка для восстановления после ошибки, возможно, с использованием других параметров. Для подробного примера этого см. Анатомия Direct3D 11 Создать устройство.

В старых образцах Microsoft, основанных на наследии DXUT Framework, обработка ошибок была сделана с помощью макросов, таких как V или же V_RETURN который сделал некоторое отслеживание или отслеживание & фатальный выход.

В современных примерах C ++ мы фактически используем помощник DX::ThrowIfFailed который генерирует исключение C ++ при неудачном HRESULT для fast-fail сценарий. Это делает код более удобным и читабельным:

DX::ThrowIfFailed(
D3D11CreateDeviceAndSwapChain( /* ... */ *)
);

Сама функция определяется как:

#include <exception>

namespace DX
{
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
// Set a breakpoint on this line to catch DirectX API errors
throw std::exception();
}
}
}

Ваша программа должна быть скомпилирована с /EHsc который уже находится в шаблонах Visual Studio по умолчанию. Увидеть эта страница темы Больше подробностей.

Из приведенного выше фрагмента кода вы читаете довольно старый учебник. Многое изменилось даже для разработки DirectX 11, и большинство из этих старых руководств приведут к путанице. Поскольку вы новичок в DirectX, я рекомендую вам взглянуть на Набор инструментов DirectX и учебные пособия там первый Затем вы можете вернуться к старым учебникам с лучшим пониманием «современного» Direct3D и иметь возможность извлекать более релевантную информацию из старого материала.

После проверки всех HRESULTS, Следующее, что вы должны сделать, это включить уровень отладки Direct3D, который предоставляет дополнительную информацию об отладке в окне вывода.

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

hr = D3D11CreateDeviceAndSwapChain(
nullptr, D3D_DRIVER_TYPE_HARDWARE,
nullptr, createDeviceFlags, nullptr,
0, D3D11_SDK_VERSION,
&swapChainDesc, &swapChain, &dev, nullptr, &devCon);

Вы заметите, что я использую C ++ 11 nullptr который поддерживается в Visual C ++ 2010 или более поздней версии вместо старой школы NULL, Это потому, что это напечатано. Вы использовали NULL в двух местах в вашей версии, где параметр на самом деле не был указателем, это число.

Благодаря этому вы получите множество отзывов об основных ошибках, которые помогут диагностировать, почему в вашем конкретном случае создание свопчейна не удается. Я подозреваю, что это потому, что вы предоставляете некоторые значения, которые имеют смысл только для «эксклюзивного полноэкранного режима», а не для оконного режима (bufferDesc.RefreshRate). Вместо этого попробуйте:

DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferDesc.Width = 800;
swapChainDesc.BufferDesc.Height = 600;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hWnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

Это работает на VS 2013 или VS 2015, которая инициализирует структуру до 0 с ={}; синтаксис. На старых компиляторах вам нужно использовать ZeroMemory как вы делали в коде старой школы выше.

Обратите внимание, что с помощью D3D11_CREATE_DEVICE_DEBUG произойдет сбой в системе, в которой отсутствует отладочное устройство, предназначенное только для разработчиков. Сведения о том, где вы получаете слой устройства отладки, отличаются в зависимости от того, какую версию Windows вы используете. Увидеть Трюки с отладочным слоем Direct3D SDK который имеет небольшую сводную таблицу внизу.

Также, отладка при наличии неинициализированных переменных — огромная боль. В частности, неинициализированные указатели могут быть огромной тратой времени. Это помогло бы, если бы вы сделали следующее:

static HWND hWnd = nullptr;
static IDXGISwapChain* swapChain = nullptr;
static ID3D11Device* dev = nullptr;
static ID3D11DeviceContext* devCon = nullptr;
static ID3D11RenderTargetView* renderTarget = nullptr;

Более того, вместо использования сырых указателей для ваших COM-объектов, вы должны использовать смарт-указатель C ++, например Microsoft::WRL::ComPtr, Увидеть эта страница темы для деталей. Наши современные образцы используют его, и он работает для классических настольных приложений Win32, а также для приложений Магазина Windows, UWP и Xbox One, поскольку это всего лишь шаблон C ++.

3

Другие решения

Других решений пока нет …