【发布时间】:2022-01-17 05:11:57
【问题描述】:
我第一次尝试学习DX11,目标是将API添加到my rendition的The 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();
}
最后,Window 的 RenderingContext 交换缓冲区:
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 相关的问题保持沉默(尽管并非总是如此 - 它帮助我昨天发现了一个错误,所以至少我知道它有效)。我不确定我还能做些什么来收集证据或寻找线索,所以我来了。
提前感谢所有愿意阅读本文的人,更不用说那些提供帮助的高尚灵魂了。 =)
【问题讨论】: