DirectX 窗口在多窗口和多线程下快速闪烁
Posted
技术标签:
【中文标题】DirectX 窗口在多窗口和多线程下快速闪烁【英文标题】:DirectX window flickers rapidly with multi-windows and multi-threading 【发布时间】:2019-07-18 01:21:41 【问题描述】:我正在尝试创建一个基本的基于 DirectX 的 C++ Windows 应用程序,它可以在两个单独的窗口上显示两个图像。我可以轻松地显示单个图像。但是,我必须使用多线程——每个窗口一个线程——因为同时更新窗口的内容至关重要。
然而,目前我只专注于为两个静态图像实现多线程,每个窗口每个线程一个图像。然而,我遇到的问题是两个窗口都有些快速/随机闪烁(它们大多闪烁为黑色,偶尔闪烁绿色)。
我怀疑这可能与我将多线程与 DirectX 相结合的方式有关,据我所知,必须使用互斥锁,因此不会同时调用设备或上下文。因此,我怀疑当窗口重新渲染时,它们的内容在某些时间段内被另一个线程阻塞。
但是,我不确定是否是这种情况,如果是,我不知道如何解决。
这是主游戏循环:
//
// Game.cpp
//
#include "pch.h"
#include "Game.h"
#include <synchapi.h>
extern void ExitGame();
using namespace DirectX;
using Microsoft::WRL::ComPtr;
ComPtr<ID3D11Texture2D> textures[4];
Game::Game() noexcept(false)
m_deviceResources = std::make_unique<DX::DeviceResources>(DXGI_FORMAT_R10G10B10A2_UNORM,
DXGI_FORMAT_D32_FLOAT, 2, D3D_FEATURE_LEVEL_10_0, DX::DeviceResources::c_EnableHDR);
m_deviceResources->RegisterDeviceNotify(this);
m_hdrScene[0] = std::make_unique<DX::RenderTexture>(DXGI_FORMAT_R16G16B16A16_FLOAT);
m_hdrScene[1] = std::make_unique<DX::RenderTexture>(DXGI_FORMAT_R16G16B16A16_FLOAT);
// Initialize the Direct3D resources required to run.
void Game::Initialize(HWND windows[], int width, int height)
m_deviceResources->SetWindow(0, windows[0], width, height);
m_deviceResources->SetWindow(1, windows[1], width, height);
m_deviceResources->CreateDeviceResources();
CreateDeviceDependentResources();
m_deviceResources->CreateWindowSizeDependentResources(0);
m_deviceResources->CreateWindowSizeDependentResources(1);
CreateWindowSizeDependentResources();
// TODO: Change the timer settings if you want something other than the default variable timestep mode.
// e.g. for 60 FPS fixed timestep update logic, call:
m_timer.SetFixedTimeStep(true);
m_timer.SetTargetElapsedSeconds(1.0 / 60);
void Game::CreateThreads()
std::thread one(&Game::Render, this, 0);
std::thread two(&Game::Render, this, 1);
one.join();
two.join();
#pragma region Frame Update
// Executes the basic game loop.
void Game::Tick()
m_timer.Tick([&]() Update(m_timer); );
CreateThreads();
// Updates the world.
void Game::Update(DX::StepTimer const& timer)
float elapsedTime = float(timer.GetElapsedSeconds());
char buff[128] = ;
sprintf_s(buff, "%f\n", elapsedTime);
OutputDebugStringA(buff);
// TODO: Add your game logic here.
#pragma endregion
std::mutex mut;
#pragma region Frame Render
// Draws the scene.
void Game::Render(int i)
// Don't try to render anything before the first Update.
if (m_timer.GetFrameCount() == 0)
return;
mut.lock();
Clear(i);
m_deviceResources->PIXBeginEvent(L"Render");
auto context = m_deviceResources->GetD3DDeviceContext();
m_spriteBatch = std::make_unique<SpriteBatch>(context);
mut.unlock();
// TODO: Add your rendering code here.
D3D11_SHADER_RESOURCE_VIEW_DESC desc2 = ;
desc2.Format = DXGI_FORMAT_R16G16B16A16_UNORM;
desc2.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
desc2.Texture2D.MipLevels = 1;
ComPtr<ID3D11ShaderResourceView> shaderResourceView;
mut.lock();
auto hr = m_deviceResources->m_d3dDevice->CreateShaderResourceView(
textures[i].Get(),
&desc2,
shaderResourceView.GetAddressOf()
);
try
m_spriteBatch->Begin();
m_spriteBatch->Draw(shaderResourceView.Get(), XMFLOAT2(0, 0));
m_spriteBatch->End();
catch (std::exception& e)
auto msg = e.what();
throw std::exception(msg);
m_deviceResources->PIXEndEvent();
auto renderTarget = m_deviceResources->GetRenderTargetView(i);
context->OMSetRenderTargets(1, &renderTarget, nullptr);
mut.unlock();
m_toneMap[i]->SetOperator(ToneMapPostProcess::None);
m_toneMap[i]->SetTransferFunction(ToneMapPostProcess::ST2084);
m_toneMap[i]->SetST2084Parameter(10000.f);
mut.lock();
m_toneMap[i]->Process(context);
ID3D11ShaderResourceView* nullsrv[] = nullptr ;
context->PSSetShaderResources(0, 1, nullsrv);
mut.unlock();
// Show the new frame.
m_deviceResources->Present(i);
// Helper method to clear the back buffers.
void Game::Clear(int i)
m_deviceResources->PIXBeginEvent(L"Clear");
// Clear the views.
auto context = m_deviceResources->GetD3DDeviceContext();
auto renderTarget = m_hdrScene[i]->GetRenderTargetView();
auto depthStencil = m_deviceResources->GetDepthStencilView();
XMVECTORF32 color;
auto actual = FXMVECTOR( 0, 0, 0, 0 );
color.v = XMColorSRGBToRGB(actual);
context->ClearRenderTargetView(renderTarget, color);
context->ClearDepthStencilView(depthStencil, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
context->OMSetRenderTargets(1, &renderTarget, depthStencil);
// Set the viewport.
auto viewport = m_deviceResources->GetScreenViewport();
context->RSSetViewports(1, &viewport);
m_deviceResources->PIXEndEvent();
#pragma endregion
void Game::OnWindowMoved()
auto r = m_deviceResources->GetOutputSize();
m_deviceResources->WindowSizeChanged(0, r.right, r.bottom);
m_deviceResources->WindowSizeChanged(1, r.right, r.bottom);
void Game::OnWindowSizeChanged(int index, int width, int height)
if (!m_deviceResources->WindowSizeChanged(index, width, height))
return;
CreateWindowSizeDependentResources();
// TODO: Game window is being resized.
// Properties
void Game::GetDefaultSize(int& width, int& height) const
// TODO: Change to desired default window size (note minimum size is 320x200).
width = 800;
height = 600;
#pragma region Direct3D Resources
// These are the resources that depend on the device.
void Game::CreateDeviceDependentResources()
auto device = m_deviceResources->GetD3DDevice();
cv::directx::ocl::initializeContextFromD3D11Device(device);
// TODO: Initialize device dependent objects here (independent of window size).
for (int i = 0; i < 2; i++)
m_hdrScene[i]->SetDevice(device);
m_toneMap[i] = std::make_unique<ToneMapPostProcess>(device);
m_toneMap[i]->SetOperator(ToneMapPostProcess::None);
m_toneMap[i]->SetTransferFunction(ToneMapPostProcess::ST2084);
for (int i = 0; i < 4; i++)
textures[i] = this->getImagesAsTextures()[i];
// Allocate all memory resources that change on a window SizeChanged event.
void Game::CreateWindowSizeDependentResources()
auto size = m_deviceResources->GetOutputSize();
for (int i = 0; i < 2; i++)
m_hdrScene[i]->SetWindow(size);
m_toneMap[i]->SetHDRSourceTexture(m_hdrScene[i]->GetShaderResourceView());
void Game::OnDeviceLost()
// TODO: Add Direct3D resource cleanup here.
m_hdrScene[0]->ReleaseDevice();
m_hdrScene[1]->ReleaseDevice();
m_toneMap[0].reset();
m_toneMap[1].reset();
void Game::OnDeviceRestored()
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
CreateWindowSizeDependentResources();
#pragma endregion
我也收到此警告,但我不确定它的含义:D3D11 WARNING: ID3D11DeviceContext::DrawIndexed: The Pixel Shader expects a Render Target View bound to slot 0, but none is bound. This is OK, as writes of an unbound Render Target View are discarded. It is also possible the developer knows the data will not be used anyway. This is only a problem if the developer actually intended to bind a Render Target View here. [ EXECUTION WARNING #3146081: DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET]
此代码主要基于 DirectXTK Win32 模板。任何帮助将不胜感激,谢谢。
【问题讨论】:
【参考方案1】:渲染过程中的多个手动互斥锁/解锁使整个同步毫无意义。不会同时调用设备或上下文,但可以从不同的线程修改渲染管道。例如,在lock
OMSetRenderTargets
unlock
之后,另一个线程可能会获得锁定并设置不同的渲染目标,而第一个渲染目标会在屏幕上显示丢弃的内容。
【讨论】:
以上是关于DirectX 窗口在多窗口和多线程下快速闪烁的主要内容,如果未能解决你的问题,请参考以下文章
即使使用 Win32GUI,cx_Freeze 也会闪烁 cmd 窗口
基于 DirectX11 的 MMDViewer 04-渲染目标视图和多视口