DirectX窗口通过多窗口和多线程快速闪烁

时间:2019-07-18 01:21:41

标签: c++ windows multithreading graphics directx

我正在尝试创建一个基于DirectX的基本C ++ Windows应用程序,该应用程序可以在两个单独的窗口上显示两个图像。我很容易显示单个图像。但是,我必须使用多线程(每个窗口一个线程),因为至关重要的是窗口的内容必须同时更新。

但是,目前,我只专注于为两个静态图像实现多线程,每个窗口每个线程一个图像。但是,我遇到的问题是两个窗口都快速/随机闪烁(它们的闪烁大部分为黑色,偶尔为绿色)。

据我所知,这可能与我将多线程与DirectX组合在一起的方式有关,据我所知,必须使用互斥锁,以便不会同时调用设备或上下文。因此,我怀疑当Windows重新渲染时,它们的内容在某些时候会被其他线程阻塞。

但是,我不确定是否是这种情况,如果这样,我不知道如何解决。

这是主要的游戏循环:

//
// 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 个答案:

答案 0 :(得分:0)

在渲染过程中进行多次手动互斥锁锁定/解锁,使整个同步毫无意义。没有对设备或上下文的同时调用,但是可以从不同的线程修改渲染管道。例如,在lock OMSetRenderTargets unlock之后,另一个线程可能会获得锁定并设置不同的渲染目标,从而使第一个线程剩下显示在屏幕上的废弃内容。