Проблемы совместимости D2D1 и D3D11 в многопоточной среде с общим ID3D11Device - PullRequest
1 голос
/ 25 марта 2020

В D2D я использую объект ID2D1RenderTarget для рисования линии и изображения в отдельных потоках, даже для рендеринга в другой объект ID2D1RenderTarget в том же потоке, он всегда работает нормально.

Но в случае необходимости поддержки многопоточный D3D / Media Foundation, я использую глобальный ID3D3d11Device и сделал взаимодействие D2D / D3D.

если работает только один поток, он работает хорошо; ошибка при одновременном запуске двух потоков (у каждого потока есть окно CDxRender), сообщалось об ошибке, я не знаю, как ее решить:

D2D DEBUG ERROR - An attempt to draw to an inaccessible target has been detected.

MFVerifDemo.exe возникает точка останова отладки

Then press F5, continue to run:

D3D11 ERROR: ID3D11DeviceContext::Draw: A Vertex Shader is always required when drawing, but none is currently bound. [ EXECUTION ERROR #341: DEVICE_DRAW_VERTEX_SHADER_NOT_SET]
D3D11 ERROR: ID3D11DeviceContext::Draw: Rasterization Unit is enabled (PixelShader is not NULL or Depth/Stencil test is enabled and RasterizedStream is not D3D11_SO_NO_RASTERIZED_STREAM) but position is not provided by the last shader before the Rasterization Unit. [ EXECUTION ERROR #362: DEVICE_DRAW_POSITION_NOT_PRESENT]
D3D11: Removing Device.

1 , Используйте d3d11createdevice для создания глобальной переменной _d3d11_device и упакуйте список executecommandlist:

CComPtr<id3d11device>  _d3d11_device

HRESULT CreateD3DDevice(ID3D11Device** d3d11_device)
{
    CComPtr<ID3D11DeviceContext> d3d11_immedContex;

    //Describe our Buffer
    DXGI_MODE_DESC bufferDesc;
    ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
    bufferDesc.Width = 0;
    bufferDesc.Height = 0;
    bufferDesc.RefreshRate.Numerator = 60;
    bufferDesc.RefreshRate.Denominator = 1;
    bufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

    //Create our _swap_chain1   
    HRESULT hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE , NULL, D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, NULL, D3D11_SDK_VERSION, d3d11_device, NULL, &d3d11_immedContex);
    RETURN_ON_FAIL(hr);

    CComPtr<ID3D11Multithread> D3DDevMT;
    hr = (*d3d11_device)->QueryInterface(IID_PPV_ARGS(&D3DDevMT));
    RETURN_ON_FAIL(hr);
    D3DDevMT->SetMultithreadProtected(TRUE);

    return hr;
}
void ExecuteCommandList(CComPtr<ID3D11DeviceContext>    deferred_context)
{
    CComPtr<ID3D11CommandList> command_list;
    HRESULT hr = deferred_context->FinishCommandList(FALSE, &command_list);
    RETURN_ON_FAIL2(hr);

    CComPtr<ID3D11DeviceContext> immediate_context;
    _d3d11_device->GetImmediateContext(&immediate_context);

    if (immediate_context)
    {
        std::unique_lock <std::mutex> lck(_mutex);
        immediate_context->ExecuteCommandList(command_list, FALSE);
    }
}

2 , В классе окна "CDxRender" определяет серию переменных-членов:

const int Output_Width = 960;
const int Output_Height = 720;
const UINT32 fps = 50;

struct DefVertBuffer
{
    DirectX::XMFLOAT3 pos;
    DirectX::XMFLOAT4 color;
    DirectX::XMFLOAT2 texCoord;
};

CComPtr<IDXGISwapChain1>        _swap_chain1;
CComPtr<ID3D11DeviceContext>    _deferred_context;
CComPtr<ID2D1Factory>           _d2d_factory;

CComPtr<ID3D11RenderTargetView> _render_target_view;
CComPtr<ID3D11Texture2D>        _back_texture2d;
CComPtr<IDXGISurface>           _back_dxgi_surface;
CComPtr<ID2D1RenderTarget>      _back_render_target;
CComPtr<ID2D1Bitmap>            _back_d2d1_bitmap;

CComPtr<ID3D11SamplerState>     _sampler_state;
CComPtr<ID3D11InputLayout>      _input_layout;
CComPtr<ID3D11VertexShader>     _vertex_shader;
CComPtr<ID3D11PixelShader>      _pixel_shader;
CComPtr<ID3D11Buffer>           _vert_buffer;
CComPtr<ID3D11Buffer>           _index_buffer;

3 , Основная реализация выглядит следующим образом:

HRESULT CDxRender::InitD3d11()
{
    RECT client_rect;
    GetClientRect(&client_rect);
    DirectX::XMMATRIX WVP;
    WVP = DirectX::XMMatrixOrthographicOffCenterLH(0.0f, static_cast<float>(client_rect.right - client_rect.left),  static_cast<float>(client_rect.bottom - client_rect.top), 0.0f, 0.1f, 100.0f);
    //Create our _swap_chain1
    CComPtr<IDXGIFactory2> dxgi_factory2;
    HRESULT hr = GetDxgiFactoryFromD3DDevice(_d3d11_device, &dxgi_factory2);
    RETURN_ON_FAIL(hr);

    //Describe our _swap_chain1
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
    swapChainDesc.Width = client_rect.right - client_rect.left; // Match the size of the window.
    swapChainDesc.Height = client_rect.bottom - client_rect.top;
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
    swapChainDesc.Stereo = FALSE;
    swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
    swapChainDesc.Flags = 0;
    swapChainDesc.Scaling = DXGI_SCALING_NONE;
    swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;

    hr = dxgi_factory2->CreateSwapChainForHwnd(_d3d11_device, m_hWnd, &swapChainDesc, nullptr, nullptr, &_swap_chain1);
    RETURN_ON_FAIL(hr);

    D2D1_FACTORY_OPTIONS options;
    options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, options, &_d2d_factory);
    RETURN_ON_FAIL(hr);

    hr = _d3d11_device->CreateDeferredContext(0, &_deferred_context);
    RETURN_ON_FAIL(hr);

    D3D11_BUFFER_DESC bufDs;
    memset(&bufDs, 0, sizeof(bufDs));
    bufDs.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bufDs.Usage = D3D11_USAGE_DEFAULT;
    bufDs.ByteWidth = sizeof(DefVertBuffer) * 4;
    D3D11_SUBRESOURCE_DATA subData;
    std::vector<DefVertBuffer> temptBuffer;
    DefVertBuffer temptData;
    //  lb
    temptData.pos = { -1.0f, -1.0f, 0.0f };
    temptData.color = { 1.0f,0.0f,0.0f,1.0f };
    temptData.texCoord = { 0.0f, 1.0f };
    temptBuffer.push_back(temptData);
    //  lt
    temptData.pos = { -1.0f,1.0f,0.0f };
    temptData.color = { 1.0f,0.0f,0.0f,1.0f };
    temptData.texCoord = { 0.0f, 0.0f };
    temptBuffer.push_back(temptData);
    //  rt
    temptData.pos = { 1.0f, 1.0f, 0.0f};
    temptData.color = { 1.0f,0.0f,0.0f,1.0f };
    temptData.texCoord = { 1.0f, 0.0f };
    temptBuffer.push_back(temptData);
    //  rb
    temptData.pos = { 1.0f, -1.0f, 0.0f };
    temptData.color = { 1.0f,0.0f,0.0f,1.0f };
    temptData.texCoord = { 1.0f, 1.0f };
    temptBuffer.push_back(temptData);
    subData.pSysMem = temptBuffer.data();
    hr = _d3d11_device->CreateBuffer(&bufDs, &subData, &_vert_buffer);
    RETURN_ON_FAIL(hr);
    unsigned __int32 indexBuff[] =
    {
        0,1,2,
        0,2,3
    };
    bufDs.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bufDs.ByteWidth = sizeof(unsigned __int32) * 6;
    subData.pSysMem = indexBuff;
    hr = _d3d11_device->CreateBuffer(&bufDs, &subData, &_index_buffer);
    RETURN_ON_FAIL(hr);

    std::vector<D3D11_INPUT_ELEMENT_DESC> layout;
    layout.push_back({ "POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0 });
    layout.push_back({ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 });
    layout.push_back({ "TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,28,D3D11_INPUT_PER_VERTEX_DATA,0 });

    hr = _d3d11_device->CreateInputLayout(layout.data(), layout.size(), g_VertexShader, ARRAYSIZE(g_VertexShader), &_input_layout);
    RETURN_ON_FAIL(hr);

    hr = _d3d11_device->CreateVertexShader(g_VertexShader, ARRAYSIZE(g_VertexShader), NULL, &_vertex_shader);
    RETURN_ON_FAIL(hr);
    hr = _d3d11_device->CreatePixelShader(g_PixelShader, ARRAYSIZE(g_PixelShader), NULL, &_pixel_shader);
    RETURN_ON_FAIL(hr);

    D3D11_SAMPLER_DESC samplerDesc;
    ZeroMemory(&samplerDesc, sizeof(D3D11_SAMPLER_DESC));
    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
    samplerDesc.MinLOD = 0;
    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

    hr = _d3d11_device->CreateSamplerState(&samplerDesc, &_sampler_state);
    RETURN_ON_FAIL(hr);

    return hr;
}

HRESULT CDxRender::Resize(const UINT32& w, const UINT32& h)
{
    _render_target_view = nullptr;
    _back_texture2d = nullptr;
    _back_dxgi_surface = nullptr;
    _back_d2d1_bitmap = nullptr;
    _back_render_target = nullptr;

    HRESULT hr = _swap_chain1->ResizeBuffers(2, w, h, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
    RETURN_ON_FAIL(hr);

    hr = _swap_chain1->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&_back_texture2d);
    RETURN_ON_FAIL(hr);

    hr = _d3d11_device->CreateRenderTargetView(_back_texture2d, NULL, &_render_target_view);
    RETURN_ON_FAIL(hr);

    _deferred_context->OMSetRenderTargets(1, &_render_target_view.p, NULL);

    hr = _back_texture2d->QueryInterface(IID_PPV_ARGS(&_back_dxgi_surface));
    RETURN_ON_FAIL(hr);

#if 1
    UINT dpi = GetDpiForWindow(m_hWnd);
    D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), static_cast<float>(dpi), static_cast<float>(dpi));

    hr = _d2d_factory->CreateDxgiSurfaceRenderTarget(_back_dxgi_surface, &props, &_back_render_target);
    RETURN_ON_FAIL(hr);

    float dpi_x = 0.f;
    float dpi_y = 0.f;
    _back_render_target->GetDpi(&dpi_x, &dpi_y);
    D2D1_SIZE_U src_sizeU = _back_render_target->GetPixelSize();
    D2D1_BITMAP_PROPERTIES prop = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), dpi_x, dpi_y);
    hr = _back_render_target->CreateBitmap(src_sizeU, prop, &_back_d2d1_bitmap);
    RETURN_ON_FAIL(hr);        
#endif

    return hr;
}

void CDxRender::PreRender(const UINT32& w, const UINT32& h)
{
    unsigned __int32 stride = sizeof(DefVertBuffer);
    unsigned __int32 offset = 0;
    _deferred_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    _deferred_context->IASetIndexBuffer(_index_buffer, DXGI_FORMAT_R32_UINT, 0);
    _deferred_context->IASetVertexBuffers(0, 1, &_vert_buffer.p, &stride, &offset);
    _deferred_context->PSSetShader(_pixel_shader, 0, 0);
    _deferred_context->VSSetShader(_vertex_shader, 0, 0);
    _deferred_context->IASetInputLayout(_input_layout);
    _deferred_context->PSSetSamplers(0, 1, &_sampler_state.p);
    _deferred_context->OMSetRenderTargets(1, &_render_target_view.p, nullptr);

    D3D11_VIEWPORT viewPort;
    unsigned int viewPort_index = 1ui32;
    _deferred_context->RSGetViewports(&viewPort_index, &viewPort);
    viewPort.Width = static_cast<float>(w);
    viewPort.Height = static_cast<float>(h);
    viewPort.MinDepth = 0.0f;
    viewPort.MaxDepth = 1.0f;
    _deferred_context->RSSetViewports(1, &viewPort);
}

void CDxRender::DrawScene()
{
    RECT client_rect;
    GetClientRect(&client_rect);

    PreRender(client_rect.right - client_rect.left, client_rect.bottom - client_rect.top);

    FLOAT bgArray[][4] =
    {
        { 1.f, 0.f, 0.f, 1.0f },
        { 0.f, 1.f, 0.f, 1.0f },
        { 0.f, 0.f, 1.f, 1.0f },
        { 1.f, 0.f, 1.f, 1.0f },
        { 1.f, 1.f, 0.f, 1.0f },
        { 0.f, 1.f, 1.f, 1.0f },
        { 1.f, 1.f, 1.f, 1.0f },
        { 0.f, 0.f, 0.f, 1.0f },
    };

    INT64 counter = (_capture_counter / (5 * fps)) % ARRAYSIZE(bgArray);
    FLOAT* bgColor = bgArray[counter];

#if 1
    CComPtr<ID2D1SolidColorBrush> whiteBrush;
    _back_render_target->CreateSolidColorBrush(D2D1::ColorF(1.f, 1.f, 0.f, 1.f), &whiteBrush);

    // draw the text
    D2D1_SIZE_F border = _back_render_target->GetSize();

    float _offset_x = (rand() % 20 - 10) / 10.f;
    float _offset_y = (rand() % 20 - 10) / 10.f;

    _back_render_target->BeginDraw();
    _back_render_target->Clear(D2D1::ColorF(bgColor[0], bgColor[1], bgColor[2], bgColor[3]));
    _back_render_target->FillRectangle(D2D1::RectF(200 - 50 * _offset_y, 200 - 50 * _offset_x, 800 + 50 * (_offset_x + _offset_y) / 2, 600 - 50 * (_offset_x + _offset_y) / 2), whiteBrush);
    _back_render_target->DrawRectangle(D2D1::RectF(100 + 50 * _offset_x, 100 + 50 * _offset_y, border.width - 100 - 50 * (_offset_x + _offset_y) / 2, border.height - 100 + 50 * (_offset_x + _offset_y) / 2), whiteBrush, 10.f);

    hr = _back_render_target->EndDraw();///<------------will debug breakpoint here
#endif

    ExecuteCommandList(_deferred_context);

    //Present the backbuffer to the screen
    _swap_chain1->Present(0, 0);
}

4 , PixelShader.hlsl:

struct PS_Input
{
    float4 pos:SV_POSITION;
    float4 color:COLOR;
    float2 texCoord:TEXCOORD;
};

Texture2D objTexture :register(t0);
SamplerState objSamplerState;

float4 main(PS_Input input) : SV_TARGET
{
    /*float4 retColor = 0;
    retColor = objTexture.Sample(objSamplerState, input.texCoord) * input.color;
    clip(retColor.a - 0.3);
    return retColor;*/
    return input.color;
}

5 , VertexShader.hlsl:

struct VS_Input
{
    float4 pos:POSITION;
    float4 color:COLOR;
    float2 texCoord:TEXCOORD;
};

struct VS_Output
{
    float4 pos:SV_POSITION;
    float4 color:COLOR;
    float2 texCoord:TEXCOORD;
};

VS_Output main(VS_Input input)
{
    VS_Output output;
    output.pos = input.pos;/*mul(input.pos, WVP);*/
    output.color = input.color;
    output.texCoord = input.texCoord;
    return output;
}
...