Direct3D 11 IDXGISwapChain::Present() 在 Windows 10 窗口模式下每次调用时都会阻塞
我正在尝试 Windows 10、Direct3D 11 和 IDXGISwapChain::Present() 函数。 当我在全屏模式下调用 Present 函数时,它会对命令进行排队,并且直到大约第四次调用时才会阻塞,这是预期的行为。
但是,当我在窗口模式下调用相同的函数时,它在第一次调用和每个后续调用时都会阻塞。
我还尝试将 IDXGIDevice1::SetMaximumFrameLatency 函数设置为不同的值,但在窗口模式下该函数似乎被忽略。在全屏模式下该功能工作正常。
我想知道是否有一种方法可以解决在保持窗口模式时每次调用时阻塞的 Present 函数。
这是我用来对上述问题进行单元测试的 C++ 代码:
#include <windows.h>
#include <windowsx.h>
#include <d3d11_1.h>
#include <wrl.h>
#include <dxgi1_2.h>
#include <iostream>
#include <chrono>
#include <stdio.h>
#include <list>
#pragma comment (lib, "d3d11.lib")
#define SCREEN_WIDTH 2560
#define SCREEN_HEIGHT 1440
using namespace std;
using chrono::milliseconds;
using chrono::high_resolution_clock;
using chrono::duration_cast;
using chrono::duration;
using Microsoft::WRL::ComPtr;
ComPtr<IDXGISwapChain> swapchain;
ComPtr<ID3D11Device> dev;
ComPtr<ID3D11DeviceContext> devcon;
ID3D11RenderTargetView* backbuffer;
list<milliseconds> times;
void initConsole()
{
AllocConsole();
FILE* pstdin, * pstdout, * pstderr;
freopen_s(&pstdin, "CONIN$", "r", stdin);
freopen_s(&pstdout, "CONOUT$", "w", stdout);
freopen_s(&pstderr, "CONOUT$", "w", stderr);
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
void InitD3D(HWND hWnd)
{
DXGI_SWAP_CHAIN_DESC scd = {};
scd.BufferCount = 2;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferDesc.Width = SCREEN_WIDTH;
scd.BufferDesc.Height = SCREEN_HEIGHT;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hWnd;
scd.SampleDesc.Count = 1;
scd.Windowed = true;
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &scd, &swapchain, &dev, nullptr, &devcon);
//swapchain->SetFullscreenState(true, nullptr);
ComPtr<ID3D11Texture2D> pBackBuffer;
swapchain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
dev->CreateRenderTargetView(pBackBuffer.Get(), nullptr, &backbuffer);
devcon->OMSetRenderTargets(1, &backbuffer, nullptr);
D3D11_VIEWPORT viewport = {};
viewport.Width = SCREEN_WIDTH;
viewport.Height = SCREEN_HEIGHT;
devcon->RSSetViewports(1, &viewport);
}
void RenderFrame(void)
{
float color[] = { 0.0f, 0.2f, 0.4f, 1.0f };
devcon->ClearRenderTargetView(backbuffer, color);
auto t1 = high_resolution_clock::now();
swapchain->Present(1, 0);
auto t2 = high_resolution_clock::now();
auto time = duration_cast<milliseconds>(t2 - t1);
times.push_back(time);
}
void CleanD3D(void)
{
swapchain->SetFullscreenState(false, nullptr);
backbuffer->Release();
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
initConsole();
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.lpszClassName = L"WindowClass";
RegisterClassEx(&wc);
RECT wr = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
auto hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our First Direct3D Program", WS_OVERLAPPEDWINDOW, 0, 0, wr.right - wr.left, wr.bottom - wr.top, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
InitD3D(hWnd);
MSG msg;
for (int i = 0; i < 20; ++i)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT) {
break;
}
}
RenderFrame();
}
CleanD3D();
for (auto time : times)
{
cout << time << " ";
}
cin.ignore();
return 0;
}
如果运行上面的代码,它会输出:
4ms 8ms 15ms 16ms 14ms 16ms 14ms 16ms 15ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms
但如果取消注释 SetFullscreenState 行,则会输出:
1ms 0ms 0ms 13ms 16ms 49ms 33ms 50ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms
在窗口情况下,它会从第一次调用开始阻塞。在全屏情况下,它从第四次调用开始阻塞。
编辑:添加示例代码。
I am experimenting with Windows 10, Direct3D 11 and the IDXGISwapChain::Present() function.
When I call the Present function in fullscreen mode, it queues commands and doesn't block until about the 4th call, which is the expected behaviour.
However, when I call the same function in windowed mode, it is blocking at the 1st call and every subsequent call.
I also tried setting the IDXGIDevice1::SetMaximumFrameLatency function to different values, but in windowed mode this function seems to be ignored. In fullscreen mode the function works ok.
I wonder if there is a way around the Present function blocking at every call while staying in windowed mode.
Here is the C++ code i'm using to unit test the issue above:
#include <windows.h>
#include <windowsx.h>
#include <d3d11_1.h>
#include <wrl.h>
#include <dxgi1_2.h>
#include <iostream>
#include <chrono>
#include <stdio.h>
#include <list>
#pragma comment (lib, "d3d11.lib")
#define SCREEN_WIDTH 2560
#define SCREEN_HEIGHT 1440
using namespace std;
using chrono::milliseconds;
using chrono::high_resolution_clock;
using chrono::duration_cast;
using chrono::duration;
using Microsoft::WRL::ComPtr;
ComPtr<IDXGISwapChain> swapchain;
ComPtr<ID3D11Device> dev;
ComPtr<ID3D11DeviceContext> devcon;
ID3D11RenderTargetView* backbuffer;
list<milliseconds> times;
void initConsole()
{
AllocConsole();
FILE* pstdin, * pstdout, * pstderr;
freopen_s(&pstdin, "CONINquot;, "r", stdin);
freopen_s(&pstdout, "CONOUTquot;, "w", stdout);
freopen_s(&pstderr, "CONOUTquot;, "w", stderr);
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
void InitD3D(HWND hWnd)
{
DXGI_SWAP_CHAIN_DESC scd = {};
scd.BufferCount = 2;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferDesc.Width = SCREEN_WIDTH;
scd.BufferDesc.Height = SCREEN_HEIGHT;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hWnd;
scd.SampleDesc.Count = 1;
scd.Windowed = true;
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION, &scd, &swapchain, &dev, nullptr, &devcon);
//swapchain->SetFullscreenState(true, nullptr);
ComPtr<ID3D11Texture2D> pBackBuffer;
swapchain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
dev->CreateRenderTargetView(pBackBuffer.Get(), nullptr, &backbuffer);
devcon->OMSetRenderTargets(1, &backbuffer, nullptr);
D3D11_VIEWPORT viewport = {};
viewport.Width = SCREEN_WIDTH;
viewport.Height = SCREEN_HEIGHT;
devcon->RSSetViewports(1, &viewport);
}
void RenderFrame(void)
{
float color[] = { 0.0f, 0.2f, 0.4f, 1.0f };
devcon->ClearRenderTargetView(backbuffer, color);
auto t1 = high_resolution_clock::now();
swapchain->Present(1, 0);
auto t2 = high_resolution_clock::now();
auto time = duration_cast<milliseconds>(t2 - t1);
times.push_back(time);
}
void CleanD3D(void)
{
swapchain->SetFullscreenState(false, nullptr);
backbuffer->Release();
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
initConsole();
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.lpszClassName = L"WindowClass";
RegisterClassEx(&wc);
RECT wr = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
auto hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our First Direct3D Program", WS_OVERLAPPEDWINDOW, 0, 0, wr.right - wr.left, wr.bottom - wr.top, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
InitD3D(hWnd);
MSG msg;
for (int i = 0; i < 20; ++i)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT) {
break;
}
}
RenderFrame();
}
CleanD3D();
for (auto time : times)
{
cout << time << " ";
}
cin.ignore();
return 0;
}
If you run the code above, it outputs:
4ms 8ms 15ms 16ms 14ms 16ms 14ms 16ms 15ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms
But if you uncomment the SetFullscreenState line, then it outputs:
1ms 0ms 0ms 13ms 16ms 49ms 33ms 50ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 16ms 15ms 16ms 16ms
In the windowed case, it blocks from the first call onwards. In the fullscreen case, it blocks from the fourth call onwards.
EDIT: Added sample code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论